Overall Statistics |
Total Orders 3644 Average Win 0.41% Average Loss -0.26% Compounding Annual Return 29.811% Drawdown 35.000% Expectancy 0.081 Start Equity 1000000 End Equity 1400959.7 Net Profit 40.096% Sharpe Ratio 0.673 Sortino Ratio 0.753 Probabilistic Sharpe Ratio 37.300% Loss Rate 58% Win Rate 42% Profit-Loss Ratio 1.59 Alpha 0 Beta 0 Annual Standard Deviation 0.294 Annual Variance 0.087 Information Ratio 0.852 Tracking Error 0.294 Treynor Ratio 0 Total Fees $68137.80 Estimated Strategy Capacity $540000000.00 Lowest Capacity Asset NQ YJHOAMPYKQGX Portfolio Turnover 1246.88% |
# region imports from datetime import timedelta from AlgorithmImports import * import numpy as np import json # endregion class Purplereignv2(QCAlgorithm): def Initialize(self): # Set the start and end dates of the backtest self.set_start_date(2023, 1, 1) self.set_end_date(2024, 6, 1) self.set_cash(1000000) self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN) # Initialize the future chains dictionary self.future_chains = {} # Initialize the portfolio dictionary self.portfolio_data = {} # List of futures to trade self.portfolio_assets = { Futures.Indices.NASDAQ_100_E_MINI: "NQM4", # Futures.Indices.MICRO_NASDAQ_100_E_MINI: "MNQM4", Futures.Indices.SP_500_E_MINI: "ESM4", # Futures.Indices.MICRO_SP_500_E_MINI: "MESM4" } for future_api, ticker in self.portfolio_assets.items(): # Store the initalizer in the portfolio dictionary self.portfolio_data[ticker] = {} # Add the future to the algorithm future = self.add_future(future_api, Resolution.MINUTE, data_normalization_mode = DataNormalizationMode.RAW, leverage = 1, data_mapping_mode = DataMappingMode.LAST_TRADING_DAY) future.set_filter(lambda future_filter_universe: future_filter_universe.front_month()) # Store the symbol, squeeze, and MACD long entry indicator self.portfolio_data[ticker]['symbol'] = future.Symbol self.portfolio_data[ticker]['squeeze'] = False self.portfolio_data[ticker]['macd_long_in'] = False # Set up indicators self.portfolio_data[ticker]['macd'] = self.MACD(self.portfolio_data[ticker]['symbol'], 8, 17, 9, MovingAverageType.Simple) self.portfolio_data[ticker]['bb'] = self.BB(self.portfolio_data[ticker]['symbol'], 18, 2, MovingAverageType.Simple) self.portfolio_data[ticker]['kc'] = self.KCH(self.portfolio_data[ticker]['symbol'], 18, 1.5, MovingAverageType.Simple) # Create a RollingWindow to store the past 3 values of the MACD Histogram self.portfolio_data[ticker]['macd_hist_window'] = RollingWindow[IndicatorDataPoint](3) # Create a RollingWindow to store the past 2 values of trading bars self.portfolio_data[ticker]['trading_window'] = RollingWindow[TradeBar](2) # Consolidate the data into 5-minute bars self.Consolidate(self.portfolio_data[ticker]['symbol'], timedelta(minutes=5), self.on_data_consolidated) self.register_indicator(self.portfolio_data[ticker]['symbol'], self.portfolio_data[ticker]['macd'], timedelta(minutes=5)) self.register_indicator(self.portfolio_data[ticker]['symbol'], self.portfolio_data[ticker]['bb'], timedelta(minutes=5)) self.register_indicator(self.portfolio_data[ticker]['symbol'], self.portfolio_data[ticker]['kc'], timedelta(minutes=5)) # Store the open, high, low, and close prices self.portfolio_data[ticker]['open'] = 0 self.portfolio_data[ticker]['high'] = 0 self.portfolio_data[ticker]['low'] = 0 self.portfolio_data[ticker]['close'] = 0 # Setting stoploss self.portfolio_data[ticker]['stop_loss_len'] = 5*20 self.portfolio_data[ticker]['stop_loss_indicator'] = self.MIN(self.portfolio_data[ticker]['symbol'], self.portfolio_data[ticker]['stop_loss_len'], Resolution.MINUTE) self.portfolio_data[ticker]['stop_loss'] = 0 self.portfolio_data[ticker]['start_stop_loss'] = False # Warming up engine self.set_warm_up(5*20, Resolution.MINUTE) self.settings.free_portfolio_value = 0.3 # Schedule the algo to run every 5 minutes self.schedule.on(self.date_rules.every_day(), self.time_rules.every(timedelta(minutes=5)), self.trade_logic) def on_data(self, data: Slice): # Check if the data strategy is warming up if self.is_warming_up: return for ticker in self.portfolio_data.keys(): if data.bars.contains_key(self.portfolio_data[ticker]['symbol']): bar = data.bars[self.portfolio_data[ticker]['symbol']] self.portfolio_data[ticker]['open'] = bar.Open self.portfolio_data[ticker]['high'] = bar.High self.portfolio_data[ticker]['low'] = bar.Low self.portfolio_data[ticker]['close'] = bar.Close self.portfolio_data[ticker]['stop_loss'] = self.portfolio_data[ticker]['stop_loss_indicator'].Current.Value def on_data_consolidated(self, data: slice): # Check if the data strategy is warming up if self.is_warming_up: return def trade_logic(self): if self.is_warming_up: return data = self.portfolio_data # Loop through the tickers in the portfolio for ticker in self.portfolio_data.keys(): # Check if the Bollinger Bands are within the Keltner Channels self.portfolio_data[ticker]['squeeze'] = self.portfolio_data[ticker]['bb'].UpperBand.Current.Value < self.portfolio_data[ticker]['kc'].UpperBand.Current.Value and self.portfolio_data[ticker]['bb'].LowerBand.Current.Value > self.portfolio_data[ticker]['kc'].LowerBand.Current.Value # self.log(f"Squeeze indicator {ticker}: {self.portfolio_data[ticker]['squeeze']}") # Check for MACD entry signal self.portfolio_data[ticker]['macd_hist_window'].Add(self.portfolio_data[ticker]['macd'].Histogram.Current) # Ensure we have 3 data points in the window if self.portfolio_data[ticker]['macd_hist_window'].IsReady: macd_hist = self.portfolio_data[ticker]['macd_hist_window'][0].Value macd_hist_1 = self.portfolio_data[ticker]['macd_hist_window'][1].Value macd_hist_2 = self.portfolio_data[ticker]['macd_hist_window'][2].Value self.portfolio_data[ticker]['macd_long_in'] = (macd_hist > macd_hist_1 or macd_hist > macd_hist_2) and macd_hist > 0 # self.log(f"MACD entry {ticker}: {self.portfolio_data[ticker]['macd_long_in']}") # Get the current contract continuous_future_symbol = self.portfolio_data[ticker]['symbol'] current_contract = self.securities[continuous_future_symbol].mapped # Entry signal if not self.portfolio[current_contract].invested: if self.portfolio_data[ticker]['squeeze'] and self.portfolio_data[ticker]['macd_long_in']: # Entering the trade self.set_holdings(current_contract, 0.1) # Tracking stop loss self.portfolio_data[ticker]['start_stop_loss'] = True self.portfolio_data[ticker]['stop_loss'] = self.portfolio_data[ticker]['stop_loss_indicator'].Current.Value # Exit signal else: if self.portfolio_data[ticker]['trading_window'].IsReady: if self.portfolio_data[ticker]['close'] > self.portfolio_data[ticker]['trading_window'][1].Close and self.portfolio_data[ticker]['start_stop_loss']: self.portfolio_data[ticker]['stop_loss'] += self.portfolio_data[ticker['close'] - self.portfolio_data[ticker]['trading_window'][1].Close] if self.portfolio_data[ticker]['start_stop_loss'] and (self.portfolio_data[ticker]['close'] < self.portfolio_data[ticker]['stop_loss'] or not self.Time.hour in range(9, 16)): self.liquidate(current_contract) self.portfolio_data[ticker]['start_stop_loss'] = False self.send_webhook_notification("Sell", ticker) self.log(f"Stop loss for {ticker}/{current_contract} at {self.portfolio_data[ticker]['close']}") def send_webhook_notification(self, position, ticker): url = 'https://newagewallstreet.io/version-test/api/1.1/wf/catch-trades?api_token=cc7d071598606d101e84f252c9654956' data = { "strat": "1716875896866x801605936682184200", "ticker": ticker, "position": position } headers = {'Content-Type': 'application/json'} self.Notify.Web(url, json.dumps(data), headers)