QC Strategy Improver — Agent Memory (relocalisé)

Origine : fichier .claude/agent-memory/qc-strategy-improver/MEMORY.md (relocalisé en c.1301+211 dans le cadre de l’EPIC #9535, item 7 PR-C narrow). Le contenu est conservé tel quel (anti-régression : cf triage jsboigeEpita, durable vs scratch) ; seul un sommaire FR est ajouté.

Statut : durable. Le contenu documente (a) le statut de 25 stratégies (Sharpe v1 → latest, Cloud ID, issue #) avec identifier explicite du ceiling atteindable ; (b) les leçons par stratégie (Sector-ML-Classification, Chronos, LSTM, Momentum, Options, etc.) — patterns de factor momentum, position sizing, regime filters, multi-symbol warmup ; (c) les règles critiques (SPY Parking forbidden, read_file before update_file_contents, options = Resolution.MINUTE). C’est de la substance consolidée cross-stratégie, pas du snapshot daté.

Note de fraîcheur : le dernier update documenté est 2026-03-28 (Sector-ML-Classification v5). Pour des chiffres récents, croiser avec docs/qc/qc-strategies-status.md (auto-généré par qc-freshness guard PR #11441). Ce fichier reste valide comme consolidation de patterns même si les chiffres précis datent.

Strategy Status (last update 2026-03-28, Sector-ML-Classification v5)

Strategy Cloud ID Issue Sharpe v1 Sharpe latest Status
Sector-ML-Classification 29318875 — 0.352 0.473 v5: always top-N bull + bear cash 0.35 threshold + 11 features + equal-weight. Alpha -0.007->+0.009, Beta 0.964->0.799, MaxDD 41.7%->34.4%. IMPROVED.
RL-DQN-Trading 29443478 — 0.136 0.533 v2.0.1: MLPRegressor DQN, 5-ETF, risk-adj reward, target-fix. Beta=0.452, Alpha=0.019. STRONGLY IMPROVED.
VIX-TermStructure 28657907 #18 -0.27 +0.051 HARD CEILING REACHED - v4.1 is final
ForexCarry 28657908 #17 -0.654 -0.654 CEILING REACHED
MeanReversion 28657904 #19 0.294 0.365 IMPROVED
FuturesTrend 28657834 #20 0.280 0.301 HARD CEILING iter5 (2026-03-09)
TurnOfMonth 28657905 #21 0.127 0.128 CEILING REACHED
MomentumStrategy 28657837 #22 0.411 0.472 HARD CEILING - v4.0 is final (iter5 H10 rejected)
AllWeather 28657833 #23 0.365 0.602 CEILING (iter5 GLD30/IEF30)
OptionsIncome 28657838 #24 0.791 0.234 HARD CEILING - v7.0 2018-2026 is final
FamaFrench 28657910 #25 0.471 0.540 IMPROVED (HEALTHY)
Sector-Momentum 28433643 #26 0.554 0.555 CEILING (marginal)
DualMomentum 28692516 #35 NEW 0.350 CEILING REACHED (iter2 revert)
RiskParity 28692653 #35 NEW 0.399 CEILING REACHED
EMA-Cross-Index 28789945 — 0.384 ~0.43 expected RESEARCH DONE, NO BACKTEST YET
TrendStocksLite 28817425 — NEW 0.719 v1.0 initial (2026-03-09)
DualMomentumNoTLT 28817424 — NEW 0.469 v1.0 initial (2026-03-09)
Trend-Following 28797562 Partner 0.212 0.212 CEILING REACHED (iter6, 2026-03-09)
TrendFilteredMeanReversion 28817422 #40 NEW -0.016 HARD CEILING: H4 multi-instrument REJECTED (-0.129)
Gaussian-Direction-Classifier 29398513 — 0.864 0.761 v2.0: Beta 1.133->0.540, MaxDD 36.8%->25.6%
LSTM-Forecasting 29443476 — 0.366 0.525 v2.1: real MLPClassifier, 7-ETF, alpha -0.008->+0.016. IMPROVED.
Temporal-CNN-Prediction 29443034 — 0.169 0.536 v2: real MLPClassifier(128,64,32), 8-ETF, 18 features. Beta=0.997 (high).
Chronos-Foundation-Forecasting 29443479 — 0.114 0.253 v2: GBM+Ridge ensemble, SMA200 regime, 8-ETF. Beta 0.643->0.252, Alpha +0.002, MaxDD 22.4%. IMPROVED.

Sector-ML-Classification Lessons (2026-03-28) - iter1

IMPROVED: Sharpe 0.352 -> 0.473, Alpha -0.007 -> +0.009, Beta 0.964 -> 0.799, MaxDD 41.7% -> 34.4%

Key insight: RF as RANKER vs RF as GATE - v3/v3.1/v4 used RF as a binary gate (prob >= 0.5 to hold). When all sectors have prob < threshold, strategy either holds stale positions (bull) or goes to cash (bear). Cash drag kills bull Sharpe. - v5 solution: always hold top-N in bull market. RF ranks which sectors to prefer, but never gates out. Only in BEAR (SPY < SMA200) does a threshold (0.35) decide cash vs defensive.

Why equal-weight beat confidence-weighted (v4 vs v5): - With only 8 sectors and a RF model, confidence differences between top-4 are small (~0.40-0.60 range). - Confidence-weighting creates slight concentration in 1-2 sectors = higher vol, similar return. - Equal weight over top-N is simpler and more robust for ETF sector rotation.

v4 was important stepping stone (first positive Alpha +0.003): - Monthly retraining (vs quarterly): catches regime changes earlier. - 11 features (vs 9): 50d momentum + 52-week high ratio add long-term trend context. - Lower BUY_THRESHOLD 1.2% (vs 1.5%): more training samples, better balanced classes. - These improvements carried over to v5 and explain the Alpha improvement.

Bear market regime handling: - SPY < SMA200 activates defensive mode: only XLU, XLP, XLV candidates. - BEAR_MIN_PROB = 0.35: go to cash if even the best defensive sector looks weak. - This is softer than v3 (2 positions) - allows 2 positions if signals are there.

Evolution summary: - v2b: Beta 0.964 (pure SPY beta). No regime filter. - v3: SMA200 added, Beta 0.832. But forced fallback = noisy. Quarterly train. - v4: Monthly train + 11 features + prob-weighted. Alpha turns positive. But threshold gate = cash drag. - v5: No gate in bull + BEAR_MIN_PROB 0.35 + equal weight. Best Sharpe so far.

Chronos-Foundation-Forecasting Lessons (2026-03-28) - iter1

IMPROVED: Sharpe 0.114 -> 0.253, Beta 0.643 -> 0.252, MaxDD 31.4% -> 22.4%

Root cause v1-v5: Fake Chronos using hardcoded attention weights + random noise. Not learning anything. 4/7 versions had 0 trades (wrong schedule pattern).

v1 ensemble (GBM+Ridge, SPY only -> 8-ETF): Sharpe 0.234, Beta 0.643, Alpha -0.023. Real ML but high beta = bull market bias. Predicts positive returns for all equity ETFs in bull market. MaxDD 31.4% because all equity ETFs drop simultaneously in 2022 bear.

v2 (SMA200 regime + threshold=0.002) WINNER: Sharpe 0.253, Beta 0.252, Alpha +0.002, MaxDD 22.4%. SMA200 bear regime -> only consider GLD/IEF/TLT (defensive), max 2 positions. Minimum threshold 0.002 (0.2% over 10 days) filters noise predictions. KEY INSIGHT: SMA200 filter reduces beta from 0.643 to 0.252 (-61%) - transforms beta-loading to signal-driven.

Critical lesson: High beta in ML multi-ETF strategies If beta > 0.5 on a multi-ETF strategy, the ML model is predicting “always long equities” (training in bull market). Fix: SMA200 regime filter to reduce equity exposure in bear markets. This same pattern likely applies to Temporal-CNN-Prediction (Beta=0.997).

GBM+Ridge ensemble design validated: - 21 features: lag returns (1,2,3,5,10,20), rolling vol (5,10,20), rolling mean (5,10,20), price/SMA (20,50), SPY cross-asset (1,5,20) - 252-day rolling training window, monthly retraining - 60% GBM + 40% Ridge weighted ensemble - Pipeline with StandardScaler before each model

LSTM-Forecasting Lessons (2026-03-28) - iter1

IMPROVED: Sharpe 0.366 -> 0.525, Alpha -0.008 -> +0.016

Root cause v1.0: Fixed gate weights (0.7/0.3) = NOT training. 50% momentum blend hid the fake LSTM. Beta=0.886 meant the strategy was just buying SPY 88% of the time.

v2 (threshold=0.55, no min_pos) INTERMEDIATE: Sharpe 0.278, Beta 0.292. Real MLP but cash drag: 0 positions when all scores < 0.55. Cash earns 0% vs risk-free ~2-5%.

v2.1 (threshold=0.52, min_pos=2) WINNER: Sharpe 0.525, Beta 0.544, Alpha +0.016. Two fixes combined: lower threshold (more signals) + min_positions=2 (no cash drag).

Critical QC pattern for multi-symbol history warmup: - self.history[TradeBar](symbols_list, ...) returns a slice dict. Iterating gives TradeBars objects, NOT individual bars. - TradeBars has no .symbol attribute -> runtime error if you try bar.symbol - CORRECT pattern: loop per symbol, call self.history[TradeBar](sym, timedelta, Resolution.DAILY)

sklearn availability on QC Cloud: CONFIRMED. MLPClassifier, StandardScaler, Pipeline all work.

Pedagogical value: Shows the contrast between a fake “LSTM” (fixed weights) and a real trained classifier. Alpha turns from -0.008 to +0.016 when you use actual ML.

MomentumStrategy Lessons (2026-03-09) - iter5

HARD CEILING: Sharpe 0.472 (v4.0) - H10 rejected

H10 (momentum-proportional weights) REJECTED: Sharpe 0.472 -> 0.398, MaxDD 25.8% -> 33.3%. Concentration sur XLK (50-70% du poids) cree un risque idiosyncratique eleve. Quand XLK corrige, le portefeuille entier souffre.

Lecon cle: Dans la rotation sectorielle par ETFs, poids egaux > poids proportionnels au signal. Le signal de SELECTION (quel secteur) est informatif. Le signal d’INTENSITE (combien) ne l’est pas. Diversification intra-portfolio > concentration par signal pour ETFs sectoriels.

V4.0 est la version finale: vol-adj momentum, skip-month 21j, top-4, SMA200+SMA20, SL -10%.

OptionsIncome Lessons (2026-03-09)

HARD CEILING: Sharpe 0.234 on 2018-2026. v8.0 REVERTED.

Stock stop-loss -15% (v8.0): Sharpe 0.234 -> 0.090. Whipsaw on 2022 slow bear. Win rate paradox: 33% -> 65% (calls improved) but CAGR 6.8% -> 5.0% (stock leg whipsaw). Stock stop-loss triggered 2-3x during 2022 slow bear: sell low, buy after 20d, repeat.

DO NOT re-attempt: ceiling is structural (alpha=-0.021, beta=0.517). Premium ~6%/yr cannot offset -20% to -34% crashes. Covered calls cap gains in bull = negative alpha.

FuturesTrend Lessons - iter5 (2026-03-09)

HARD CEILING: Sharpe 0.301 (v3.1) - 5 iterations exhausted

Grid search 14 configs: parametres (SMA, positions, exit) tous explores. Aucun ameliore QC. - SMA30: yfinance IS 0.864 mais OOS 2022-2025 = -0.010 -> OVERFITTING SEVERE - 4x25% / 5x20%: tous < 3x33% baseline - Exit=15j: MaxDD monte, Sharpe baisse - Sans VNQ: yfinance +11% mais QC cloud -10% -> REVERT

LECON CRITIQUE: yfinance vs QC pour actifs a dividendes eleves (REITs/VNQ) - yfinance auto_adjust=True: dividendes integres dans le prix historique (retroactif) -> VNQ semble “drag” car ses gains de dividendes disparaissent dans le prix ajuste - QC raw prices: dividendes verses en cash, prix monte autour de l’ex-div date -> VNQ contribue positivement via gaps de prix + versements cash - Toute analyse yfinance sur REIT, utilities, high-yield peut INVERSER les conclusions - Regle: utiliser yfinance uniquement pour signaux de prix purs (trend, momentum). Pour universe selection/comparative analysis incluant dividendes -> tester directement sur QC.

Plafond structurel: Donchian 20/10 sur 6 ETFs, 2018-2026. Pas d’amelioration parametrique possible. Beta 0.225 = signal-driven. Pedagogiquement honnete.

Crypto-MultiCanal Lessons (2026-03-09)

CEILING REACHED at v17: Sharpe 0.486, CAGR 7.6%, MaxDD 16.8%

v18 REJECTED: 2-level progressive trailing (lock +3% at +6% gain). Sharpe 0.486 -> 0.232. - Rationale: BTC daily vol ~3-5%. A +6% move followed by a -3% pullback is extremely common. - The 2nd trail level triggers SL at entry+3%, converting winning positions to partial exits. - This cuts the big moves (which drive most CAGR) while barely reducing MaxDD. - Rule confirmed: trail to breakeven once is enough. Don’t add more levels on daily BTC.

Why v17 trail=3% works but trail_lock=6% breaks it: - v17: SL moves to entry+0.5% when price hits entry+3%. Most big BTC moves are >15%. The SL rarely triggers on winning trades; it mainly protects against reversal after small gain. - v18: SL jumps to entry+3% at entry+6%. This triggers on ~30% of trades, turning many “in progress” winners into sub-TP exits that lower the average win significantly.

Hard ceiling for this strategy 2020-2026: Sharpe ~0.486, beta=0.053, alpha=0.012. The signal is real (alpha positive, very low beta) but BTC bull market dominates returns. No further improvements found after 18 iterations. Signal quality is maxed out.

EMA-Cross-Index Lessons (2026-03-08)

v2.0 changes: EMA 20/60 + cooldown 3j

Grid search finding (25 combos, fast 8-20, slow 30-100): - EMA 20/60 retained: IS/OOS robustness = 1.55 (best), OOS Sharpe=1.325 (2010-2015) - slow=60 captures quarterly trends, less reactive to short corrections than slow=50 - All tested params have OOS > IS: 2015-2026 is LESS favorable than 2010-2015 for EMA (quasi-uninterrupted bull market, fewer real directional trends)

What was REJECTED and why: - Triple EMA (8/21/55): Sharpe -10% vs dual. Entry too restrictive, exit too early (fast<medium). On bull 2015-2026, every day of delayed entry costs. - Volume filter: Degrades Sharpe -5% to -19%. SPY is hyper-liquid, volume not directional signal for ETFs (volume follows price, not leads). Valid for individual stocks, not index ETFs. - Trailing stop 3%: Best IS Sharpe (+11.8%) but 68 trades vs 19 expected. Changes strategy nature from trend-following to micro-trading. NOT aligned with pedagogical objective. - Trailing stop 5-8%: Degrades Sharpe. SPY daily vol ~1% means 8% = 8 days, cuts healthy positions. - QQQ addition: Rolling correlation 0.92-0.95 with SPY. Signals nearly simultaneous. CAGR increases but Sharpe decreases (-5.4%). Not true diversification on 2015-2026. - Cooldown > 5j: Misses legitimate re-entries after short corrections. Optimal = 3j.

Key metric: beta=0.41 throughout — confirms signal-driven, not beta loading.

yfinance vs QC Sharpe discrepancy (0.765 yfinance vs 0.384 QC): - yfinance uses Adj Close (dividends included, ~1.5% CAGR bonus) - QC uses raw prices, includes transaction costs - ~2x discrepancy is normal and consistent across all EMA strategies tested - Relative improvement should transfer (IS/OOS robustness confirmed)

DualMomentum Lessons (iter2 - 2026-03-09)

MaxDD -33.6% est structurel: la latence du signal mensuel, pas le refuge

Iter2 a teste SHY comme refuge (vs BND v1.0). Resultat QC: REVERT (Sharpe 0.324 < 0.350, MaxDD identique).

Lecon cle: Le MaxDD de -33.6% vient du COVID Mars 2020, pas de la crise des taux 2022. En Mars 2020, la strategie etait investie en SPY (signal mensuel positif). Le refuge importe peu car la strategie n’etait PAS en mode defensif pendant le crash.

Discordance yfinance vs QC: yfinance montrait SHY ameliorant le MaxDD (simulation mensuelle). QC utilise les prix bruts day-by-day: le MaxDD intramonth (Mars 2020: -34% en 3 semaines) est capture fidelement. La simulation mensuelle lisse ces crashes soudains et les sous-estime.

Pour reduire le MaxDD de DualMomentum, il faut: - Un signal plus rapide (hebdo) - mais augmente le bruit - Un stop-loss intramonth - change la nature de la strategie - Un filtre VIX (VIX > seuil -> defensif) - possible mais complexe Aucune de ces options ne respecte le principe “1 param a la fois” facilement.

Plafond Sharpe 2015-2026: 0.350 est honnete. Bull market quasi-uninterrompu = peu de periodes de tendance negative durable. Dual Momentum brille sur periodes 1970-2010.

AllWeather Lessons

Synthese partielle : les lignes qui suivent reprennent les lecons porteuses (TLT/XLP/ drift/SMA) ; le detail historique complet – dont le rejet de DBC (contango : Sharpe 0.691 avec vs 0.817 sans) et la correlation TIP/IEF (double exposition bonds intermediaires) – reste dans le blob historique du fichier detracke par #13739 : git show d46d3f5259d^:.claude/agent-memory/qc-strategy-improver/allweather-lessons.md

Iter 4 (2026-03-08): v4.0 = TLT 0%, IEF 40%, GLD 20%, XLP 10%. Research H5 confirme TLT monotonement negatif. Vol targeting (H7) rejete. TIP (H6) marginal. Sharpe v4.0 = 0.520 (vs 0.482 v3.0).

Iter 5 (2026-03-09): v5.0 = IEF 30%, GLD 30%. CONFIRMED IMPROVED: Sharpe 0.520 -> 0.602. Weight grid search (40+ combos): shifting IEF->GLD is the last remaining improvement. - IEF CAGR 2015-2026: only 1.47% (rate hike drag). GLD: 13.24% with corr~0 to SPY. - Beta: 0.326 -> 0.336 (marginal +0.010). Alpha: 0.009 -> 0.017 (genuine improvement). - SPY unchanged at 30% confirms no beta loading. - yfinance improvement transfered to QC: Sharpe delta was +0.048 in simulation, +0.082 on QC. CEILING NOW REACHED for AllWeather. No further angles remain.

Bug QC critique: update_file_contents requiert name pas fileName dans le model.

VIX-TermStructure Lessons (v5.x - 2026-03-09 FINAL)

v5.0: SHY 70% + stop 7% - Sharpe -0.10 (REVERTED)

  • Too diluted: SVXY at 30% of 30% position = ~13.5% effective exposure
  • Cash drag + SHY yield not compensating for vol premium dilution

v5.1: position_size 0.45 -> 0.25 - Sharpe -0.125, MaxDD 21.5% (REVERTED)

  • MaxDD improved dramatically (35.2% -> 21.5%)
  • BUT Sharpe degraded (-0.176 vs v4.1)
  • ROOT CAUSE: With 25% position, portfolio earns 2.17% CAGR vs risk-free ~2-3% Cash drag makes Sharpe go negative. The vol premium is too small to overcome the Sharpe penalty of holding 75% idle cash at 0% in a high-rate environment.

CRITICAL LESSON: Position sizing for low-CAGR strategies

For strategies that generate modest CAGR (2-5%), reducing position size proportionally reduces CAGR while the risk-free hurdle stays fixed. This tanks the Sharpe ratio even if Sharpe(signal) is maintained. The only way to reduce MaxDD without hurting Sharpe is to find a TRUE uncorrelated asset for the idle capital (not T-bills, not SPY). For SVXY: no such asset exists that is both truly uncorrelated AND has positive expected return in a rising-rate environment.

HARD CEILING: v4.1 = Sharpe 0.051 is the honest ceiling

Every iteration from v2.0 to v5.1 (11 variants) has failed to beat v4.1. The strategy is structurally limited post-2018 VIXplosion (SVXY -0.5x). STOP iterating. Accept Sharpe 0.051 as the pedagogical result.

Trend-Following Lessons (2026-03-09) - CEILING REACHED

Cloud ID: 28797562 | Current best: v2 Sharpe 0.212, CAGR 7.3%, MaxDD 40.9%

Attempts to reduce MaxDD 40.9%: all failed

v3 (ATR 1.5 + SMA200 filter): Catastrophic. Sharpe 0.011, MaxDD 62.8%, CAGR -0.55%. - Root cause: MaximumDrawdownPercentPortfolio(0.15) liquidated ALL positions during COVID 2020. This is the anti-pattern: portfolio-level stops in multi-stock strategies = catastrophic liquidations. - The SMA200 filter ALSO contributed: blocked all entries during 2020 corrections, preventing recovery.

v3b (ATR 1.5 only, no SMA200): Sharpe 0.151, MaxDD 54.1%, CAGR 4.3%. - ATR 1.5 causes MORE whipsaw than ATR 2.0. Tighter stop = more stop-outs + worse re-entries. - The strategy has very restrictive entry conditions (6 gates: EMA trend 210/250, Bollinger, MACD, RSI, ADX at max, OBV). Each exit at ATR 1.5 is often a good trade cut prematurely. - MaxDD got WORSE (-54.1% vs -40.9%) because the strategy is now making more, smaller losing trades that compound into a deeper drawdown.

Root cause of MaxDD 40.9% in v2: The strategy concentrates in high-momentum stocks during market peaks. COVID 2020 crash hit exactly when the strategy was most invested. MaxDD is structural for this type of high-conviction momentum strategy. Cannot be reduced without fundamentally changing the entry/exit logic.

LESSONS: 1. MaximumDrawdownPercentPortfolio = NEVER use on multi-stock strategies. It liquidates everything simultaneously during a crash, locking in losses and missing the recovery. 2. ATR trailing stop: for multi-indicator momentum strategies with very restrictive entries, a tighter stop increases net losses (more whipsaws > fewer large losses). 3. SMA200 regime filter: useful for simpler strategies. For strategies with 6+ entry gates, the entry filters themselves provide sufficient regime control. 4. v2 is the honest ceiling: ATR 2.0, 10% per-security drawdown, 200-stock universe.

VIX-TermStructure Lessons (v4.x iteration)

What worked

  • VIX3M/VIX contango ratio as primary entry signal (not just VIX level)
  • Start date 2015 (captures pre-VIXplosion bull run)
  • VIX < SMA10 (not double-SMA) as regime filter - simpler is better
  • Backwardation exit at ratio < 1.02 (exit before crisis deepens)

What failed

  • Double SMA (vix < sma5 < sma20): too restrictive, missed many valid entries
  • VIX < 18 threshold: too few entries, Sharpe dropped to -0.23
  • Dynamic sizing by contango depth: increased MaxDD without Sharpe gain
  • Tighter 8% stop with smaller position: net negative

Key insight: fallback when VIX3M unavailable

When VIX3M price is 0, set contango_ratio = 1.0 (neutral, no entry). DO NOT use VIX * 1.0 as fallback - that makes ratio always = 1.0, which never triggers the 1.05 entry condition. This was a bug in v4.0 that prevented any entries when VIX3M data was missing.

VIX term structure fundamentals

  • VIX3M/VIX > 1.05: steep contango -> ~5% roll yield/month -> SVXY profitable
  • VIX3M/VIX < 1.0: backwardation -> immediate danger, exit required
  • Post-2018 VIXplosion: SVXY changed from -1x to -0.5x, halving premium
  • MaxDD 35% is unavoidable due to tail events (VIXplosion 2018, COVID 2020)
  • Alpha near zero (-0.016) confirms the signal is pure vol premium, not beta

TurnOfMonth Lessons (iter 3 - 2026-03-05)

The 2015-2026 ceiling: Sharpe 0.128 is honest

6 iterations all degraded from v2.0 baseline (0.128). Key findings: - SPY+QQQ is mandatory: QQQ outperformed SPY by +200% in 2015-2026 (tech bull). Dropping QQQ drops Sharpe to -0.026 immediately. - Momentum filter hurts ToM: The 21-day momentum is anti-correlated with ToM alpha. Good ToM rebounds often come after negative momentum periods. - Window 4/3 vs 4/4: No improvement in practice despite research showing it better. The one extra BOM day in v2.0 captures some genuine alpha. - Stop-loss 4%: Sharpe drops to 0.096, MaxDD only drops by 2.5%. The stop cuts too many cycles that would have recovered. Not worth the trade-off. - IWM addition: Dilutes QQQ’s alpha in 2015-2026.

Why the ceiling is structural, not an implementation issue

Research (research.ipynb) shows Sharpe 0.36 on 2000-2025 because the ToM effect is strongest during bear markets (institutional forced rebalancing at month-end). In 2015-2026 (pure bull), every trading day has similar returns, so the ToM premium is minimal. This is a period-dependent signal, not a broken strategy.

QC-specific bug discovered: partial month tracking at warmup exit

When warmup exits mid-month, trading_days_in_month contains only partial month. If you use this to set prior_month_td_count, next month’s remaining days estimate is wrong. Fix: add month_is_complete flag that only becomes True when a new month starts from day 1. Only update prior_month_td_count from complete months.

ForexCarry Lessons (iter 3 - 2026-03-05)

Structural Sharpe ceiling: -0.654 is honest for G10 FX momentum 2018-2026

8 backtest variants tested (v4.0 through v4.6). None beat v3.2. Key learnings:

  1. Signal inversion for USD/XXX pairs is CRITICAL: The v3.2 trick inverts USDJPY/USDCAD momentum to create unified “foreign currency vs USD” cross-sectional ranking. Breaking this (v4.3) caused -35% net profit. NEVER remove the inversion.

  2. Vol filter “skip rebalance” changes nothing: Reduces risk AND return proportionally. Sharpe moves by <0.1. Menkhoff’s finding is real but vol filter cannot overcome the fundamental low-CAGR / high-risk-free hurdle problem.

  3. Vol filter “liquidate on spike” is worse: Causes whipsaw exits. Tested in v4.0-v4.1.

  4. Adding complexity always hurt: SMA200, trailing stops, entry stops, strict momentum gates all degraded from v3.2. Monthly rebalance with minimal filtering is best.

  5. Pair selection: the original 4 are well-calibrated: EUR/AUD/JPY/CAD. USDCHF or NZDUSD substitutions all degraded. USDCAD (CAD commodity momentum) contributes.

  6. Root cause of negative Sharpe is structural: Risk-free rate avg 2018-2026 ~2.5%, peaks 5.5% in 2023-2024. Strategy earns only ~0.8% CAGR from FX momentum. This is academically documented (Menkhoff 2012: G10 FX momentum weakened post-2008).

  7. The strategy IS profitable (+6.7% cumulative): It’s just below T-bills. This is pedagogically honest and valuable.

Best backtest: 5316dcaa0543b78ec9b78996f65b8248 (v3.2, Sharpe -0.654)

RiskParity (CEILING REACHED - 2026-03-09)

Cloud ID: 28692653 | Issue: #35

Final result: Sharpe 0.399, CAGR 7.82%, MaxDD -20.9% (baseline extended 2015-2026)

All hypotheses tested and rejected: - H5 IEF (replace TLT): QC backtest Sharpe 0.330 < 0.399. TLT superior on 2015-2026 because bull bond market 2015-2020 (+40% TLT) outweighs 2020-2023 rate hike losses. IEF has less CAGR. - H6 Vol Targeting 10%: rejected. Cash during low-vol = anti-pattern on bull market. No leverage. - H7 VIX filter >25: rejected. <15% of time in stress regime. Cash time destroys relative return.

Why IEF degrades on QC but seemed better in yfinance simulation: - yfinance Adj Close includes bond ETF dividends (~2-3% yield/year). On 11 years this creates a significant bias that makes IEF look better in simulation than it is in live trading. - QC uses raw prices, so the dividend yield advantage of TLT over IEF shows up correctly. - Lesson: for bond ETFs, yfinance simulations are especially unreliable vs QC cloud.

Key QC implementation note: - Used self.STD(symbol, 60, Resolution.DAILY) which triggers a type-hint warning (“RiskParity has no attribute STD”) but compiles fine - this is a static analysis false positive - Vol = std_indicator.current.value / current_price (relative vol from price-level STD) - 656 trades: ~130 monthly + ~526 drift-triggered. Fees $828 total = negligible.

DualMomentum (NEW - 2026-03-05)

Cloud ID: 28692516 | Issue: #35

Result: Sharpe 0.34, CAGR 8.9%, MaxDD -33.6% (v1.0, BND refuge)

Why 0.34 not 0.7-1.0 (as in literature): - Literature backtests start 1974/1988, include 1970s stagflation, dot-com crash, 2008 where the defensive exit to bonds earns huge alpha - 2015-2026 is a quasi-uninterrupted bull market where absolute momentum stays positive most of the time (always in SPY) and COVID was only 1 month exit signal - The Sharpe of 0.34 is honest and pedagogically valid

Design choices: - BND better than SHY as safe asset on 2015-2026 (tested both) - 12m lookback is optimal (Antonacci’s canonical choice, robust 9-18m) - 0% absolute threshold is standard (proxy for T-bills) - 37 trades in 11 years = monthly, low turnover (0.92%)

MaxDD 33.6% explanation: COVID crash March 2020. Monthly signal cannot react in time. Same drawdown with both BND and SHY. This is structural, not fixable without intra-month stops (which would hurt Sharpe due to whipsaws).

FuturesTrend Lessons (iter 3 - 2026-03-05)

v3.1 result: Sharpe 0.280 -> 0.301 (+7.5%), IMPROVED

Key changes that worked: - XLE replaces TLT: TLT lost ~40% in 2020-2023 (Fed rate hikes). XLE provides genuine trend diversification via energy commodity cycles. - SMA50 filter (light): Only enter if price > SMA50. Blocks entries into assets in structural downtrends without cutting valid signals. SMA50 preserves signal frequency much better than SMA100. - Fixed 33% weight maintained: ATR risk-parity sizing was tested (v3.0) and rejected.

What was NOT implemented (tested but rejected): - ATR position sizing (v3.0, Sharpe 0.209): DEGRADED. Donchian breakout already signals a volatility expansion - ATR sizing then gives small positions at high-vol breakouts, which is exactly backwards. ATR sizing is better suited to mean-reversion entries (buy the dip = low vol). - SMA100 filter (v3.0): Too restrictive. On a 6-ETF universe, blocks too many valid entries. The strategy ends up with fewer, smaller positions.

Why the improvement is genuine (not beta loading): - Beta stable at 0.225 (was ~0.2 in v2.3) - Alpha positive at 0.011 (small but positive signal) - Net Profit improved: +78.1% -> +87.8%

Best backtest: 64aab8780ae180d72c9fb532adb17ec3 (v3.1, Sharpe 0.301)

MeanReversion Lessons (iter 3 - 2026-03-05)

v4.0 result: Sharpe 0.294 -> 0.365 (+24%), FUNCTIONAL

Key changes that worked: - Stop-loss -8%: Cuts real breakdowns (XLE 2020, XLB 2022). MaxDD 16.5% -> 14.7%. Does NOT truncate normal reversions (which typically recover in 15 days). - 4 positions (vs 3): More signal capture. 453 -> 724 trades. Alpha 0.007 -> 0.009. - RSI exit 60 (vs 55): Lets winners run slightly more. Marginal improvement. - Start 2015 (vs 2018): Dec 2018 correction adds regime diversity. Net profit +36.5%.

What was NOT implemented (tested but rejected): - RSI(7) instead of RSI(14): Similar edge, more noise. RSI(14) is more stable. - Dynamic position sizing (oversold-weighted): More complex, not more robust.

Why the improvement is genuine (not beta loading): - Beta STABLE at 0.22 (no market exposure added) - Alpha INCREASED from 0.007 to 0.009 (signal quality improved) - Win Rate 59% (was anomalously >100% in v3.2, now correctly computed)

Best backtest: 7f998778660bb539353df6dad180e6ba (v4.0, Sharpe 0.365)

MomentumStrategy Lessons (iter 3 - 2026-03-05)

v3.0 result: Sharpe 0.411 -> 0.459 (+11.7%), IMPROVED

Key changes that worked: - Vol-adjusted momentum score (raw_momentum / 3m_vol): Ranks sectors by risk-adjusted return. High-vol sectors must have proportionally higher raw return. Reduces whipsaws from erratic breakouts. Source: Asness et al. (2013). - Skip-month (12m-1m): Measure momentum from day 252 to day 21, skipping last month. Avoids short-term reversal contamination (Jegadeesh 1990). Standard in academic momentum literature - important to apply this. - Top 4 instead of 3: More signal capture (514 orders vs fewer before). Win rate 74%, better diversification without diluting momentum premium. - Dual SMA regime filter (SMA200 AND SMA20): Risk-off only when SPY below BOTH. Reduces false risk-off signals in volatile-but-uptrending markets. The tighter condition keeps more risk-on exposure during consolidations.

Why the improvement is genuine (not beta loading): - Beta 0.813 (reasonable for a sector strategy - not changed artificially) - Alpha 0.001 (positive, confirms real signal quality improvement) - Win rate 74% reflects better entry selection from vol-adj scoring

Best backtest: 4053f3333d5c7897a3f4600aea883aff (v3.0, Sharpe 0.459)

Key formula for vol-adjusted momentum

raw_momentum = (price_252d_ago_to_21d_ago) / past_price - 1  # skip-month
vol = daily_returns[-63:].std()  # 3m realized vol
score = raw_momentum / vol  # vol-adjusted score for ranking
# Still apply raw_momentum > 0 filter (absolute momentum gate)

AllWeather Lessons (iter 3 - 2026-03-05)

v3.0 result: Sharpe 0.365 -> 0.482 (+32%), TARGET ACHIEVED (>0.4)

Key change that worked: - Reduce TLT from 35% to 20%: TLT lost ~40% in 2020-2023 (Fed rate hike cycle). Reducing duration risk was the correct fix. IEF (20%) is less rate-sensitive. - Add XLP 10%: Consumer Staples: dividends ~3%/year, low beta, uncorrelated with rates. Outperformed TLT in rate-hike regime. Not beta loading - genuine diversification. - Tighter drift 3% (was 5%): More responsive rebalancing, captures drift earlier.

Why this is NOT beta loading (alpha check): - Alpha went from -0.001 (v1.0) to +0.009 (v3.0): genuine positive signal confirmed - Beta 0.304 (vs 0.238 v1.0): XLP is equity but has its own carry (dividends ~3%/year). In a flat market, XLP still earns dividends. SPY allocation unchanged at 30%. - The improvement disappears if TLT (bonds) had worked normally in 2022. This is a regime-aware allocation change, not passive market exposure.

What was NOT implemented (SMA overlay rejected from v2.x backtests): - SMA200 50% reduction: Sharpe 0.858 standalone BUT only 0.264 in QC. Standalone overestimates overlay benefit; trust QC results. - SMA25% (v2.2, Sharpe 0.325): worse than no-SMA v2.1 (0.365). Lesson: any SMA overlay adds friction without Sharpe gain for static AllWeather.

Key insight: standalone Sharpe != QC Sharpe for event-driven overlay strategies. Simple static allocation + tight drift rebalancing (3%) outperforms SMA overlays for low-turnover portfolios (fewer commissions, no cash drag).

Best backtest: eae380495f3762e0313c996797153551 (v3.0, Sharpe 0.482)

FamaFrench Lessons (iter 3 - 2026-03-05)

v3.0 result: Sharpe 0.471 -> 0.540 (+14.6%), MaxDD 33.7% -> 24.2%, HEALTHY

Key changes that worked: - Risk-adjusted momentum score (12m return / 63d realized vol): Cleaner factor ranking - penalizes high-vol outliers. Ref: Barroso & Santa-Clara (2015). The vol-adj scoring is the SAME pattern that worked for MomentumStrategy v3.0. - Dynamic top_n (all factors with positive risk-adj score, not fixed 3): Adapts concentration to signal quality. Fewer factors held in choppy periods. - Per-position stop-loss -12%: Main driver of MaxDD reduction (33.7% -> 24.2%). Does NOT cut normal reversions (factor ETFs typically recover in <21 days). - Skip-month (252d-21d): Avoids 1-month reversal bias - standard academic pattern.

What was NOT changed (confirmed working): - SMA200 regime filter: effective at avoiding bear market exposure - USMV risk-off (no TLT): 2022 lesson preserved - Monthly rebalance: appropriate for factor rotation

Why the improvement is genuine (not beta loading): - Alpha 0.009: positive signal confirmed - Beta 0.721: reasonable diversification from SPY - Win rate 81%, Sortino 0.557: consistent factor edge

Pattern: vol-adjusted momentum is universally better than raw momentum

This is now confirmed in BOTH MomentumStrategy (v3.0: +11.7% Sharpe) AND FamaFrench (v3.0: +14.6% Sharpe). Apply this pattern to any factor/momentum strategy as a default improvement hypothesis.

Best backtest: 8f1eb90c99ba819406a85bed4d3fa5d0 (v3.0, Sharpe 0.540)

OptionsIncome Lessons (iter 3 - 2026-03-05)

v6.0 result: Sharpe 0.747 (1yr) -> 0.791 (2yr), MaxDD 8.3% -> 7.5%, IMPROVED

Key changes that worked: - Period extension 2023-2024 (vs 2024 only): More robust Sharpe, tests 2 regimes - VIX band 15-35 (vs VIX > 15 alone): Cap at 35 avoids gamma risk in extreme vol - Profit target 50%: Buy back call when it’s lost 50% of value. Frees capital sooner for re-entry, reduces assignment risk. Standard TastyTrade practice. - Defensive exit -3%/day: Close call if SPY drops >3% in a session. Reduces directional exposure on big down days. Beta 0.825 -> 0.646.

Key metrics after improvement

Win rate improved 50% -> 57%. Beta reduced 0.825 -> 0.646. PSR 63% -> 77%. 96 trades over 2 years (vs 34 over 1 year = higher activity per year).

What makes this genuine (not beta loading)

  • Beta REDUCED from 0.825 -> 0.646 (less market exposure, not more)
  • Alpha improved: -0.031 -> -0.023
  • Win rate improvement came from profit target mechanism (signal-based)
  • Defensive exit reduces directional bet, doesn’t add passive exposure

Covered call fundamentals

  • Alpha is structurally negative for covered calls: you cap your upside vs SPY. But the vol risk premium (IV > RV) generates consistent income.
  • VIX band (15-35) is critical: below 15 = not enough premium to justify capping your upside; above 35 = gap risk overwhelms the premium collected.
  • Profit target at 50% is the TastyTrade rule: 50% of max profit captured in ~1/3 of the time => better Sharpe than holding to expiry.
  • 2 years minimum backtest for options to see enough trade cycles (96 trades is robust, 34 is borderline for statistical significance).

Best backtest: b29ad63ddb9d0a758fb472cf32f11534 (v6.0, Sharpe 0.791, 2023-2024)

SectorMomentum Lessons (iter 3 - 2026-03-05)

Sharpe ceiling 0.554 -> 0.555 (marginal), best backtest: 2101944df98f6edbd455cf3fb1caaf4e (v3.2)

Key findings: - Composite lookback > 12m simple FOR THIS STRATEGY (unlike other strategies) Research: composite 0.409 vs 12m simple 0.301 on 2005-2025 data - TLT+GLD best-of IS the right defensive on 2015-2026. Attempts to replace TLT: - SHY+GLD+TIP: Beta jumped 0.145->0.311 (SHY near-zero momentum forces more SPY) - GLD-only: MaxDD 27.8%, Sharpe 0.413 (research showed this on 2005-2025, wrong for 2015-2026) - Only genuine improvement: Daily SMA200 exit (v3.2). If holding SPY and SMA200 breaks intra-month, exit immediately. Entry stays monthly (avoids whipsaw). Result: Beta 0.145->0.098. - This is a structural ceiling for this period. Strategy is well-calibrated.

Gaussian-Direction-Classifier Lessons (2026-03-28) - iter1

Cloud ID: 29398513 | v2.0: Sharpe 0.761, Beta 0.540, CAGR 23.1%, MaxDD 25.6%

Key finding: SMA200 regime filter is the most powerful single improvement for HIGH-BETA stock-picking strategies

v1.0 had Beta 1.133 - meaning this GaussianNB stock picker was essentially a leveraged SPY with a thin alpha layer. Adding if SPY < SMA200: go to cash transformed the risk profile dramatically: - Beta: 1.133 -> 0.540 (halved) - MaxDD: 36.8% -> 25.6% (-11pp) - Treynor: 0.177 -> 0.283 (+60% - better return per unit of systematic risk) - Alpha: 0.112 -> 0.111 (essentially unchanged - signal quality preserved)

The tradeoff: Sharpe 0.864 -> 0.761 because CAGR dropped from 29.1% to 23.1%. The missing ~6% comes from the periods when the strategy was in cash due to the regime filter. But those periods had lower signal reliability anyway. An honest assessment: the v2.0 is pedagogically superior because it demonstrates genuine alpha (beta 0.540, not 1.133).

Why this is different from AllWeather SMA200 (Regle #8): AllWeather SMA200 overlay FAILED (0.858->0.264) because static portfolio allocation was disrupted. Gaussian NB is a STOCK PICKER - being in cash during bear markets is natural and improves the signal. The difference: static portfolios lose rebalancing benefits when one leg is removed. Dynamic strategies benefit from regime awareness.

Remaining hypotheses for future iterations: 1. 5-day labels (instead of 1-day): Less noise but longer holding period 2. RSI/BB z-score features instead of raw returns 3. Retrain interval 10d vs 21d (faster regime adaptation) 4. Sector ETFs universe (XLK, XLF, etc.) instead of individual stocks

Critical Rules (from MEMORY in user system prompt)

  • SPY Parking = FORBIDDEN (beta loading disguised as alpha)
  • read_file BEFORE update_file_contents (collaboration lock)
  • 1 backtest at a time on QC node
  • Options = Resolution.MINUTE (chain empty otherwise)
  • Start date matters: 2015 captures more regime diversity than 2018
Retour au sommet