Overall Statistics |
Total Orders 3417 Average Win 3.40% Average Loss -2.13% Compounding Annual Return 277.341% Drawdown 75.000% Expectancy 0.088 Start Equity 100000 End Equity 556259.5 Net Profit 456.260% Sharpe Ratio 3.891 Sortino Ratio 5.409 Probabilistic Sharpe Ratio 68.704% Loss Rate 58% Win Rate 42% Profit-Loss Ratio 1.59 Alpha 0 Beta 0 Annual Standard Deviation 1.378 Annual Variance 1.898 Information Ratio 3.929 Tracking Error 1.378 Treynor Ratio 0 Total Fees $223600.00 Estimated Strategy Capacity $84000000.00 Lowest Capacity Asset NQ YJHOAMPYKQGX Portfolio Turnover 8029.59% |
# region imports from datetime import timedelta from AlgorithmImports import * import numpy as np import json # endregion class PurpleReign(QCAlgorithm): def initialize(self): self.set_start_date(2023, 1, 1) self.set_end_date(2024, 6, 1) self.set_cash(100000) self.future_chains = None # Set the broker self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN) # Set the symbol of the asset we want to trade self.future = self.add_future(Futures.Indices.NASDAQ_100_E_MINI, Resolution.Minute, data_normalization_mode = DataNormalizationMode.RAW) self.future.set_filter(self._contract_selector) self.symbol = self.future.Symbol # Set up indicators self._macd = self.MACD(self.symbol, 8, 17, 9, MovingAverageType.Simple) self._bb = self.BB(self.symbol, 18, 2, MovingAverageType.Simple) self._kc = self.KCH(self.symbol, 18, 1.5, MovingAverageType.Simple) # Create a RollingWindow to store the past 3 values of the MACD Histogram self._macd_hist_window = RollingWindow[IndicatorDataPoint](3) # Consolidate the data into 5-minute bars self.Consolidate(self.symbol, timedelta(minutes=5), self.on_data_consolidated) self.register_indicator(self.symbol, self._macd, timedelta(minutes=5)) self.register_indicator(self.symbol, self._bb, timedelta(minutes=5)) self.register_indicator(self.symbol, self._kc, timedelta(minutes=5)) # Setting stoploss self.stop_loss_len = 20*5 self.stop_loss_indicator = self.MIN(self.symbol, self.stop_loss_len, Resolution.MINUTE) self.lowest_low = 0 self.stop_loss = 0 self.start_stop_loss = False self.closed_window = RollingWindow[TradeBar](2) # Warming up engine self.set_warm_up(5*20, Resolution.MINUTE) self.settings.free_portfolio_value_percentage = 0.3 def on_margin_call_warning(self): self.Error("Margin call warning") def on_data_consolidated(self, data: slice): # Check if the data strategy is warming up if self.is_warming_up: return # Track the last 2 bars self.closed_window.Add(data) # Check if the Bollinger Bands are within the Keltner Channels self.squeeze = self._bb.UpperBand.Current.Value < self._kc.UpperBand.Current.Value and self._bb.LowerBand.Current.Value > self._kc.LowerBand.Current.Value # self.Log(f"Squeeze indicator: {self.squeeze}") # Check for MACD entry signal self._macd_hist_window.Add(self._macd.Histogram.Current) # Ensure we have 3 data points in the window if self._macd_hist_window.IsReady: macd_hist = self._macd_hist_window[0].Value # Current MACD Histogram value macd_hist_1 = self._macd_hist_window[1].Value # MACD Histogram value 1 bar ago macd_hist_2 = self._macd_hist_window[2].Value # MACD Histogram value 2 bars ago self.macd_long_in = (macd_hist > macd_hist_1 or macd_hist > macd_hist_2) and macd_hist > 0 # self.Log(f"MACD entry: {self.macd_long_in}") # Find the future contract # for chain in self.future_chains: # self.popular_contracts = [contract for contract in chain.value if contract.open_interest > 1000] # if len(self.popular_contracts) == 0: # continue # sorted_bt_o_i_contracts = sorted(self.popular_contracts, key=lambda k: k.open_interest, reverse=True) # self.future_contract = sorted_bt_o_i_contracts[0] # Entry if not self.portfolio.invested: if self.squeeze and self.macd_long_in: self.log(f"Buy at {data.Close}") self.set_holdings(self.future_contract.symbol, 1) self.start_stop_loss = True self.stop_loss = self.lowest_low # self.log(f"Stop loss level {self.stop_loss}") # Send notification self.send_webhook_notification("Buy") # Exit if self.portfolio.invested and self.start_stop_loss: # Register stop loss and take profit levels if self.closed_window.IsReady: if self.closed_window[0].Close > self.closed_window[1].Close: self.stop_loss += (self.closed_window[0].Close - self.closed_window[1].Close) # Stop loss # if data.Close < self.stop_loss or not self.Time.hour in range(9, 16): if self.closed_window[0].Close < self.stop_loss: self.log(f"Stop loss at {data.Close}") self.liquidate(self.future_contract.symbol) self.start_stop_loss = False # Send notification self.send_webhook_notification("Sell") def on_data(self, data: Slice): # Find the contract chain = data.futures_chains.get(self.symbol) if chain: self.future_contract = sorted(chain, key=lambda contract: contract.open_interest, reverse=True)[0] # Roll Over logic for symbol, changed_event in data.symbol_changed_events.items(): old_symbol = changed_event.old_symbol new_symbol = changed_event.new_symbol tag = f"Rollover - Symbol changed at {self.time}: {old_symbol} -> {new_symbol}" quantity = self.portfolio[old_symbol].quantity # Rolling over: to liquidate any position of the old mapped contract and switch to the newly mapped contract self.liquidate(old_symbol, tag=tag) if quantity != 0: self.market_order(new_symbol, quantity, tag=tag) self.log(tag) # Tracking stoploss indicator self.lowest_low = self.stop_loss_indicator.Current.Value def _contract_selector(self, future_filter_universe: Callable[[FutureFilterUniverse], FutureFilterUniverse]) -> FutureFilterUniverse: return future_filter_universe.standards_only().front_month() def send_webhook_notification(self, position): """ Send a webhook notification with the given position (Buy/Sell). """ url = 'https://newagewallstreet.io/version-test/api/1.1/wf/catch-trades?api_token=cc7d071598606d101e84f252c9654956' data = { "strat": "1716875896866x801605936682184200", "ticker": "NQM4", "position": position } headers = {'Content-Type': 'application/json'} try: self.notify.web(url, json.dumps(data), headers) self.log(f"Webhook notification sent. Position: {position}") except Exception as e: self.log(f"Failed to send webhook notification. Error: {str(e)}")