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