Overall Statistics |
Total Orders 11362 Average Win 0.14% Average Loss -0.13% Compounding Annual Return 8.109% Drawdown 16.200% Expectancy 0.165 Start Equity 1000000 End Equity 3599879.84 Net Profit 259.988% Sharpe Ratio 0.454 Sortino Ratio 0.413 Probabilistic Sharpe Ratio 2.452% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 1.07 Alpha 0.03 Beta 0.226 Annual Standard Deviation 0.099 Annual Variance 0.01 Information Ratio -0.149 Tracking Error 0.158 Treynor Ratio 0.2 Total Fees $174684.70 Estimated Strategy Capacity $200000.00 Lowest Capacity Asset CRPT XS13GRGHR7MT Portfolio Turnover 11.92% |
#region imports from AlgorithmImports import * #endregion class SectorMomentumAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2008, 1, 1) self.SetEndDate(2024, 5, 31) self.SetCash(1000000) # Set the benchmark to SPY self.SetBenchmark("SPY") # BrokerageModel --> Here we select IB (this model includes fees) self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) # Define parameters self.cmo_window = int(self.GetParameter("CMO window")) self.sma_s = int(self.GetParameter("small SMA window")) self.sma_l = int(self.GetParameter("long SMA window")) self.roc_period = int(self.GetParameter("roc_period")) self.selected_symbol_count = int(self.GetParameter("selected_symbol_count")) # CMO indicator for SPY self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol self.cmo = self.CMO(self.spy, self.cmo_window, Resolution.Daily) self.cmo.Updated += self.OnCMOUpdated self.cmoWindow = RollingWindow[IndicatorDataPoint](self.cmo_window) # Define the universe of symbols self.long_universe = [ "XLK", "XLE", "XLV", "XLF", "XLI", "XLB", "XLY", "XLP", "XLU", "XLC", "XLRE", "URA", "OIH", "XOP", "CRAK", "ICLN", "TAN", "PBW", "VLUE", "INFL", "FTGC", "GNR", "XME", "ARKK", "QCLN", "USO", "SLV", "GLD", "LIT", "HACK", "SKYY", "CLOU", "IBB", "KWEB", "ASHR", "FXI", "VGK", "EWJ", "EEM", "SOXX", "KBE", "KIE", "GDX", "SIL", "ASHS", "METV", "BITQ", "CRPT", "TLT", "XRT", "HYG", "LQD", "MOO", "SPHB", "SPLV", "IYT", "ITA", "CGW", "NSPY", "FEZ", "DAX", "FNGS", "KRBN", "GSG", "MEME", "JETS", "INDA", "UUP", "KMET", "NRJ", "IPAY", "AIQ", "HERO" ] # Initialize indicators and rolling windows for each symbol in the universe self.data = {} for ticker in self.long_universe: equity = self.AddEquity(ticker, Resolution.Daily) roc = RateOfChange(self.roc_period) sma_short = SimpleMovingAverage(self.sma_s) sma_long = SimpleMovingAverage(self.sma_l) self.data[ticker] = { "roc": roc, "sma_short": sma_short, "sma_long": sma_long, "rocWindow": RollingWindow[IndicatorDataPoint](self.roc_period), "smaShortWindow": RollingWindow[IndicatorDataPoint](self.sma_s), "smaLongWindow": RollingWindow[IndicatorDataPoint](self.sma_l) } roc.Updated += lambda sender, updated, symbol=ticker: self.OnROCUpdated(sender, updated, symbol) sma_short.Updated += lambda sender, updated, symbol=ticker: self.OnSMAShortUpdated(sender, updated, symbol) sma_long.Updated += lambda sender, updated, symbol=ticker: self.OnSMALongUpdated(sender, updated, symbol) # Schedule the rebalance function 6 and 5 minutes before market close self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 6), self.UpdateIndicators) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 5), self.Rebalance) # Set warmup period self.SetWarmUp(timedelta(days=max(self.sma_l, self.roc_period))) def OnCMOUpdated(self, sender, updated): if self.cmo.IsReady: self.cmoWindow.Add(updated) #self.Debug(f"CMO Value at {self.Time}: {self.cmo.Current.Value}") def OnROCUpdated(self, sender, updated, symbol): if self.data[symbol]["roc"].IsReady: self.data[symbol]["rocWindow"].Add(updated) def OnSMAShortUpdated(self, sender, updated, symbol): if self.data[symbol]["sma_short"].IsReady: self.data[symbol]["smaShortWindow"].Add(updated) def OnSMALongUpdated(self, sender, updated, symbol): if self.data[symbol]["sma_long"].IsReady: self.data[symbol]["smaLongWindow"].Add(updated) def UpdateIndicators(self): # Manually update CMO with historical close prices history = self.History(["SPY"], self.cmo_window, Resolution.Daily) for time, row in history.loc["SPY"].iterrows(): self.cmo.Update(time, row['close']) # Update CMO with the current price at 5 minutes before close self.cmo.Update(self.Time, self.Securities["SPY"].Price) # Update indicators for each symbol in the universe with the current price for ticker, indicators in self.data.items(): current_price = self.Securities[ticker].Price indicators["roc"].Update(self.Time, current_price) indicators["sma_short"].Update(self.Time, current_price) indicators["sma_long"].Update(self.Time, current_price) def Rebalance(self): if self.IsWarmingUp: return if not self.cmo.IsReady or self.cmo.Current.Value < 0: self.Liquidate() return # Filter the long universe based on momentum and SMA delta sorted_by_momentum = sorted( [ticker for ticker in self.long_universe if self.data[ticker]["roc"].IsReady and self.data[ticker]["sma_short"].IsReady and self.data[ticker]["sma_long"].IsReady and (self.data[ticker]["sma_short"].Current.Value - self.data[ticker]["sma_long"].Current.Value) >= 0], key=lambda ticker: self.data[ticker]["roc"].Current.Value, reverse=True ) if len(sorted_by_momentum) < self.selected_symbol_count: self.Liquidate() return long_positions = sorted_by_momentum[:self.selected_symbol_count] # Adjust holdings for holding in self.Portfolio.Values: if holding.Invested and holding.Symbol.Value not in long_positions: self.Liquidate(holding.Symbol) weight = 1 / len(long_positions) for ticker in long_positions: self.SetHoldings(ticker, weight) def OnData(self, data: Slice): if self.IsWarmingUp: return pass
# https://quantpedia.com/strategies/sector-momentum-rotational-system/ # # Use ten sector ETFs. Pick 4 ETFs with the strongest 12-week momentum into your portfolio and weight them equally. Hold them for one week and then rebalance. #region imports from AlgorithmImports import * #endregion class SectorMomentumAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2021, 1, 1) self.SetEndDate(2024, 5, 31) self.SetCash(1000000) # Set the benchmark to SPY self.SetBenchmark("SPY") # BrokerageModel --> Here we select IB (this model includes fees) self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) # CMO indicator self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol self.cmo = self.CMO(self.spy, int(self.GetParameter("CMO window"))) # daily data self.data = {} self.sma_s = int(self.GetParameter("small SMA window")) self.sma_l = int(self.GetParameter("long SMA window")) self.roc_period = int(self.GetParameter("roc_period")) if self.roc_period >= self.sma_l: self.SetWarmUp(self.roc_period) else: self.SetWarmUp(self.sma_l) self.selected_symbol_count = int(self.GetParameter("selected_symbol_count")) # long symbol count self.long_universe = [ "XLK", # Technology Select Sector SPDR Fund "XLE", # Energy Select Sector SPDR Fund "XLV", # Health Care Select Sector SPDR Fund "XLF", # Financial Select Sector SPDR Fund "XLI", # Industrials Select Sector SPDR Fund "XLB", # Materials Select Sector SPDR Fund "XLY", # Consumer Discretionary Select Sector SPDR Fund "XLP", # Consumer Staples Select Sector SPDR Fund "XLU", # Utilities Select Sector SPDR Fund "XLC", # Communication Services Select Sector SPDR Fund "XLRE", # Real Estate Select Sector SPDR Fund "URA", "OIH", "XLE", "XOP", "CRAK", "ICLN", "TAN", "PBW", "VLUE", "INFL", "FTGC", "GNR", "XME", "ARKK", "QCLN", "USO", "SLV", "GLD", "LIT", "HACK", "SKYY", "CLOU", "IBB", "KWEB", "ASHR", "FXI", "VGK", "EWJ", "EEM", "SOXX", "KBE", "KIE", "GDX", "SIL", "ASHS", "METV", "BITQ", "CRPT", "TLT", "XRT", "HYG", "LQD", "MOO", "SPHB", "SPLV", "IYT", "ITA", "CGW", "NSPY", "FEZ", "DAX", "FNGS", "KRBN", "GSG", "MEME", "JETS", "INDA", "UUP", "KMET", "NRJ", "IPAY", "AIQ", "HERO", ] for ticker in self.long_universe: data = self.AddEquity(ticker, Resolution.Daily) data.SetLeverage(1) self.data[ticker] = { "roc": self.ROC(ticker, self.roc_period, Resolution.Daily), "sma_short": self.SMA(ticker, self.sma_s, Resolution.Daily), "sma_long": self.SMA(ticker, self.sma_l, Resolution.Daily), } self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 5), self.Rebalance) def Rebalance(self): if self.IsWarmingUp: return # Check if CMO is ready and its value if self.cmo.IsReady and self.cmo.Current.Value < 0: self.Liquidate() return # Filter the long universe based on momentum and SMA delta sorted_by_momentum = sorted( [ticker for ticker in self.long_universe if self.data[ticker]["roc"].IsReady and self.data[ticker]["sma_short"].IsReady and self.data[ticker]["sma_long"].IsReady and (self.data[ticker]["sma_short"].Current.Value - self.data[ticker]["sma_long"].Current.Value) >= 0], key=lambda ticker: self.data[ticker]["roc"].Current.Value, reverse=True ) if len(sorted_by_momentum) < self.selected_symbol_count: self.Liquidate() return long_positions = sorted_by_momentum[:self.selected_symbol_count] # Adjust holdings for holding in self.Portfolio.Values: if holding.Invested and holding.Symbol.Value not in long_positions: self.Liquidate(holding.Symbol) weight = 1 / len(long_positions) for ticker in long_positions: self.SetHoldings(ticker, weight) def OnData(self, data: Slice): if self.IsWarmingUp: return pass
#region imports from AlgorithmImports import * #endregion import plotly.express as px import plotly.graph_objects as go # Your New Python File #Plot stuff equity_chart = backtest.Charts["Strategy Equity"] drawdown_chart = backtest.Charts["Drawdown"] benchmark_chart = backtest.Charts["Benchmark"] equity = equity_chart.Series["Equity"].Values drawdown = drawdown_chart.Series["Equity Drawdown"].Values benchmark = benchmark_chart.Series["Benchmark"].Values df = pd.DataFrame({ "Equity": pd.Series({value.Time: value.Close for value in equity}), "Drawdown": pd.Series({value.Time: value.Y for value in drawdown}), "Benchmark": pd.Series({value.Time: value.Y for value in benchmark}) }).ffill() # Create subplots to plot series on same/different plots fig, ax = plt.subplots(2, 1, figsize=(12, 12), sharex=True, gridspec_kw={'height_ratios': [2, 1]}) # Plot the equity curve ax[0].plot(df.index, df["Equity"]) ax[0].set_title("Strategy Equity Curve") ax[0].set_ylabel("Portfolio Value ($)") # Plot the benchmark on the same plot, scale by using another y-axis ax2 = ax[0].twinx() ax2.plot(df.index, df["Benchmark"], color="grey") ax2.set_ylabel("Benchmark Price ($)", color="grey")