Overall Statistics |
Total Orders 1099 Average Win 5.92% Average Loss -3.06% Compounding Annual Return 120.035% Drawdown 74.300% Expectancy 0.353 Start Equity 100000.00 End Equity 10337274.08 Net Profit 10237.274% Sharpe Ratio 1.683 Sortino Ratio 2.365 Probabilistic Sharpe Ratio 55.160% Loss Rate 54% Win Rate 46% Profit-Loss Ratio 1.94 Alpha 1.401 Beta 0.912 Annual Standard Deviation 0.89 Annual Variance 0.792 Information Ratio 1.586 Tracking Error 0.877 Treynor Ratio 1.641 Total Fees $0.00 Estimated Strategy Capacity $660000.00 Lowest Capacity Asset ADAUSD 2XR Portfolio Turnover 40.44% |
from AlgorithmImports import * import numpy as np class MomentumBasedCryptoSelector(QCAlgorithm): def Initialize(self): self.SetStartDate(2019, 1, 1) #self.SetEndDate(2022, 6, 1) self.SetCash(100000) # Set starting cash in USD # Updated list of crypto pairs to include the new assets self.crypto_symbols = [ "BTCUSD", "ETHUSD", "LTCUSD", "XRPUSD", "BCHUSD", "ADAUSD", "DOGEUSD", "SOLUSD", "DOTUSD", "MATICUSD", "HNTUSD", "STXUSD", "MKRUSD" ] # Add all crypto pairs and store their Symbol objects self.symbols = [self.AddCrypto(symbol, Resolution.Daily).Symbol for symbol in self.crypto_symbols] # Warm-up period to ensure data availability (based on lookback period and SMA period) self.lookback_period = 5 # Set the lookback period as a variable self.sma_period = 50 # Set the SMA period as a variable self.SetWarmUp(self.sma_period) # Maximum allocation rule: 80% of the total portfolio value self.max_allocation_percentage = 0.8 def OnData(self, slice): if self.IsWarmingUp: return # Dictionary to store momentum values for each symbol momentum_scores = {} # Calculate momentum and apply the SMA filter for each crypto pair for symbol in self.symbols: history = self.History(symbol, max(self.lookback_period, self.sma_period), Resolution.Daily) if history.empty or len(history["close"]) < self.sma_period: continue close_prices = history["close"].values # Calculate the 50-period SMA sma = np.mean(close_prices[-self.sma_period:]) # Check if the current price is above the SMA if close_prices[-1] < sma: continue # Skip symbols trading below their 50 SMA # Calculate momentum as the change in price over the lookback period momentum = close_prices[-1] - close_prices[-self.lookback_period] momentum_scores[symbol] = momentum # If no momentum scores were calculated, return if not momentum_scores: return # Find the symbol with the highest momentum best_symbol = max(momentum_scores, key=momentum_scores.get) # If already invested in the best symbol, do nothing if self.Portfolio[best_symbol].Invested: return # Liquidate any other holdings for symbol in self.symbols: if symbol != best_symbol and self.Portfolio[symbol].Invested: self.Liquidate(symbol) # Calculate the amount to invest based on the 80% maximum allocation rule available_cash = self.Portfolio.Cash max_investment_value = self.Portfolio.TotalPortfolioValue * self.max_allocation_percentage # Determine the proportion to allocate to the best symbol allocation_percentage = max_investment_value / self.Portfolio.TotalPortfolioValue # Invest in the symbol with the highest momentum, limited to 80% of total portfolio value self.SetHoldings(best_symbol, allocation_percentage) self.Debug(f"Entering Long Position: {best_symbol} with Momentum: {momentum_scores[best_symbol]} and Allocation: {allocation_percentage * 100:.2f}%")