| Overall Statistics |
|
Total Orders 1380 Average Win 6.97% Average Loss -3.43% Compounding Annual Return 67.302% Drawdown 86.700% Expectancy 0.344 Start Equity 10000 End Equity 2708028.88 Net Profit 26980.289% Sharpe Ratio 1.141 Sortino Ratio 1.478 Probabilistic Sharpe Ratio 25.338% Loss Rate 56% Win Rate 44% Profit-Loss Ratio 2.03 Alpha 0.804 Beta 0.69 Annual Standard Deviation 0.753 Annual Variance 0.566 Information Ratio 1.043 Tracking Error 0.747 Treynor Ratio 1.245 Total Fees $738563.39 Estimated Strategy Capacity $41000000000000000000.00 Lowest Capacity Asset ADAUSD 2XR Portfolio Turnover 27.48% |
from AlgorithmImports import *
import numpy as np
class MomentumBasedCryptoSelector(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2014, 1, 1)
# self.SetEndDate(2022, 6, 1)
self.SetCash(10000) # 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 apply the Binance fee model
self.symbols = []
for symbol in self.crypto_symbols:
crypto = self.AddCrypto(symbol, Resolution.Daily)
crypto.SetFeeModel(BinanceFeeModel()) # Apply the Binance fee model
self.symbols.append(crypto.Symbol)
# 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}%")