Overall Statistics
Total Orders
1305
Average Win
0.48%
Average Loss
-0.51%
Compounding Annual Return
8.642%
Drawdown
13.800%
Expectancy
0.118
Start Equity
100000
End Equity
152863.97
Net Profit
52.864%
Sharpe Ratio
0.323
Sortino Ratio
0.381
Probabilistic Sharpe Ratio
27.421%
Loss Rate
42%
Win Rate
58%
Profit-Loss Ratio
0.93
Alpha
0.027
Beta
-0.03
Annual Standard Deviation
0.078
Annual Variance
0.006
Information Ratio
-0.277
Tracking Error
0.165
Treynor Ratio
-0.838
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
AMD R735QTJ8XC9X
Portfolio Turnover
4.31%
Drawdown Recovery
530
from AlgorithmImports import *

class TechMomentumWithHedges(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2021, 1, 1)
        self.SetCash(100_000)
        
        self.tech_stocks = ["NVDA", "AMD", "PLTR", "TSLA", "MSFT", "GOOGL", "META", "AVGO", "ARM", "SNOW"]
        self.hedge_assets = {"GLD": 0.12, "TLT": 0.12, "XLU": 0.08, "VNQ": 0.04, "SQQQ": 0.14}
        
        # Benchmark and Relative Strength Indicator
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        self.spy_mom = self.MOMP(self.spy, 21)
        self.SetBenchmark(self.spy)
        
        self.symbols = {}
        for ticker in self.tech_stocks + list(self.hedge_assets.keys()):
            self.symbols[ticker] = self.AddEquity(ticker, Resolution.Daily).Symbol
        
        self.indicators = {}
        for ticker in self.tech_stocks:
            s = self.symbols[ticker]
            self.indicators[ticker] = {
                'rsi': self.RSI(s, 14),
                'macd': self.MACD(s, 12, 26, 9),
                'sma20': self.SMA(s, 20),
                'sma50': self.SMA(s, 50),
                'mom': self.MOMP(s, 21),
                'std': self.STD(s, 20)
            }
        
        self.Schedule.On(self.DateRules.WeekStart(self.spy), self.TimeRules.AfterMarketOpen(self.spy, 30), self.Rebalance)
        self.Schedule.On(self.DateRules.EveryDay(self.spy), self.TimeRules.AfterMarketOpen(self.spy, 60), self.RiskManagement)
        
        self.SetWarmUp(50)
        self.set_brokerage_model(BrokerageName.ALPACA) 

    def CalculateMomentumScore(self, ticker):
        if not self.indicators[ticker]['macd'].IsReady or not self.spy_mom.IsReady:
            return 0
        
        price = self.Securities[self.symbols[ticker]].Price
        ind = self.indicators[ticker]
        score = 0
        
        # Trend (40pts)
        if price > ind['sma20'].Current.Value and price > ind['sma50'].Current.Value: score += 20
        if ind['sma20'].Current.Value > ind['sma50'].Current.Value: score += 20
        
        # MACD (20pts)
        if ind['macd'].Current.Value > ind['macd'].Signal.Current.Value: score += 20
        
        # RSI Sweet Spot (20pts)
        rsi = ind['rsi'].Current.Value
        if 45 < rsi < 70: score += 20
        
        # REFINED: Relative Strength (20pts)
        # Bonus if outperforming SPY over the last month
        if ind['mom'].Current.Value > self.spy_mom.Current.Value:
            score += 20
            
        return score

    def Rebalance(self):
        if self.IsWarmingUp: return
        
        # 1. DETERMINE REGIME
        tech_vols = [self.indicators[t]['std'].Current.Value / self.Securities[self.symbols[t]].Price 
                     for t in self.tech_stocks if self.indicators[t]['std'].IsReady]
        avg_vol = sum(tech_vols) / len(tech_vols) if tech_vols else 0.02
        
        if avg_vol > 0.035:   tech_ratio, hedge_ratio = 0.35, 0.65
        elif avg_vol > 0.025: tech_ratio, hedge_ratio = 0.50, 0.50
        else:                 tech_ratio, hedge_ratio = 0.60, 0.40

        # 2. SELECT TECH TARGETS
        tech_scores = {t: self.CalculateMomentumScore(t) for t in self.tech_stocks}
        top_tech = [t for t, s in sorted(tech_scores.items(), key=lambda x: x[1], reverse=True)[:6] if s > 50]
        
        target_weights = {}

        # 3. ASSIGN TECH WEIGHTS (Mathematically locked to tech_ratio)
        if top_tech:
            total_score = sum(tech_scores[t] for t in top_tech)
            for t in top_tech:
                # Weighted by score, capped at 15% of total portfolio
                raw_weight = (tech_scores[t] / total_score) * tech_ratio
                target_weights[t] = min(raw_weight, 0.15)
            
            # Re-normalize tech to ensure they sum exactly to tech_ratio
            current_tech_sum = sum(target_weights.values())
            scale = tech_ratio / current_tech_sum
            for t in top_tech: target_weights[t] *= scale

        # 4. ASSIGN HEDGE WEIGHTS (Locked to hedge_ratio)
        base_hedge_sum = sum(self.hedge_assets.values())
        for ticker, base_w in self.hedge_assets.items():
            target_weights[ticker] = (base_w / base_hedge_sum) * hedge_ratio

        # 5. EXECUTION
        for ticker in self.tech_stocks + list(self.hedge_assets.keys()):
            symbol = self.symbols[ticker]
            weight = target_weights.get(ticker, 0)
            if abs(self.Portfolio[symbol].HoldingsValue / self.Portfolio.TotalPortfolioValue - weight) > 0.02:
                self.SetHoldings(symbol, weight)

    def RiskManagement(self):
        if self.IsWarmingUp: return
        
        for ticker in self.tech_stocks:
            symbol = self.symbols[ticker]
            if not self.Portfolio[symbol].Invested: continue
            
            # LOGIC CORRECTION: Use AveragePrice (Cost Basis)
            cost_basis = self.Portfolio[symbol].AveragePrice
            current_price = self.Securities[symbol].Price
            pnl_pct = (current_price - cost_basis) / cost_basis
            
            # Profit Taking (Partial)
            if pnl_pct > 0.30:
                self.MarketOrder(symbol, -int(self.Portfolio[symbol].Quantity * 0.5))
                self.Debug(f"Harvested 50% profit on {ticker}")
            
            # Stop Loss
            elif pnl_pct < -0.08:
                self.Liquidate(symbol)
                self.Debug(f"Stop-loss triggered for {ticker}")