Overall Statistics |
Total Orders 889 Average Win 0.31% Average Loss -0.13% Compounding Annual Return 19.168% Drawdown 14.700% Expectancy 0.370 Start Equity 100000.00 End Equity 123765.97 Net Profit 23.766% Sharpe Ratio 0.507 Sortino Ratio 0.499 Probabilistic Sharpe Ratio 37.395% Loss Rate 60% Win Rate 40% Profit-Loss Ratio 2.38 Alpha 0.064 Beta 0.503 Annual Standard Deviation 0.184 Annual Variance 0.034 Information Ratio 0.189 Tracking Error 0.184 Treynor Ratio 0.186 Total Fees $667.71 Estimated Strategy Capacity $48000000.00 Lowest Capacity Asset TOT R735QTJ8XC9X Portfolio Turnover 9.69% |
# region imports from AlgorithmImports import * import time # endregion class MultiAssetMomentum(QCAlgorithm): def Initialize(self): self.SetStartDate(2024, 1, 1) # Set Start Date self.SetEndDate(2025, 3, 26) self.SetCash(100000) # Set Strategy Cash self.SetWarmUp(30, Resolution.Daily) # 30 days of historical data # Set sector allocation at initialization (25% each sector) # self.sector_allocation = 0.25 # 25% for each sector # self.asset_allocation = 0.05 # 5% of the sector allocation per asset (i.e., 1.25% of total portfolio per asset) self.rsi_buy_top = self.GetParameter("rsi_buy_top", 80) # Default 70 self.rsi_buy_bot = self.GetParameter("rsi_buy_bot", 50) # Default 30 self.rsi_sell_top = self.GetParameter("rsi_sell_top", 90) # Default 80 self.rsi_sell_bot = self.GetParameter("rsi_sell_bot", 40) # Default 40 self.ema_short_period = self.GetParameter("ema_short_period", 12) # Default 12 self.ema_long_period = self.GetParameter("ema_long_period", 26) # Default 26 self.atr_multiplier = 2 # ATR Stop Loss Multiplier self.max_holding_days = 10 # Time-based Stop Loss (Exit after 10 days) # Configure brokerage and security initializer # Universe settings self.UniverseSettings.Resolution = Resolution.Daily self.UniverseSettings.Asynchronous = True # Define fixed universes # BTCUSD - Bitcoin, ETHUSD - Ethereum , SOLUSD - Solana, XRPUSD - XRP (Ripple), ADAUSD - Cardano , DOGEUSD - Dogecoin self.crypto_symbols = ["BTCUSD", "ETHUSD", "SOLUSD", "XRPUSD", "ADAUSD", "DOGEUSD"] # apple, microsoft, nvidia, google, amazon, meta, tesla, boardcome, taiwan semiconductor, ali baba self.tech_symbols = ["AAPL", "MSFT", "NVDA", "GOOGL", "AMZN", "META", "TSLA", "AVGO", "TSM", "BABA"] # XOM - ExxonMobil, CVX - Chevron Corporation, BP - BP Plc , TOT - TotalEnergies, COP - ConocoPhillips , SHEL - Shell Plc self.energy_symbols = ["XOM", "CVX", "BP", "TOT", "COP", "SHEL"] # EURUSD - Euro / US Dollar, USDJPY - US Dollar / Japanese Yen, GBPUSD - British Pound / US Dollar, AUDUSD - Australian Dollar / US Dollar # USDCAD - US Dollar / Canadian Dollar, EURJPY - Euro / Japanese Yen, GBPJPY - British Pound / Japanese Yen, EURGBP - Euro / British Pound self.fx_symbols = ["EURUSD", "USDJPY", "GBPUSD", "AUDUSD", "USDCAD", "EURJPY", "GBPJPY", "EURGBP"] # Add securities and initialize indicators self.rsi = {} self.ema_short = {} self.ema_long = {} self.macd = {} self.atr = {} self.entry_prices = {} self.entry_dates = {} # Track entry time for time-based stop loss self.stop_losses = {} # Track ATR-based stop loss levels self.trailing_stop_losses = {} # Track trailing stop losses for symbol in self.crypto_symbols: self.AddCrypto(symbol, Resolution.Daily) for symbol in self.tech_symbols + self.energy_symbols: self.AddEquity(symbol, Resolution.Daily) for symbol in self.fx_symbols: self.AddForex(symbol, Resolution.Daily) for symbol in self.crypto_symbols + self.tech_symbols + self.energy_symbols + self.fx_symbols: self.rsi[symbol] = self.RSI(symbol, 14, MovingAverageType.Simple, Resolution.Daily) self.ema_short[symbol] = self.EMA(symbol, 12, Resolution.Daily) self.ema_long[symbol] = self.EMA(symbol, 26, Resolution.Daily) self.macd[symbol] = self.MACD(symbol, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily) self.atr[symbol] = self.ATR(symbol, 14, MovingAverageType.Simple, Resolution.Daily) def OnData(self, data): # Exit positions if conditions are met if self.IsWarmingUp: return current_time = self.Time for symbol in list(self.entry_prices.keys()): if symbol not in data.Bars: #self.Debug(f"{symbol} No data available at this time.") ### continue # Skip if no data current_price = data.Bars[symbol].Close atr_value = self.atr[symbol].Current.Value # Time-based stop-loss if (current_time - self.entry_dates[symbol]).days >= self.max_holding_days: self.Debug(f"Time-based stop-loss triggered for {symbol}") self.Liquidate(symbol) self.RemovePositionTracking(symbol) continue # ATR-based stop-loss if current_price < self.stop_losses[symbol]: self.Debug(f"ATR stop-loss triggered for {symbol}") self.Liquidate(symbol) self.RemovePositionTracking(symbol) continue # Update trailing stop loss if current_price > self.entry_prices[symbol]: # Adjust only when price increases self.trailing_stop_losses[symbol] = max(self.trailing_stop_losses[symbol], current_price - 1.5 * atr_value) # Check trailing stop loss if current_price < self.trailing_stop_losses[symbol]: self.Debug(f"Trailing stop-loss triggered for {symbol}") self.Liquidate(symbol) self.RemovePositionTracking(symbol) for symbol in self.rsi.keys(): if symbol not in data.Bars: continue current_price = data.Bars[symbol].Close rsi_value = self.rsi[symbol].Current.Value ema_short_value = self.ema_short[symbol].Current.Value ema_long_value = self.ema_long[symbol].Current.Value macd_line = self.macd[symbol].Current.Value signal_line = self.macd[symbol].Signal.Current.Value histogram = macd_line - signal_line atr_value = self.atr[symbol].Current.Value if ( self.entry_prices.get(symbol) is None and # Not already holding ema_short_value > ema_long_value and self.rsi_buy_bot <= rsi_value <= self.rsi_buy_top and histogram > 0 ): self.Debug(f"Buying {symbol}") self.SetHoldings(symbol, 0.05) self.entry_prices[symbol] = current_price self.entry_dates[symbol] = current_time # Track entry date # Set ATR-based stop loss self.stop_losses[symbol] = current_price - self.atr_multiplier * atr_value # Initialize trailing stop loss self.trailing_stop_losses[symbol] = current_price - 1.5 * atr_value def RemovePositionTracking(self, symbol): """Removes tracking data for a symbol when a position is closed.""" del self.entry_prices[symbol] del self.entry_dates[symbol] del self.stop_losses[symbol] del self.trailing_stop_losses[symbol] ''' self.Debug(f"{symbol}: rsi {rsi_value} emaS{ema_short_value} emaL {ema_long_value} histogram {histogram} ") # Exit condition: bearish crossover or MACD reversal if ema_short_value < ema_long_value or histogram < 0: self.Debug(f"Selling {symbol}") self.Liquidate(symbol) del self.entry_prices[symbol] # Entry conditions for symbol in self.rsi.keys(): if symbol not in data.Bars: self.Debug(f"{symbol} No data available at this time.") continue # Skip if no data current_price = data.Bars[symbol].Close rsi_value = self.rsi[symbol].Current.Value ema_short_value = self.ema_short[symbol].Current.Value ema_long_value = self.ema_long[symbol].Current.Value macd_line = self.macd[symbol].Current.Value signal_line = self.macd[symbol].Signal.Current.Value histogram = macd_line - signal_line self.Debug(f"{symbol}: rsi {rsi_value} emaS{ema_short_value} emaL {ema_long_value} histogram {histogram} ") if ( self.entry_prices.get(symbol) is None and # Not already holding ema_short_value > ema_long_value and self.rsi_buy_bot <= rsi_value <= self.rsi_buy_top and histogram > 0 ): self.Debug(f"Buying {symbol}") self.SetHoldings(symbol, 0.05) self.entry_prices[symbol] = current_price'''