| Overall Statistics |
|
Total Orders 194 Average Win 1.12% Average Loss -0.27% Compounding Annual Return 37.530% Drawdown 24.400% Expectancy 1.578 Start Equity 100000 End Equity 144143.37 Net Profit 44.143% Sharpe Ratio 1.198 Sortino Ratio 1.758 Probabilistic Sharpe Ratio 55.368% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 4.16 Alpha 0.228 Beta 0.881 Annual Standard Deviation 0.333 Annual Variance 0.111 Information Ratio 0.652 Tracking Error 0.314 Treynor Ratio 0.453 Total Fees $1605.05 Estimated Strategy Capacity $1500000.00 Lowest Capacity Asset USDCUSD 10B Portfolio Turnover 1.49% |
# region imports
from AlgorithmImports import *
from QuantConnect.DataSource import *
from QuantConnect.Data.UniverseSelection import *
# endregion
class CryptoMomentum(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2024, 1,1) # Set Start Date
self.SetEndDate(2025, 3, 1)
self.SetCash(100000) # Set Strategy Cash
self.SetBenchmark("SPY")
# SET UNIVERSE BASED ON MARKET CAP - top 25 (update weekly)
# Configure brokerage and security initializer
self.SetBrokerageModel(BrokerageName.Kraken, AccountType.Cash)
self.SetSecurityInitializer(BrokerageModelSecurityInitializer(self.BrokerageModel, FuncSecuritySeeder(self.GetLastKnownPrice)))
# Universe settings
self.UniverseSettings.Resolution = Resolution.Daily
self.UniverseSettings.Asynchronous = True
# The asynchronous setting is a bool that defines whether or not LEAN can run universe selection asynchronously
# utilizing concurrent execution to increase the speed of your algorithm.
# Add crypto universe with a selection filter
self.AddUniverse(CryptoUniverse.Kraken(self.UniverseSelectionFilter))
# Indicator dictionaries
self.rsi = {}
self.ema_short = {}
self.ema_long = {}
self.macd = {}
self.entry_prices = {}
def UniverseSelectionFilter(self, universe_day: List[CryptoUniverse]) -> List[Symbol]:
# only want top 10 traded based on volume - to ensure liquidty for momentum algo
return [crypto.Symbol for crypto in sorted(universe_day, key=lambda x: x.VolumeInUsd, reverse=True)[:10]]
def OnData(self, data):
# Warm Up is a great way to prepare your algorithm and its indicators for trading.
# if self.IsWarmingUp:
# return
# Sell securities that are no longer in the universe
for symbol in list(self.entry_prices.keys()):
if symbol not in self.ActiveSecurities.Keys:
self.Debug(f"Selling {symbol} as it is no longer in the top 25.")
self.Liquidate(symbol)
del self.entry_prices[symbol]
for symbol in self.ActiveSecurities.Keys:
# no data, move on
if not data.Bars.ContainsKey(symbol):
continue
# Retrieve indicator values
current_price = data.Bars[symbol].Close
# RSI
rsi_value = self.rsi[symbol].Current.Value
# EMA
ema_short_value = self.ema_short[symbol].Current.Value
ema_long_value = self.ema_long[symbol].Current.Value
# MACD
macd = self.macd[symbol]
# The MACD line represents the distance between a shorter moving average and a longer moving average
macd_line = macd.Current.Value
# 9-day EMA of the MACD series
signal_line = macd.Signal.Current.Value
histogram = macd_line - signal_line
# Exit Condition
if self.entry_prices.get(symbol) is not None: # Already holding
if (
ema_short_value < ema_long_value # Bearish crossover
or macd_line < signal_line and histogram < 0
# macd less than signal and negative - bear trend
):
self.Debug(f"Selling {symbol}")
self.Liquidate(symbol)
self.entry_prices[symbol] = None
# Entry Condition
if (
self.entry_prices.get(symbol) is None and # Not already holding
ema_short_value > ema_long_value and # Bullish crossover
rsi_value is not None and 55 <= rsi_value <= 85 and # RSI in range
macd_line > signal_line and histogram > 0
# macd more than signal and positive - bull trend
):
self.Debug(f"Buying {symbol}")
self.SetHoldings(symbol, 0.15) # Allocate 15% of the portfolio
self.entry_prices[symbol] = current_price
def OnSecuritiesChanged(self, changes: SecurityChanges):
for security in changes.AddedSecurities:
symbol = security.Symbol
# Initialize indicators for the symbol
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)