Overall Statistics
Total Trades
154
Average Win
0.20%
Average Loss
-0.10%
Compounding Annual Return
10.656%
Drawdown
7.500%
Expectancy
0.620
Net Profit
5.199%
Sharpe Ratio
1.122
Probabilistic Sharpe Ratio
51.825%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
1.96
Alpha
-0.062
Beta
0.341
Annual Standard Deviation
0.094
Annual Variance
0.009
Information Ratio
-2.648
Tracking Error
0.146
Treynor Ratio
0.308
Total Fees
$159.15
import talib
import numpy as np

class VerticalNadionComputer(QCAlgorithm):
    
    period = 21
    leverage = 1
    assets = []
    symbol_data_by_symbol = {}

    def Initialize(self):
        self.SetStartDate(2020, 5, 22)
        self.SetCash(100000)
        
        for ticker in ['SPY', 'QQQ', 'TLT']:
            symbol = self.AddEquity(ticker, Resolution.Minute).Symbol
            self.assets.append(symbol)
            self.symbol_data_by_symbol[symbol] = SymbolData(self, symbol, self.period)
        
        self.bond = self.AddEquity('IEF', Resolution.Minute).Symbol
        
        self.Schedule.On(self.DateRules.EveryDay(self.bond), self.TimeRules.AfterMarketOpen(self.bond, 65), self.trade)


    def trade(self):
        wt_a = 0; wt = self.leverage / len(self.Securities)
        
        for symbol in self.assets:
            symbol_data = self.symbol_data_by_symbol[symbol]
            AroonDown, AroonUp = talib.AROON(symbol_data.highs, symbol_data.lows, self.period)
            
            if AroonUp[-1] > AroonDown[-1]:
                self.SetHoldings(symbol, wt)
                wt_a += wt
            else:
                self.Liquidate(symbol)
        
        wt_b = self.leverage - wt_a
        
        self.SetHoldings(self.bond, wt_b)
        
            
class SymbolData:
    highs = np.array([])
    lows = np.array([])
    
    def __init__(self, algorithm, symbol, period):
        self.symbol = symbol
        self.algorithm = algorithm
        self.period = period
        
        # setup consolidator
        self.consolidator = TradeBarConsolidator(timedelta(days=1))
        self.consolidator.DataConsolidated += self.consolidation_handler
        algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
        
        history = algorithm.History(symbol, period + 1, Resolution.Daily)
        if history.empty or 'high' not in history.columns:
            return
        for time, row in history.loc[symbol].iterrows():
            self.highs = np.append(self.highs, row.high)
            self.lows = np.append(self.lows, row.low)
            
    
    @property
    def IsReady(self):
        return len(self.highs) == self.period + 1 and len(self.lows) == self.period + 1
        
    def consolidation_handler(self, sender, consolidated):
        self.highs = np.append(self.highs, consolidated.High)[-(self.period+1):]
        self.lows = np.append(self.lows, consolidated.Low)[-(self.period+1):]