| 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}%")