| Overall Statistics |
|
Total Orders 541 Average Win 2.51% Average Loss -3.30% Compounding Annual Return 60.785% Drawdown 56.400% Expectancy 0.287 Start Equity 100000 End Equity 1072688.05 Net Profit 972.688% Sharpe Ratio 1.125 Sortino Ratio 1.479 Probabilistic Sharpe Ratio 47.354% Loss Rate 27% Win Rate 73% Profit-Loss Ratio 0.76 Alpha 0.377 Beta 1.415 Annual Standard Deviation 0.427 Annual Variance 0.182 Information Ratio 1.068 Tracking Error 0.381 Treynor Ratio 0.339 Total Fees $6285.47 Estimated Strategy Capacity $0 Lowest Capacity Asset PLTR XIAKBH8EIMHX Portfolio Turnover 7.98% Drawdown Recovery 533 |
#region imports
from AlgorithmImports import *
#endregion
class MaxAlphaMomentum(QCAlgorithm):
def Initialize(self):
# 1. Timeline: Jan 2021 - Present (High Volatility Era)
self.SetStartDate(2021, 1, 1)
self.SetEndDate(2026, 1, 28)
self.SetCash(100000)
self.SetName("Max Alpha: Winner Takes All")
# 2. The "Rocket Ship" Universe
# We removed ARM. We kept only the highest beta AI names.
self.tickers = [
"NVDA", # The King
"SMCI", # The Prince (High Volatility)
"PLTR", # The Retail Favorite
"AMD", # The Runner Up
"META", # The Comeback Kid
"AVGO", # The Infrastructure
"TSM" # The Foundry
]
self.symbols = []
self.data = {}
# 3. Setup Indicators
for ticker in self.tickers:
symbol = self.AddEquity(ticker, Resolution.Daily).Symbol
self.symbols.append(symbol)
# We use a 40-Day EMA (Exponential Moving Average)
# EMA reacts faster than SMA, protecting Sharpe during crashes.
self.data[symbol] = self.EMA(symbol, 40, Resolution.Daily)
# 4. Strategy Parameters
self.lookback = 63 # 3 Months (Trading Days) for Return Calculation
self.hold_count = 2 # Hold only the TOP 2 stocks (Concentrated bets)
# 5. Schedule: Check for new winners every week (Not month)
# Checking weekly captures breakouts faster than monthly.
self.Schedule.On(
self.DateRules.WeekStart("NVDA"),
self.TimeRules.AfterMarketOpen("NVDA", 30),
self.Rebalance
)
# 6. Realistic Settings
self.SetBenchmark("SPY")
self.SetSecurityInitializer(lambda x: x.SetFeeModel(ConstantFeeModel(1.0)))
# Warmup is critical to avoid the error you saw
self.SetWarmUp(self.lookback + 10)
def Rebalance(self):
# FIX: Stop the "OrderRequest" error
if self.IsWarmingUp: return
# 1. Calculate 3-Month Returns
history = self.History(self.symbols, self.lookback, Resolution.Daily)
if history.empty: return
scores = {}
for symbol in self.symbols:
if symbol not in history.index: continue
closes = history.loc[symbol]['close']
if len(closes) < self.lookback: continue
# Metric: 3-Month Return
start_price = closes.iloc[0]
end_price = closes.iloc[-1]
if start_price == 0: continue
momentum = (end_price / start_price) - 1.0
# TREND FILTER:
# Only buy if price is ABOVE the 40-Day EMA.
# If it's below, we assume the trend is broken -> Score = -100
ema = self.data[symbol].Current.Value
if end_price > ema:
scores[symbol] = momentum
else:
scores[symbol] = -100 # Penalty box
# 2. Pick the Winners
# Sort descending by Momentum
sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True)
# Select Top N
# Filter out negative scores (Stocks in downtrend)
selected = [x[0] for x in sorted_scores[:self.hold_count] if x[1] > -100]
# 3. Execute
# Liquidate losers
for symbol in self.Portfolio.Keys:
if self.Portfolio[symbol].Invested and symbol not in selected:
self.Liquidate(symbol)
# Buy Winners
if len(selected) > 0:
weight = 1.0 / len(selected)
for symbol in selected:
self.SetHoldings(symbol, weight)
else:
# If NO stocks are trending (e.g., 2022 Bear Market), hold Cash.
pass