| 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%")