Overall Statistics |
Total Orders 1123 Average Win 6.32% Average Loss -6.22% Compounding Annual Return 29.757% Drawdown 63.400% Expectancy 0.201 Start Equity 1000000 End Equity 44734356.33 Net Profit 4373.436% Sharpe Ratio 0.687 Sortino Ratio 0.542 Probabilistic Sharpe Ratio 4.003% Loss Rate 40% Win Rate 60% Profit-Loss Ratio 1.02 Alpha 0.154 Beta 2.175 Annual Standard Deviation 0.502 Annual Variance 0.252 Information Ratio 0.6 Tracking Error 0.429 Treynor Ratio 0.158 Total Fees $1766334.61 Estimated Strategy Capacity $11000000.00 Lowest Capacity Asset BGU U7EC123NWZTX Portfolio Turnover 30.63% |
# region imports from AlgorithmImports import * # endregion class MomentumStrategy(QCAlgorithm): def initialize(self): self.set_start_date(2010, 1, 1) # self.set_end_date(2024, 7, 1) self.set_cash(1000000) self.spy = self.add_equity("SPXL", Resolution.DAILY).symbol self.set_benchmark("SPY") # Schedule recalibration at the start of each week self.schedule.on(self.date_rules.every_day(), self.time_rules.at(15, 58), self.rebalance) self.state = 0 chart = Chart("My Custom Chart") self.add_chart(chart) chart.AddSeries(Series("SPY Close", SeriesType.LINE, '$')) chart.AddSeries(Series("Long_term_mean", SeriesType.LINE, '$')) chart.AddSeries(Series("Short_term_mean", SeriesType.LINE, '$')) chart.AddSeries(Series(name="Buy Signal", type=SeriesType.SCATTER, unit='$', color=Color.DarkGreen, symbol=ScatterMarkerSymbol.TRIANGLE)) chart.AddSeries(Series("Sell Signal", SeriesType.SCATTER, '$', Color.DarkRed, ScatterMarkerSymbol.TRIANGLE_DOWN)) chart.AddSeries(Series("Volatility Upper", SeriesType.Line, '$')) chart.AddSeries(Series("Volatility Lower", SeriesType.Line, '$')) self.volatility_period = 27 # Period for volatility calculation self.rolling_window = RollingWindow[float](self.volatility_period) def rebalance(self): history = self.history(self.spy, self.volatility_period+1, Resolution.DAILY) if history.empty or history.isnull().values.any(): raise ValueError("Empty History") #self.Debug(history) self.data = history['close'].unstack(level=0) for i in range(len(self.data)): self.rolling_window.add(self.data.iloc[i]) long_term_mean = float(self.data.mean().values) * float(self.data.std().values) * np.sqrt( 252 / len(self.data)) short_term_mean = float(self.data[:5].mean().values) * float(self.data[:5].std().values) * np.sqrt( 252 / 5) self.Plot("My Custom Chart", "SPY Close", self.data.iloc[-1]) self.Plot("My Custom Chart", "Long_term_mean", long_term_mean) self.Plot("My Custom Chart", "Short_term_mean", short_term_mean) if self.rolling_window.IsReady: returns = [self.rolling_window[i] / self.rolling_window[i+1] - 1 for i in range(self.volatility_period - 1)] volatility = np.std(returns)*500 upper_bar = 100 + volatility / 2 lower_bar = 100 - volatility / 2 self.Plot("My Custom Chart", "Volatility Upper", upper_bar) self.Plot("My Custom Chart", "Volatility Lower", lower_bar) self.previous_state = self.state if (float(self.data.std().values) * np.sqrt( 252 / len(self.data))) > 22.5: self.debug(f"HIGH VOL {(float(self.data.std().values) * np.sqrt( 252 / len(self.data)))}") self.liquidate() return if short_term_mean < long_term_mean and self.state != 1: self.state = 1 elif short_term_mean > long_term_mean and self.state != -1: self.state = -1 if self.previous_state != self.state: self.trade() def trade(self): if self.state == 1: self.plot("My Custom Chart", "Buy Signal", self.data.iloc[-1]) self.liquidate() self.set_holdings("SPXL", 1.9) if self.state == -1: self.plot("My Custom Chart", "Sell Signal", self.data.iloc[-1]) self.liquidate() # self.set_holdings("SPXL", -1.9)