Overall Statistics
Total Orders
39
Average Win
0.91%
Average Loss
-1.14%
Compounding Annual Return
4.515%
Drawdown
6.000%
Expectancy
0.325
Start Equity
10000
End Equity
10768.18
Net Profit
7.682%
Sharpe Ratio
-0.595
Sortino Ratio
-0.619
Probabilistic Sharpe Ratio
37.672%
Loss Rate
26%
Win Rate
74%
Profit-Loss Ratio
0.80
Alpha
-0.05
Beta
0.21
Annual Standard Deviation
0.039
Annual Variance
0.002
Information Ratio
-1.065
Tracking Error
0.141
Treynor Ratio
-0.111
Total Fees
$39.00
Estimated Strategy Capacity
$2700000000.00
Lowest Capacity Asset
QQQ RIWIV7K5Z9LX
Portfolio Turnover
1.46%
Drawdown Recovery
174
#region imports
from AlgorithmImports import *
#endregion

"""
TRADE LOGIC DESCRIPTION

This strategy trades QQQ using a simple monthly timing rule. The model enters
a long QQQ position near the beginning of each month and exits near the end of
the month. The purpose is to test whether being invested during most of the
calendar month can create value compared with a passive benchmark. The strategy
does not use technical indicators, price momentum, volatility filters, or macro
signals. It is purely calendar-based. To keep the model conservative and easy to
interpret, the strategy invests only 25% of the portfolio in QQQ when active and
keeps the remaining 75% in cash. The benchmark is therefore also constructed as
a steady 25% QQQ and 75% cash portfolio. This creates a fair comparison because
both the strategy and benchmark use the same maximum equity exposure. The model
plots the strategy equity curve and the benchmark equity curve on the same chart.
"""

class RetrospectiveYellowGreenAlligator(QCAlgorithm):

    def Initialize(self):

        # ------------------------------------------------------------
        # 1. BACKTEST SETTINGS
        # ------------------------------------------------------------
        self.SetStartDate(2024, 9, 1)
        self.SetEndDate(2026, 5, 5)

        self.initial_cash = 10000
        self.SetCash(self.initial_cash)

        # ------------------------------------------------------------
        # 2. ASSETS
        # ------------------------------------------------------------
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        self.qqq = self.AddEquity("QQQ", Resolution.Daily).Symbol

        # Built-in benchmark
        self.SetBenchmark(self.qqq)

        # ------------------------------------------------------------
        # 3. STRATEGY PARAMETERS
        # ------------------------------------------------------------
        # Maximum QQQ exposure.
        # The strategy never invests more than 25% of the portfolio in QQQ.
        self.qqq_target_weight = 0.25

        # Benchmark is steady 25% QQQ and 75% cash.
        self.benchmark_qqq_weight = 0.25
        self.benchmark_cash_weight = 0.75

        # Used for benchmark plotting
        self.initial_qqq_price = None

        # ------------------------------------------------------------
        # 4. SCHEDULED TRADING RULES
        # ------------------------------------------------------------
        # Buy QQQ on the first trading day of each month.
        self.Schedule.On(
            self.DateRules.MonthStart(self.qqq),
            self.TimeRules.AfterMarketOpen(self.qqq, 10),
            self.Rebalance
        )

        # Sell QQQ on the last trading day of each month.
        self.Schedule.On(
            self.DateRules.MonthEnd(self.qqq),
            self.TimeRules.BeforeMarketClose(self.qqq, 10),
            self.ExitPosition
        )

    def OnData(self, data):

        # ------------------------------------------------------------
        # 1. CHECK DATA
        # ------------------------------------------------------------
        if self.qqq not in data or data[self.qqq] is None:
            return

        qqq_price = self.Securities[self.qqq].Close

        # ------------------------------------------------------------
        # 2. INITIALIZE BENCHMARK PRICE
        # ------------------------------------------------------------
        if self.initial_qqq_price is None:
            self.initial_qqq_price = qqq_price

        # ------------------------------------------------------------
        # 3. PLOT STRATEGY EQUITY CURVE
        # ------------------------------------------------------------
        self.Plot(
            "Equity Curve vs Benchmark",
            "Strategy Monthly QQQ",
            self.Portfolio.TotalPortfolioValue
        )

        # ------------------------------------------------------------
        # 4. PLOT FAIR BENCHMARK
        # ------------------------------------------------------------
        # Benchmark: steady 25% QQQ and 75% cash.
        if self.initial_qqq_price is not None:

            benchmark_value = (
                self.initial_cash
                * (
                    self.benchmark_cash_weight
                    + self.benchmark_qqq_weight
                    * qqq_price
                    / self.initial_qqq_price
                )
            )

            self.Plot(
                "Equity Curve vs Benchmark",
                "Benchmark 25 pct QQQ 75 pct Cash",
                benchmark_value
            )

    def Rebalance(self):

        # ------------------------------------------------------------
        # ENTER POSITION
        # ------------------------------------------------------------
        # At the start of the month, invest 25% of the portfolio in QQQ.
        self.SetHoldings(self.qqq, self.qqq_target_weight)

        self.Debug(
            f"MONTH START BUY QQQ | Target Weight {self.qqq_target_weight:.0%}"
        )

    def ExitPosition(self):

        # ------------------------------------------------------------
        # EXIT POSITION
        # ------------------------------------------------------------
        # At the end of the month, exit QQQ and return the tactical sleeve to cash.
        self.Liquidate(self.qqq)

        self.Debug("MONTH END SELL QQQ | Target Weight 0%")