sma200.trade
For informational and educational use only. Not investment advice. Past performance does not predict future returns. Read full disclaimer →
🟡 Research note · May 14, 2026 · Updated · superseded by Borrow-Cost Correction: Synthetic LETF Methodology + Revised Numbers

SMA200 on Extended/Synthetic Long Windows

Leveraged ETFs #synthetic-letf#sma200#s&p-500#long-history#regime

SMA200 on Extended/Synthetic Long Windows

Question

Reddit commenter on Post #2 challenged the regime bias of the 2010-2026 LETF inception data, arguing that extending the backtest through dotcom + GFC would flip the filter-vs-B&H comparison.

This study tests that claim by extending the SMA200 vs buy-and-hold analysis on three longer windows:

  1. Synthetic TQQQ since 1999 (~27 years, includes dotcom + GFC + COVID + 2022)
  2. Unleveraged S&P 500 since 1940 (~86 years, includes WWII, 1970s stagflation, dotcom, GFC, COVID, 2022)
  3. Synthetic 3x S&P 500 since 1940 (same window, leveraged version, illustrative not realistic)

Methodology

  • Harness: src/trader/backtest/engine.py, same as the inception study
  • Data: yfinance for QQQ (back to 1999-03-10), ^GSPC (back to 1927)
  • Synthesis: Synthetic LETFs constructed as (1 + leverage × daily_return − expense_ratio/252).cumprod(). TQQQ expense modeled at 0.86% annual; UPRO/SPXL at 0.91%. No financing cost modeled (this is the biggest pre-2009 unrealism).
  • Strategy: Pure SMA200 (long when close > SMA200, cash otherwise). Same lag, costs, no-lookahead conventions as the inception study.
  • Cost: 1bp per side.
  • Starting capital: $10,000.

Results

Synthetic TQQQ since 1999-03-10 (27.1 years)

Buy-and-hold SMA200 filter
Final equity (from $10k) $90,612 $1,605,618
CAGR 8.46% 20.58%
Max drawdown -99.96% -77.51%
Volatility 80.83% 44.11%
Sharpe (rf=0) 0.506 0.647
Sortino 0.669 0.667

Sharpe diff (filter − B&H): +0.141. Compare to real TQQQ 2010-2026: −0.063. Extending back through dotcom flips the sign by ~0.20 Sharpe points.

Unleveraged S&P 500 since 1940-01-02 (86.2 years)

Buy-and-hold SMA200 filter
Final equity (from $10k) $5,830,383 $4,174,993
CAGR 7.67% 7.25%
Max drawdown -56.78% -28.56%
Volatility 15.73% 10.30%
Sharpe (rf=0) 0.549 0.731
Sortino 0.693 0.807
Calmar 0.135 0.254

Sharpe diff (filter − B&H): +0.183. The canonical academic result for trend-following on the broad market, reproduced over the longest credible window.

Decade-by-decade on unleveraged S&P 500 (the real story)

Decade B&H CAGR SMA CAGR B&H MDD SMA MDD Sharpe diff
1940s (WWII) +2.91% +5.20% -41.5% -16.9% +0.322
1950s (boom) +13.70% +11.19% -21.5% -10.6% +0.024
1960s (sideways) +4.45% +6.55% -28.0% -7.3% +0.575
1970s (stagflation) +1.50% +2.96% -48.2% -14.5% +0.230
1980s (bull + 1987) +12.78% +10.37% -33.5% -18.0% +0.122
1990s (dotcom run-up) +15.06% +12.68% -19.9% -15.3% -0.000
2000s (lost decade) -2.63% +2.64% -56.8% -21.5% +0.353
2010s (QE bull) +11.07% +6.06% -19.8% -15.5% -0.170
2020s (so far) +13.21% +9.90% -33.9% -19.8% +0.228

The 2010s is the ONLY decade in 9 where the filter underperforms on Sharpe. And that's the decade dominating the LETF inception data (since LETFs only existed from 2009 forward).

Synthetic 3x S&P 500 since 1940 (illustrative, not realistic)

Buy-and-hold SMA200 filter
Final equity (from $10k) $1,202,884,233 $15,697,944,713
CAGR 14.54% 18.01%
Max drawdown -97.84% -65.06%
Sharpe 0.530 0.711

Sharpe diff: +0.182 — basically the same gap as unleveraged.

Interpretation

The Reddit commenter was right. The original Post #2's "filter underperforms on Sharpe" framing was a 2010-2026 sample artifact, not a general truth.

Across 86 years of S&P 500 data: - Filter wins meaningfully in 5 of 9 decades (1940s, 1960s, 1970s, 2000s, 2020s) - Ties or marginal in 2 decades (1950s, 1980s) - Marginal loss in 1990s (clean dotcom run-up) - Meaningful loss in 2010s (QE bull)

The filter is structurally a drawdown manager that wins in bear/sideways regimes and loses in clean bull regimes. LETF inception backtests are biased toward the 2010s because that's when the funds existed; the structural bias of the sample misrepresents the long-run case for the filter.

For the synthetic TQQQ comparison specifically: extending from 16 years to 27 years (adding dotcom) was sufficient to flip the Sharpe ordering. The 86-year synthetic 3x SP result of +0.182 Sharpe is consistent with the unleveraged result, suggesting the trend filter's edge is robust across the leverage dimension.

Caveats

  1. Pre-2009 LETF synthesis ignores financing costs. Real borrow at 4-15% prime rates during 1940-1980 would have eaten meaningful CAGR from any 3x daily-rebalance strategy. The synthetic 3x numbers (especially the $1.2B and $15.7B headline final values) are directional, not what you'd have actually earned.

  2. The $1.2B → $15.7B headline numbers are visually absurd because we're compounding 86 years at high CAGR without realistic frictions. Use them only to compare strategies against each other, not as predictions.

  3. ^GSPC is the S&P 500 PRICE INDEX (no dividends). The "real" S&P with dividends reinvested (^SP500TR) only goes back to 1988. Using ^GSPC understates absolute returns for both B&H and the filter, but the COMPARATIVE answer is approximately right since dividends accrue proportionally to time-in-market.

  4. The synthetic TQQQ vs testfol.io discrepancy: our model gives $90k B&H ending value; testfol shows ~$19k. Both methodologies hit ~99.97% max drawdown. The difference is recovery math from near-wipeout being extremely sensitive to modeling choices (financing, rebalance, slippage). Qualitative conclusion (B&H got eaten in 2000-2002, filter survived) is robust either way.

Source

Inline runner (SP500 1940 version):

import sys
sys.path.insert(0, '/Volumes/Mac External/Claudes/trader/src')
from trader.data.yfinance_src import fetch_daily
from trader.backtest.engine import run_backtest
from trader.strategies.buy_and_hold import BuyAndHold
from trader.strategies.price_above_sma import PriceAboveSma

spx = fetch_daily("^GSPC", start="1940-01-01")
bh = run_backtest("SPX B&H", spx, BuyAndHold().generate_positions(spx), cost_bps=1.0)
sma = run_backtest("SPX SMA200", spx, PriceAboveSma(window=200).generate_positions(spx), cost_bps=1.0)
print(f"BH Sharpe {bh.metrics['sharpe']:.3f}, SMA Sharpe {sma.metrics['sharpe']:.3f}")

Saved logs: /tmp/sma200_1940.log, /tmp/synthetic_tqqq.log.

This is research output, not investment advice. Backtest results do not predict future returns. Specific portfolio compositions discussed here are illustrative test cases, not allocation recommendations. Do your own research and consult a licensed advisor for personalized advice. Full disclaimer →