Skip to content

View translation

UsSectorEtfCrossSectionalMomentumDollarNeutralLS

Hypotheses

US Sector-ETF Cross-Sectional Momentum, Dollar-Neutral Long-Short with Absolute-Momentum Defensive Gate (USEQ, Daily Bars, 6-1 Ranking, Monthly Rebalance, Long Top-3 / Short Bottom-3 SPDR Sectors, 2-Parameter)

Hypotheses

ITERATION 2 — the reported failure was sandbox_timeout (Layer 3), so Layers 1, 1.5 and 2 already passed and the signal/structure code is left byte-for-byte intact except for the universe size and docs. I profiled the exact sandbox run (same config, same 2400-day XLK.USEQ-1-DAY window, py-spy on the live process) before changing anything, and the timeout is NOT per-bar work in this strategy: calculate_signal is O(legs) on a fixed-size deque, holds no growing list, and never re-scans history — every stack sample landed in fsspec's directory walk, never in strategy code. The wall clock is catalog file discovery: NautilusTrader's ParquetDataCatalog runs `glob(data/bar/**/*.parquet)` ONCE PER BAR TYPE (a full walk of the catalog's ~102,000 bar directories) plus one `instruments()` pass over the crypto_option/binary_option trees, all before a single bar reaches the engine. Twelve bar types therefore paid twelve of those walks. (Confirming it is a shared infra cost, not this mechanism: the same sandbox_timeout is currently hitting single-instrument, zero-extra-leg strategies on this host at the same rate.) The only lever a strategy has over that cost is the NUMBER of bar types, so I cut the universe from eleven sector ETFs to the nine original 1998 Select Sector SPDRs, dropping XLRE (2015) and XLC (2018) — a 25% cut in the fixed pre-run cost. It is also the right research call independently: XLC's 2018-06 inception was the binding constraint on the multi-leg alignment barrier, so removing it extends usable history from ~2018 to 1998 and gives the walk-forward and holdout windows far more independent months. The hypothesis is preserved exactly: still cross-sectional 6-1 momentum, still monthly rebalanced, still equal-dollar long top-3 / short bottom-3 (nine names leaves three middle sectors flat), still gated by the absolute-momentum defensive escape to cash, still two edge parameters (formation_days, skip_days) with every clamp declared in _param_bounds. Leverage 2.0 is genuinely consumed: gross = equity x gross_exposure x self.config.leverage in _rebalance. Nothing that passed an earlier layer was touched — same imports, same class, same calendar-anchored (never bar-counter) monthly schedule, same synchronized single-row price buffer, same continuous per-session z-score signal.

Hypotheses

Marginal/breakeven expectancy on a decisive 517-trade, 28-year sample: profit_factor 1.0296 (in the [0.85,1.10] drag band), avg_trade_return_pct 0.0844% (barely above the 0.05% USEQ floor), Sharpe 0.0736 with CI [-0.2866, 0.4494] straddling zero. USEQ commissions are $0 and impact is only 34.7% of gross, so this is a GROSS-marginal signal, not fee-fragility — the cross-sectional momentum spread across only 9 sector ETFs (top-3/bottom-3) is too narrow to constitute an edge, exactly QA's concern, borne out on full history. The short leg is the drag (win rate 0.345 vs long 0.60), it loses in the stressed regime (-12.3%) and craters in 2026 (-19.5%). This is the dollar-neutral long-short basket family (L45, 0/N) and the L75 USEQ signature (PF<1.2 on a near-frictionless venue = anomaly absent). A PF-1.03 signal cannot be lifted above breakeven by tuning 2 formation/skip parameters — not worth 2 hours of optimization. abandon_class=negative_expectancy.

Implementation

Monthly cross-sectional 6-1 momentum across the nine original Select Sector SPDR ETFs (XLK, XLB, XLE, XLF, XLI, XLP, XLU, XLV, XLY) on USEQ daily session bars. Each month, sectors are ranked on the cumulative return over the last `formation_days` sessions ending `skip_days` sessions ago; the book goes equal-dollar long the top 3 and short the bottom 3, so the two legs carry identical notional and the portfolio is dollar-neutral by construction. An absolute-momentum defensive gate takes the book fully flat for any month whose cross-sectional MEAN 6-1 momentum is <= 0 (the dual-momentum escape to cash), which is the drawdown control. Rebalancing is executed as netting whole-share delta orders with a $500 minimum, so names that stay in the same bucket are not churned. Gross notional = equity x gross_exposure x leverage (0.50 x 2.0 = 1.0x equity, i.e. 0.5x long + 0.5x short) under the declared Reg-T 2x margin.

Verification Results

No change required; the internal buffer gate correctly enforces warm-up. Left as informational.

Verification Results

min_bars_required() overrides the base framework method, returning 30 instead of the full formation+skip lookback. This is deliberate (the real warm-up is the strategy's own aligned-row buffer, which only fills after calculate_signal is first called) and documented, but it shadows the base warmup gate. Confirmed benign: _momentum() returns None until _rows reaches _need_rows (148), so no signal/trade fires before the buffer is genuinely full.

Verification Results

Acceptable as a research/perf call; analyst should note the 9-name cross-section when judging spread width.

Verification Results

Universe reduced from the 11 SPDR sectors implied by the hypothesis to the 9 original 1998 Select Sector SPDRs (XLRE/XLC dropped). This still satisfies the hypothesis intent (cross-sectional SPDR-sector momentum, long top-3/short bottom-3, 3 names flat) and materially extends usable history to 1998, but narrows the cross-section slightly, which reduces the extreme-decile spread the edge relies on.

Verification Results

Sandbox smoke test is decisively negative (total_return -18.6%, profit_factor 0.75, Sharpe -0.41, avg_trade_return_pct -0.90%, short-leg win rate 25% vs long 62%). USEQ is near-frictionless (commissions 0, impact only $560), so this is a GROSS-negative draw, not fee drag — the asymmetric leg win rates suggest the short (loser) leg is the drag, consistent with momentum-crash/short-squeeze dynamics on sector losers. The recent graveyard is 0/N on beta-/dollar-neutral long-short baskets (UseqResidualMomentumLargeCap abandoned, SolEth dollar-neutral abandoned; L45). The strategy is a faithful, correct implementation so it earns a full-history run and the optimizer, but the analyst should check whether the cross-sectional spread across only 9 sector ETFs (top-3/bottom-3, 3 flat) is wide enough to survive out-of-sample, and whether the short leg carries the losses. Full 1998-onward history over ~28 years of monthly rebalances is comfortably measurable.

Backtest Review

Faithful, correct dollar-neutral implementation with a genuine 28-year / 517-trade decisive sample (metrics_reliable: true)

Backtest Review

Absolute-momentum gate does cap drawdown at a moderate 25.6% for a levered L/S book

Backtest Review

profit_factor 1.0296 sits in the [0.85,1.10] fee/impact-drag band; on near-frictionless USEQ (commissions $0, impact 34.7% of gross) this is a gross-MARGINAL/breakeven signal, not fee drag — the cross-sectional spread across only 9 sector ETFs (top-3/bottom-3, 3 flat) is too narrow to be an edge

Backtest Review

avg_trade_return_pct 0.0844% barely clears the 0.05% USEQ floor and is far below the 0.10% prefer bar

Backtest Review

Sharpe 0.0736 with CI [-0.2866, 0.4494] straddling zero; Sortino 0.080; recovery_factor 0.38 — no risk-adjusted edge

Backtest Review

Short leg is the drag (short win rate 0.345 vs long 0.60) — QA's momentum-crash/short-squeeze concern borne out on full history; loses in the stressed regime (-12.3%) and collapses in 2026 (-19.5%)

Backtest Review

Matches L45 (dollar-/beta-neutral long-short baskets 0/N) and L75 (USEQ PF<1.2 means the anomaly itself is absent); same-day siblings UseqResidualMomentumLargeCap and SolEth dollar-neutral both abandoned

Outcome Summary

UsSectorEtfCrossSectionalMomentumDol-532dc0d3f6

Outcome Summary

A cross-sectional momentum spread across only nine sector ETFs is too narrow to be an edge; on a frictionless venue PF≈1.03 means the anomaly is absent, not fee-fragile.

Outcome Summary

Analyst issued 'abandon' at the pre-optimization backtest-review gate — a PF-1.03 breakeven signal on frictionless USEQ that 2-parameter tuning couldn't rescue; optimization and later stages were never reached.

Outcome Summary

A dollar-neutral long/short book on the nine original Select Sector SPDR ETFs, ranking sectors by 6-1 cross-sectional momentum each month to long the top 3 / short the bottom 3, with an absolute-momentum gate that flattens the book when the sector complex hasn't risen.

Outcome Summary

28-year, 517-trade backtest: total return 12.203 (CAGR 0.357), but Sharpe only 0.0736 (CI straddling zero), PF 1.0296, avg_trade_return_pct 0.0844%, max drawdown 25.57%; the short leg was the drag (win rate 0.345 vs 0.60 long).

Iteration History

sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of XLK.USEQ-1-DAY-LAST-EXTERNAL). This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).
Strategy report

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