Overall Statistics |
Total Trades 2129 Average Win 0.88% Average Loss -0.77% Compounding Annual Return 17.844% Drawdown 42.100% Expectancy 0.207 Net Profit 396.218% Sharpe Ratio 0.871 Probabilistic Sharpe Ratio 25.530% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 1.14 Alpha 0.023 Beta 1.041 Annual Standard Deviation 0.206 Annual Variance 0.042 Information Ratio 0.22 Tracking Error 0.132 Treynor Ratio 0.172 Total Fees $2673.30 Estimated Strategy Capacity $76000.00 Lowest Capacity Asset HAS R735QTJ8XC9X |
########################################################################################## # # QQQ Index Rebalancer # ------------------------ # # Inspired by: # https://www.quantconnect.com/forum/discussion/12347/creating-our-own-index-fund/p1 # # Values for External Params # --------------------------------------- # maxHoldings = 10 # Number of positions to hold, max # lookbackInDays = 30 # look at performance over last 30 days # perfMetricIndex = 0 # '0' corresponds to Momentum Pct # rebalancePeriodIndex = 0 # '0' corresponds to Monthly rebalancing # useETFWeights = 0 = 0 # Honor the ETF's weighting (even if a few holdings) # ########################################################################################## class ETFUniverse(QCAlgorithm): ## Main entry point for the algo ## ================================================================================== def Initialize(self): self.InitAssets() self.InitBacktestParams() self.InitExternalParams() self.InitAlgoParams() self.ScheduleRoutines() ## Init assets: Symbol, broker model, universes, etc. Called from Initialize(). ## ================================================================================== def InitAssets(self): self.ticker = "QQQ" self.etfSymbol = self.AddEquity(self.ticker, Resolution.Hour).Symbol self.AddUniverse(self.Universe.ETF(self.etfSymbol, self.UniverseSettings, self.ETFConstituentsFilter)) self.UniverseSettings.Resolution = Resolution.Hour self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x))) ## Set backtest params: dates, cash, etc. Called from Initialize(). ## ================================================================== def InitBacktestParams(self): self.SetStartDate(2012, 1, 1) # Set Start Date # self.SetEndDate(2019, 1, 1) # Set end Date self.SetCash(10000) # Set Strategy Cash self.EnableAutomaticIndicatorWarmUp = True self.SetWarmup(31, Resolution.Daily) # ==================================================================== # Initialize external parameters. Called from Initialize(). # ==================================================================== def InitExternalParams(self): self.maxHoldings = int(self.GetParameter("maxHoldings")) self.perfMetricIndex = int(self.GetParameter("perfMetricIndex")) self.rebalancePeriodIndex = int(self.GetParameter("rebalancePeriodIndex")) self.lookbackInDays = int(self.GetParameter("lookbackInDays")) self.useETFWeights = bool(self.GetParameter("useETFWeights") == 1) # ================================================================================== # Set algo params: Symbol, broker model, ticker, etc. Called from Initialize(). # ================================================================================== def InitAlgoParams(self): # Flags to track and trigger rebalancing state self.timeToRebalance = True self.universeRepopulated = False # State vars self.weights = {} self.symDataDict = {} self.SelectedSymbols = [] self.SelectedSymbolData = [] # Values related to our alpha (rebalancing period and preferred perf metric self.PerfMetrics = [MetricsEnum.MOMENTUM_PCT, MetricsEnum.SHARPE] self.rankPerfMetric = self.PerfMetrics[self.perfMetricIndex] self.RebalancePeriods = [IntervalEnum.MONTHLY, IntervalEnum.WEEKLY, IntervalEnum.DAILY] self.rebalancePeriod = self.RebalancePeriods[self.rebalancePeriodIndex] # ================================== def ScheduleRoutines(self): if( self.rebalancePeriod == IntervalEnum.MONTHLY ): self.Schedule.On( self.DateRules.MonthStart(self.etfSymbol), self.TimeRules.AfterMarketOpen(self.etfSymbol, 31), self.SetRebalanceFlag ) elif( self.rebalancePeriod == IntervalEnum.WEEKLY ): self.Schedule.On( self.DateRules.WeekStart(self.etfSymbol), self.TimeRules.AfterMarketOpen(self.etfSymbol, 31), self.SetRebalanceFlag ) # ==================================== def ETFConstituentsFilter(self, constituents): if( self.timeToRebalance ): self.weights = {c.Symbol: c.Weight for c in constituents} ## set flags self.universeRepopulated = True self.timeToRebalance = False for symbol in self.weights: if symbol not in self.symDataDict: # symbol, algo, etfWeight, lookbackInDays, MetricsEnum.MOMENTUM_PCT): self.symDataDict[symbol] = SymbolData(self, symbol, self.weights[symbol], \ self.lookbackInDays, self.rankPerfMetric) symbolData = self.symDataDict[symbol] history = self.History(symbol, 31, Resolution.Daily) symbolData.SeedHistory(history) # Sort by momentum sortedByMomentum = sorted(self.symDataDict.items(), key=lambda x: x[1].PerfMetricValue, reverse=False)[:10] # Get Symbol object (the key of dict) self.SelectedSymbols = [x[0] for x in sortedByMomentum] self.SelectedSymbolData = [x[1] for x in sortedByMomentum] #### TODO: #### Remove Symboldata from self.symDataDict that didnt make the cut return self.SelectedSymbols else: return [] # ==================================== def SetRebalanceFlag(self): self.timeToRebalance = True # ==================================== def OnData(self,data): if (self.universeRepopulated): self.Liquidate() self.weights = {} # calculate sum of weights weightsSum = sum(self.symDataDict[symbol].etfWeight for symbol in self.SelectedSymbols) for symbol in self.SelectedSymbols: # Either use ETF Weights or equal weights if(self.useETFWeights): symbolWeight = self.symDataDict[symbol].etfWeight / weightsSum # respect weighting else: symbolWeight = 1 / len(self.SelectedSymbols) # Equally weighted self.SetHoldings(symbol, symbolWeight) self.universeRepopulated = False # for symbol, weight in self.weights.items(): # if symbol in self.ActiveSecurities: # self.SetHoldings(symbol, weight) # Market cap weighted # # self.SetHoldings(symbol, 1 / len(self.weights)) # Equally weighted # ================================================================= # Periodically check if a security is no longer in the ETF # ================================================================= # def RemoveDelistedSymbols(self, changes): # for investedSymbol in [x.Key for x in self.Portfolio if x.Value.Invested]: # if( investedSymbol not in self.weights.keys() ): # self.Liquidate(symbol, 'No longer in universe') ########################## # Symbol Data Class ########################## class SymbolData(): def __init__(self, algo, symbol, etfWeight, lookbackInDays, perfMetric): self.algo = algo self.symbol = symbol self.etfWeight = etfWeight self.perfMetric = perfMetric self.momp = MomentumPercent(lookbackInDays) def SeedHistory(self,history): for row in history.loc[self.symbol].itertuples(): self.momp.Update(row.Index, row.close) @property def PerfMetricValue(self): if( (self.perfMetric == MetricsEnum.MOMENTUM_PCT) and self.momp.IsReady): return self.momp.Current.Value else: return float('-inf') ############################### # Perf Enum ############################### class MetricsEnum(Enum): MOMENTUM_PCT = "MOMENTUM PCT" SHARPE = "SHARPE" DRAWDOWN = "DRAWDOWN" RET_DD_RATIO = "RET/DD" THARP_EXP = "THARP EXP" ############################### # Interval Enum ############################### class IntervalEnum(Enum): MONTHLY = "MONTHLY" WEEKLY = "WEEKLY" DAILY = "DAILY"