Skip to content

View translation

CrossAssetDefensiveDualMomentumEtfRotation

Hypotheses

Cross-Asset Defensive Dual-Momentum Regime Rotation on US ETFs (SPY/QQQ vs TLT/IEF/GLD, Monthly Rebalance on Daily Bars, Absolute-Momentum Risk-Off Switch, Decades of History) — Non-Momentum-Confluence Family, Fills the USEQ Venue Gap on the Cleanest Dataset in the Factory

Hypotheses

A LONG-ONLY, MULTI-ASSET regime-rotation strategy across a tiny liquid US-ETF universe {SPY, QQQ, TLT, IEF, GLD} on 1-DAY bars, rebalanced monthly. This is a DELIBERATE move OFF the dying dual-timeframe momentum-confluence perp family (the collided BTC-HL port died fee_edge, and its LTC/XRP/LINK/SOL/HL-SOL/DOT/BNB clones are a graveyard) and ON TO the ONE equity family that has shown LIFE in this factory: regime rotation (UsRiskOnRiskOffRegimeRotationSpyTltHysteresis ADVANCED through backtest_review to optimizing on 2026-09-06, unlike the seven equity textbook-anomaly deaths in L74). It is categorically distinct from the SectorDispersionGatedCrossSectionalMomentumLS death (that ranked CORRELATED equity sectors long-short and lost; this rotates across UNCORRELATED asset classes with a defensive absolute-momentum kill-switch). The edge is not intraday alpha timing — it is (a) harvesting the long-run equity risk premium while (b) using absolute (time-series) momentum to step OUT of equities into bonds/gold/cash during sustained downtrends, cutting the deep bear drawdowns that halve buy-and-hold Sharpe. Chosen because USEQ 1-DAY bars carry DECADES of split-adjusted history — the most robustly backtestable venue in the whole factory — and the equity venue is under-represented. Monthly cadence (~12 rebalances/yr) makes fees a rounding error even before USEQ's ~0% commissions.

Hypotheses

Iteration 2 fixes the reported Layer-2 failure only. The TypeError ('other' expected InstrumentId, got str) came from the pending-target sentinel: after a rebalance self._pending_target held an InstrumentId, and the guard `self._pending_target != "none"` compared a Cython InstrumentId against a str, which raises rather than returning False. The sentinel is now a plain boolean flag `_pending` with `_pending_target` holding only an InstrumentId or None. For the same reason all id comparisons (`held == target`, `iid == target`, `iid == self.instrument_id`) now go through a None-safe `_same_id()` string comparison, so an InstrumentId is never compared with None or a str. Signal logic, universe split, monthly calendar anchor, sizing and execution sequencing are unchanged from the previous iteration (Layer 1 / 1.5 passed and imports are untouched).

Hypotheses

Three unwaivable HARD validity failures (overfit walk-forward, negative OOS Sharpe -0.718, PBO 0.635), confirmed by DSR 0.0346 with the selected Sharpe sitting below the best-of-N noise bar and programme FDR rejection. Verified on the optimized detail file: the top 5 of 98 trades supply 78.5% of PnL (top 10 = 110%, so the remaining 88 are net negative), and the strategy underperforms its own equal-weight benchmark ($543,635 vs $848,616) with information_ratio -0.090 — the defensive kill-switch costs more return than the drawdown it avoids, refuting the hypothesis's central claim. Not a fee problem ($0 commissions, 2.23% per trade vs a 0.05% floor) and not an iteration target (both parameters are cliffs, the heatmap is flat noise, and the threshold parameter is inert). Overfit, so abandon rather than revise. Sixth USEQ monthly ETF regime-rotation instance to die this way; the structural cause is measured — ~12 rebalances/yr yields 56-98 lifetime decisions of which a handful carry all PnL, so no 3-parameter rule is estimable on this sample.

Implementation

Long-only monthly dual-momentum regime rotation over a five-ETF USEQ universe on 1-DAY bars. Each session every ETF is scored by a blend of its 126-session and 63-session total return. On the first evaluated session of each calendar month (anchored to the bar's own timestamp, restart-safe) relative momentum picks the stronger of SPY/QQQ; that winner is held only if its absolute momentum clears abs_momentum_threshold, otherwise the sleeve rotates into the strongest of TLT/IEF/GLD that clears the same threshold, otherwise it goes to cash. One 95%-of-equity whole-share sleeve at a time, all five legs driven manually with settle-aware sequencing (flatten first, enter on a later clean bar).

Verification Results

If the base relies on its own min_bars_required for the extra-leg warmup gate, ensure all five legs have >= lookback_days+1 bars before the first rebalance fires (the per-leg _score None-guard already enforces this, so this is a belt-and-suspenders note).

Verification Results

min_bars_required() overrides a framework method (flagged by static analysis). This appears deliberate (returns lookback_days+2 = 128 to gate warmup) and the rebalance/score logic independently guards against short buffers (_score returns None when len(closes) < horizon+1), so no look-ahead or premature-trade risk. Confirm the base class honors the override for warmup gating rather than using its own value.

Verification Results

Regime rotation is inherently low-turnover: the sandbox produced only 17 trades over ~1650 days, so even over decades of USEQ 1-DAY history the strategy will likely generate on the order of ~60-120 trades. That is enough to run but thin for distinguishing edge from noise (cf. L16). The mechanism's value is drawdown reduction, not trade-level alpha, so headline avg_trade_return_pct (5.28%) is not the relevant statistic; the analyst should judge on risk-adjusted full-history metrics (Sharpe/Calmar/max_dd) and confirm the absolute-momentum kill-switch actually cuts bear drawdowns vs a SPY buy-and-hold benchmark. Sandbox Sharpe 0.84 / Calmar 2.92 / beta 0.55 are consistent with the defensive-rotation thesis on a truncated window.

Backtest Review

Numerically viable and fee-immune on the cleanest venue: metrics_reliable=true, 70 trades, avg_trade_return_pct 2.87% (>>USEQ 0.05% floor), PF 2.78, positive Sharpe 0.41, total_commission $0 — this is categorically NOT a fee-edge death.

Backtest Review

Trades implement the hypothesis: 70 long trades, 0 shorts, long-only rotation across uncorrelated asset classes with the absolute-momentum kill-switch present and firing (equity goes flat to cash in sustained downtrends, e.g. 2018-12/2019-01 and 2022-07..2022-11 plateaus in the curve).

Backtest Review

Core defensive thesis is borne out, not just asserted: in 2008 the sleeve drew down ~$125k->$78k (~37%) vs SPY buy-and-hold's ~55% — the kill-switch measurably cut the deepest bear drawdown, exactly what QA asked to confirm.

Backtest Review

This is the regime-rotation equity family that has shown LIFE in the factory (the SPY/TLT hysteresis sibling advanced to optimizing), and is categorically distinct from the SectorDispersion cross-sectional death (uncorrelated asset classes + defensive switch, not correlated-sector long/short).

Backtest Review

Positive alpha (+1.1%/yr) and low turnover (~3.3 switches/yr) make it robust to costs; recovery_factor 13.2, profit_factor 2.78.

Backtest Review

Low headline risk-adjusted metrics: Sharpe 0.41 / Sortino 0.43 / Calmar 0.26, and information_ratio -0.09 vs the equal-weight basket — the active edge over a naive multi-asset hold is thin.

Backtest Review

Stressed-vol tercile still negative (return -20%, Sharpe -0.02, DD 56.6%) and covid_crash_2020 window -21% — the monthly/6-month cadence whipsaws through fast crashes; the switch protects slow bears (2008) better than fast ones.

Backtest Review

Effective sample is ~2005-2026 (strategy is flat until GLD accumulates lookback in 2004-11), so ~21 years / ~70 switches — runnable but thin for the walk-forward split; watch for overfitting on ~23 trades/window.

Backtest Review

end_unrealized_pct 31 of a 419% total return means part of the headline is an open end-of-test position — judge on realized/risk-adjusted, not the MTM headline.

Analysis

Fee-immune by construction: USEQ commissions $0, modeled impact only 1.42% of gross, per-trade return 2.23% of notional against a 0.05% floor — NOT a fee-edge death.

Analysis

Code implements the hypothesis faithfully: 98 long-only monthly rotations across SPY/QQQ/TLT/IEF/GLD, ~2-month holds, no shorts, end_unrealized_pct -0.39 (no MTM headline inflation).

Analysis

Holdout (2013-04 to 2018-04) positive at Sharpe 0.83 on 16 trades; profit factor 2.25 clears its soft floor.

Analysis

Three unwaivable HARD failures: is_overfitted=true (IS +0.528 vs OOS -0.718), average OOS Sharpe NEGATIVE (-0.718; all three windows non-positive: -0.087/-1.843/-0.224), PBO 0.635 > 0.50.

Analysis

DSR 0.0346 with selected Sharpe 0.416 BELOW the expected-max-by-luck bar of 0.7317 over 225 trials (44 effective); programme FDR rejects (p=0.965, keeps 4 of 291).

Analysis

Both real parameters are cliffs; the 5x5 heatmap is a flat 0.41–0.63 noise field and the chosen cell is not the peak. abs_momentum_threshold is inert across every variation — the optimizer turned the hypothesis's defining kill-switch off.

Analysis

Five trades are the result: top 5 of 98 = 78.5% of $444k PnL, top 10 = 110% (the other 88 net negative).

Analysis

It loses to its own benchmark: equal-weight buy-and-hold ends $848,616 vs strategy $543,635 from the same $100k; IR -0.090, alpha 1.17%/yr on beta 0.58.

Analysis

Breaches its pre-registered 20% DD cap at 33.4% (Calmar 0.44, 1065-day underwater); met 2 of 5 pre-registered predictions.

Analysis

Benjamini-Hochberg at q=0.10 over 291 programme candidates keeps 4. A candidate that does not survive here is not distinguishable from the programme's own noise, however good its individual statistics look.

Outcome Summary

CrossAssetDefensiveDualMomentumEtfRo-74c05e6aab

Outcome Summary

This strategy moved the factory off the dying perp momentum-confluence family onto USEQ's decades-deep daily ETF data, rotating one 100%-notional sleeve monthly between SPY/QQQ, a TLT/IEF/GLD defensive block, and cash via an absolute-momentum kill-switch. The initial backtest passed backtest_review with an 'optimize' verdict on genuinely fee-immune numbers (419% return, Sharpe 0.41, 2.87% per trade, zero commissions), and the reviewer confirmed the kill-switch measurably shrank the 2008 drawdown versus buy-and-hold. Optimization then exposed the edge as noise: overfit walk-forward with negative OOS Sharpe -0.718, PBO 0.635, deflated Sharpe 0.0346, flat cliff-ridden parameter surfaces, an inert kill-switch threshold, and total underperformance of its own equal-weight benchmark. After 2 iterations it was abandoned as the sixth USEQ monthly ETF regime-rotation instance to die with the same measured structural cause — too few lifetime decisions to estimate anything.

Outcome Summary

A ~12-rebalance-per-year rule yields only 56-98 lifetime decisions, a handful of which carry all the PnL, so a 3-parameter monthly rotation is not statistically estimable on this sample no matter how clean or long the underlying data is.

Outcome Summary

The analyst abandoned it after optimization on three unwaivable hard validity failures — overfit walk-forward, negative average OOS Sharpe, and PBO above 0.50 — reinforced by programme FDR rejection (p=0.965), both real parameters registering as sensitivity cliffs with an inert abs_momentum_threshold, and the strategy underperforming its own equal-weight benchmark ($543,635 vs $848,616, IR -0.090) with the top 5 of 98 trades supplying 78.5% of PnL.

Outcome Summary

A long-only monthly dual-momentum rotation on USEQ daily bars that held the stronger of SPY/QQQ while its blended absolute momentum was positive, and otherwise stepped into the strongest of TLT/IEF/GLD or to cash, aiming to harvest the equity risk premium while cutting deep bear drawdowns.

Outcome Summary

The initial backtest over 1993-2026 produced 419.2% total return, Sharpe 0.41, Calmar 0.26, profit factor 2.78 and 37.4% max drawdown on 70 long trades with avg_trade_return_pct 2.87% and $0 commissions; after optimization it showed 443.6% return, Sharpe 0.416, 33.4% max drawdown and 98 trades, but walk-forward OOS Sharpe was -0.718 (windows -0.087 / -1.843 / -0.224) with PBO 0.635 and deflated Sharpe 0.0346, while the 2013-2018 holdout was positive at Sharpe 0.83 on 16 trades.

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['min_notional', 'n_risk_extra', 'notional_pct', '_param_bounds', 'lookback_days', 'blend_short_frac', 'abs_momentum_threshold'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: Argument 'other' has incorrect type (expected nautilus_trader.model.identifiers.InstrumentId, got str) (bar timestamp: 1735697220000) - flat_ranging: TypeError: Argument 'other' has incorrect type (expected nautilus_trader.model.identifiers.InstrumentId, got str) (bar timestamp: 1735697220000) - volatility_spike: TypeError: Argument 'other' has incorrect type (expected nautilus_trader.model.identifiers.InstrumentId, got str) (bar timestamp: 1735697220000) - zero_volume: TypeError: Argument 'other' has incorrect type (expected nautilus_trader.model.identifiers.InstrumentId, got str) (bar timestamp: 1735697220000) - price_gap: TypeError: Argument 'other' has incorrect type (expected nautilus_trader.model.identifiers.InstrumentId, got str) (bar timestamp: 1735697220000)
Strategy report

Backtest and paper results are hypothetical. Trading involves risk of loss.