| Overall Statistics |
|
Total Orders 409 Average Win 6.41% Average Loss -2.98% Compounding Annual Return 66.398% Drawdown 42.700% Expectancy 0.545 Start Equity 100000 End Equity 1386489.72 Net Profit 1286.490% Sharpe Ratio 1.284 Sortino Ratio 1.635 Probabilistic Sharpe Ratio 62.292% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 2.15 Alpha 0.384 Beta 1.166 Annual Standard Deviation 0.382 Annual Variance 0.146 Information Ratio 1.156 Tracking Error 0.345 Treynor Ratio 0.42 Total Fees $12428.58 Estimated Strategy Capacity $0 Lowest Capacity Asset TSM R735QTJ8XC9X Portfolio Turnover 11.18% Drawdown Recovery 458 |
#region imports
from AlgorithmImports import *
#endregion
class LiveReadyMomentum(QCAlgorithm):
def Initialize(self):
# 1. Timeline: Jan 2021 to Present
self.SetStartDate(2020, 11, 1)
self.SetEndDate(2026, 1, 28)
self.SetCash(100000)
self.SetName("AI Momentum + 7% Trailing Stop")
# 2. Universe: High Beta AI
self.tickers = [
"NVDA", "SMCI", "PLTR", "AMD",
"META", "AVGO", "TSM", "MSFT"
]
self.symbols = []
self.data = {}
# Track the "High Water Mark" for each holding
self.high_water_marks = {}
for ticker in self.tickers:
symbol = self.AddEquity(ticker, Resolution.Daily).Symbol
self.symbols.append(symbol)
# Faster Trend Filter (21 EMA) for quick trend detection
self.data[symbol] = self.EMA(symbol, 21, Resolution.Daily)
self.high_water_marks[symbol] = 0
# 3. Parameters
self.lookback = 63 # 3 Month Momentum
self.hold_count = 2 # Top 2 Stocks
self.stop_loss_pct = 0.07 # 7% Trailing Stop
# 4. Schedule Rebalance (Weekly)
self.Schedule.On(
self.DateRules.WeekStart("NVDA"),
self.TimeRules.AfterMarketOpen("NVDA", 30),
self.Rebalance
)
self.SetBenchmark("SPY")
self.SetSecurityInitializer(lambda x: x.SetFeeModel(ConstantFeeModel(1.0)))
self.SetWarmUp(self.lookback + 10)
def OnData(self, data):
if self.IsWarmingUp: return
# --- TRAILING STOP LOGIC ---
# This runs every day to protect your gains
for symbol in self.Portfolio.Keys:
if not self.Portfolio[symbol].Invested:
self.high_water_marks[symbol] = 0 # Reset if not owned
continue
# Check if we have data
if not data.ContainsKey(symbol) or data[symbol] is None:
continue
current_price = data[symbol].Close
# 1. Update High Water Mark
# If price is higher than our record, update the record
if current_price > self.high_water_marks[symbol]:
self.high_water_marks[symbol] = current_price
# 2. Check for Stop Loss Hit
# If price is 7% below the High Water Mark -> SELL
stop_price = self.high_water_marks[symbol] * (1 - self.stop_loss_pct)
if current_price < stop_price:
self.Liquidate(symbol, "Trailing Stop Hit")
# Reset high water mark to prevent immediate rebuy issues
self.high_water_marks[symbol] = 0
def Rebalance(self):
if self.IsWarmingUp: return
# 1. Calculate Momentum
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
# Momentum Calculation
start_price = closes.iloc[0]
end_price = closes.iloc[-1]
if start_price == 0: continue
momentum = (end_price / start_price) - 1.0
# TREND FILTER (Entry Requirement)
# Only buy if price is > 21 EMA
# AND if we haven't just been stopped out (Price > Stop Price check implied)
ema = self.data[symbol].Current.Value
if end_price > ema:
scores[symbol] = momentum
else:
scores[symbol] = -100
# 2. Select Winners
sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True)
selected = [x[0] for x in sorted_scores[:self.hold_count] if x[1] > -100]
# 3. Execution
for symbol in self.Portfolio.Keys:
# Don't sell if it's a winner we want to keep
# UNLESS it hit the trailing stop (handled in OnData)
if self.Portfolio[symbol].Invested and symbol not in selected:
self.Liquidate(symbol)
if len(selected) > 0:
weight = 1.0 / len(selected)
for symbol in selected:
# Only buy if not already invested to avoid fee churn
# (Or rebalance if you want exact equal weights)
if not self.Portfolio[symbol].Invested:
self.SetHoldings(symbol, weight)
# Initialize High Water Mark for new trade
self.high_water_marks[symbol] = self.Securities[symbol].Price