Overall Statistics
Total Orders
3878
Average Win
0.71%
Average Loss
-0.67%
Compounding Annual Return
-9.425%
Drawdown
55.700%
Expectancy
-0.028
Start Equity
100000
End Equity
55564.15
Net Profit
-44.436%
Sharpe Ratio
-0.304
Sortino Ratio
-0.291
Probabilistic Sharpe Ratio
0.025%
Loss Rate
53%
Win Rate
47%
Profit-Loss Ratio
1.07
Alpha
-0.113
Beta
0.407
Annual Standard Deviation
0.226
Annual Variance
0.051
Information Ratio
-0.752
Tracking Error
0.237
Treynor Ratio
-0.169
Total Fees
$15521.01
Estimated Strategy Capacity
$1100000.00
Lowest Capacity Asset
UUP TQBX2PUC67OL
Portfolio Turnover
136.99%
from AlgorithmImports import *
from datetime import timedelta
import numpy as np

class AdaptiveDayTradingStrategy(QCAlgorithm):
    """
    Day Trading Strategy that implements:
    - Market regime detection using SPY 200 EMA
    - RSI-based fund surfing among different asset groups
    - Volatility hedging with UUP+GLD weighted by inverse volatility
    - Overbought/oversold triggers using multiple timeframes
    """
    
    def Initialize(self):
        """Initialize algorithm settings, data subscriptions, and indicators"""
        # Set start date and cash
        self.SetStartDate(2019, 1, 1)
        self.SetCash(100000)
        
        # Dictionary to store symbols
        self.symbols = {}
        
        # Add all required equities
        tickers = ["SPY", "QQQ", "TQQQ", "SPXL", "SQQQ", "UVXY", "UUP", "GLD", "SHY"]
        for ticker in tickers:
            self.symbols[ticker] = self.AddEquity(ticker, Resolution.Minute).Symbol
        
        # Dictionary to store indicators
        self.indicators = {}
        
        # Initialize indicators for each symbol
        for ticker in tickers:
            symbol = self.symbols[ticker]
            
            # RSI indicators
            self.indicators[f"{ticker}_rsi_10"] = self.RSI(symbol, 10)
            self.indicators[f"{ticker}_rsi_20"] = self.RSI(symbol, 20)
            
            # EMAs and SMAs
            if ticker == "SPY":
                self.indicators[f"{ticker}_ema_200"] = self.EMA(symbol, 200)
            if ticker == "TQQQ":
                self.indicators[f"{ticker}_sma_20"] = self.SMA(symbol, 20)
                
        # Volatility windows for inverse vol weighting
        self.volatility_window = 30
        
        # Schedule execution times
        self.Schedule.On(self.DateRules.EveryDay(), 
                        self.TimeRules.AfterMarketOpen("SPY", 1), 
                        self.CheckSignalsAndTrade)
        
        self.Schedule.On(self.DateRules.EveryDay(), 
                        self.TimeRules.BeforeMarketClose("SPY", 5), 
                        self.ClosePositions)

    def CheckSignalsAndTrade(self):
        """
        Main trading logic implementation following the strategy flowchart
        """
        if not self.AreIndicatorsReady():
            return
        
        # Clear any existing positions first
        self.Liquidate()
        
        # Check market regime using SPY 200 EMA
        spy_price = self.Securities["SPY"].Price
        spy_200_ema = self.indicators["SPY_ema_200"].Current.Value
        
        # Execute the appropriate market regime logic
        if spy_price > spy_200_ema:
            self.ExecuteBullMarketLogic()
        else:
            self.ExecuteBearMarketLogic()

    def ExecuteBullMarketLogic(self):
        """
        Bull Market Logic Branch (SPY > 200 EMA)
        Exactly follows the PlantUML specification
        """
        # Check TQQQ overbought
        if self.GetRSI("TQQQ", 10) > 79:
            self.SetHoldings("UVXY", 1.0)
            return
            
        # Check SPXL overbought
        if self.GetRSI("SPXL", 10) > 79:
            self.SetHoldings("UVXY", 1.0)
            return
            
        # Check QQQ 5-day decline
        qqq_5d_return = self.GetPeriodReturn("QQQ", 5)
        
        if qqq_5d_return < -0.06:  # Less than -6%
            tqqq_1d_return = self.GetPeriodReturn("TQQQ", 1)
            
            if tqqq_1d_return > 0.05:  # Greater than 5%
                self.ExecuteVolatilityHedge()
            elif self.GetRSI("TQQQ", 10) > 31:
                self.ExecuteVolatilityHedge()
            else:
                self.ExecuteFundSurf(["SHY", "TQQQ", "SPXL"])
        else:
            if self.GetRSI("QQQ", 10) < 31:
                self.ExecuteFundSurf(["SHY", "TQQQ", "SPXL"])
            else:
                self.ExecuteFundSurf(["QQQ", "SPY"])

    def ExecuteBearMarketLogic(self):
        """
        Bear Market Logic Branch (SPY <= 200 EMA)
        Exactly follows the PlantUML specification
        """
        # First check TQQQ oversold condition
        if self.GetRSI("TQQQ", 10) < 31:
            self.ExecuteFundSurf(["SHY", "TQQQ", "SPXL"])
            return
        
        # Check UVXY overbought conditions
        uvxy_rsi = self.GetRSI("UVXY", 10)
        if uvxy_rsi > 74:
            if uvxy_rsi > 84:
                self.ExecuteVolatilityHedge()
            else:
                self.SetHoldings("UVXY", 1.0)
            return
        
        # Check TQQQ price vs SMA
        tqqq_price = self.Securities["TQQQ"].Price
        tqqq_sma = self.indicators["TQQQ_sma_20"].Current.Value
        
        if tqqq_price > tqqq_sma:
            if self.GetRSI("SQQQ", 10) < 31:
                self.ExecuteVolatilityHedge()
            else:
                self.ExecuteFundSurf(["SHY", "TQQQ", "SPXL"])
        else:
            self.ExecuteVolatilityHedge()

    def ExecuteVolatilityHedge(self):
        """
        Implements volatility hedging using UUP and GLD
        Weighted by 30-day inverse volatility
        """
        # Calculate 30-day volatilities
        uup_vol = self.GetHistoricalVolatility("UUP", self.volatility_window)
        gld_vol = self.GetHistoricalVolatility("GLD", self.volatility_window)
        
        # Calculate inverse volatility weights
        total_inv_vol = (1/uup_vol + 1/gld_vol)
        uup_weight = (1/uup_vol) / total_inv_vol
        gld_weight = (1/gld_vol) / total_inv_vol
        
        # Set positions
        self.SetHoldings("UUP", uup_weight)
        self.SetHoldings("GLD", gld_weight)

    def ExecuteFundSurf(self, tickers):
        """
        Implements fund surfing by selecting the asset with lowest 20-day RSI
        
        Parameters:
        tickers (list): List of tickers to consider for fund surfing
        """
        lowest_rsi = float('inf')
        selected_ticker = None
        
        # Find the ticker with the lowest 20-day RSI
        for ticker in tickers:
            current_rsi = self.GetRSI(ticker, 20)
            if current_rsi < lowest_rsi:
                lowest_rsi = current_rsi
                selected_ticker = ticker
        
        if selected_ticker:
            self.SetHoldings(selected_ticker, 1.0)

    def ClosePositions(self):
        """Close all positions at end of day"""
        self.Liquidate()

    def GetRSI(self, ticker, period):
        """Helper method to get current RSI value"""
        return self.indicators[f"{ticker}_rsi_{period}"].Current.Value

    def GetHistoricalVolatility(self, ticker, window):
        """Calculate historical volatility for the specified window"""
        history = self.History(self.symbols[ticker], window, Resolution.Daily)
        if len(history) < window:
            return float('inf')
        returns = np.diff(np.log(history['close'].values))
        return np.std(returns) * np.sqrt(252)  # Annualized

    def GetPeriodReturn(self, ticker, days):
        """Calculate return over specified number of days"""
        history = self.History(self.symbols[ticker], days + 1, Resolution.Daily)
        if len(history) < days + 1:
            return 0
        prices = history['close'].values
        return (prices[-1] / prices[0]) - 1

    def AreIndicatorsReady(self):
        """Check if all indicators have enough data"""
        for indicator in self.indicators.values():
            if not indicator.IsReady:
                return False
        return True

    def OnData(self, data):
        """OnData event handler. Required but not used."""
        pass