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