Overall Statistics
Total Orders
25
Average Win
104.14%
Average Loss
-22.74%
Compounding Annual Return
31.556%
Drawdown
76.600%
Expectancy
2.550
Start Equity
100000
End Equity
4660524.92
Net Profit
4560.525%
Sharpe Ratio
0.709
Sortino Ratio
0.763
Probabilistic Sharpe Ratio
5.084%
Loss Rate
36%
Win Rate
64%
Profit-Loss Ratio
4.58
Alpha
0.185
Beta
1.785
Annual Standard Deviation
0.494
Annual Variance
0.244
Information Ratio
0.586
Tracking Error
0.439
Treynor Ratio
0.196
Total Fees
$6749.26
Estimated Strategy Capacity
$200000000.00
Lowest Capacity Asset
TQQQ UK280CGTCB51
Portfolio Turnover
0.44%
from AlgorithmImports import *

class SMAStrategy(QCAlgorithm):

    def Initialize(self):
        # --- Strategy Initialization ---
        self.SetStartDate(2010, 9, 1)
        self.SetEndDate(2024, 9, 1)
        self.SetCash(100000)  # Set the initial capital for the strategy

        # --- Symbol Selection ---
        # We will use QQQ for the SMA calculations, but trade TQQQ and SQQQ
        self.qqq = self.AddEquity("QQQ", Resolution.Daily).Symbol
        self.tqqq = self.AddEquity("TQQQ", Resolution.Daily).Symbol
        self.sqqq = self.AddEquity("SQQQ", Resolution.Daily).Symbol

        # --- Data Preparation: SMA Definitions ---
        # Define fast and slow SMAs for QQQ
        self.fast_sma = self.SMA(self.qqq, 100, Resolution.Daily)  # Fast SMA (around 5 months)
        self.slow_sma = self.SMA(self.qqq, 200, Resolution.Daily)  # Slow SMA (around 10 months)

        # Initialize benchmark for QQQ Buy and Hold
        self.benchmark_initial_price = None
        self.benchmark_initial_cash = self.Portfolio.Cash  # Start with the same cash amount for benchmark

        # --- Data Warmup: Ensure enough data for SMA calculations ---
        self.SetWarmUp(252)  # Warm up period of 252 days (~12 months)

    def OnData(self, data: Slice):
        # --- Data Preparation and Validity Check ---
        # Ensure data is warmed up and available
        if self.IsWarmingUp:
            return
        
        # Ensure data is available for QQQ (used for SMA and benchmark)
        if not data.ContainsKey(self.qqq) or not data[self.qqq]:
            return

        # --- Benchmark Plotting: QQQ Buy and Hold Calculation ---
        if self.benchmark_initial_price is None:
            # Initialize the benchmark with the first price available
            self.benchmark_initial_price = data[self.qqq].Price
        
        # Calculate the current value of the QQQ Buy and Hold benchmark
        benchmark_return = (data[self.qqq].Price - self.benchmark_initial_price) / self.benchmark_initial_price
        benchmark_value = self.benchmark_initial_cash * (1 + benchmark_return)
        
        # Plot the QQQ Buy and Hold benchmark
        self.Plot("Strategy Equity", "QQQ Buy and Hold", benchmark_value)

        # --- Trade Logic: Buy TQQQ or SQQQ based on SMA ---
        # Ensure SMAs are ready before executing trades
        if not self.fast_sma.IsReady or not self.slow_sma.IsReady:
            return

        # If fast SMA > slow SMA, buy TQQQ (leveraged long)
        if self.fast_sma.Current.Value > self.slow_sma.Current.Value:
            # Sell SQQQ if held
            if self.Portfolio[self.sqqq].Invested:
                self.Liquidate(self.sqqq)
            # Buy TQQQ if not already invested
            if not self.Portfolio[self.tqqq].Invested:
                self.SetHoldings(self.tqqq, 1.0)  # Invest 100% in TQQQ
        
        # If fast SMA <= slow SMA, sell QQQ and buy SQQQ (leveraged short)
        elif self.fast_sma.Current.Value <= self.slow_sma.Current.Value:
            # Liquidate TQQQ if held
            if self.Portfolio[self.tqqq].Invested:
                self.Liquidate(self.tqqq)
            # Buy SQQQ if not already invested
            if not self.Portfolio[self.sqqq].Invested:
                self.SetHoldings(self.sqqq, 1.0)  # Invest 100% in SQQQ