Appearance
VIX-Regime Breakout
The first candidate through the evaluation loop — chosen to exercise every new piece (aux data → features → gated strategy → benchmarks → walk-forward), not because the hypothesis is expected to be a big edge. Code: vix_regime_breakout.py.
Hypothesis
Long Donchian breakouts carry worse in stressed-volatility regimes: follow-through after a new N-day high is weaker (and reversals sharper) when implied volatility is already elevated. Blocking new entries in those regimes should cut drawdown and fee drag more than it costs in missed winners. Exits are never blocked — the filter may only flatten exposure, never strand a position.
Exact rule
Identical to the SwingBreakout baseline (long-only 20/10 Donchian: enter on close above the prior 20-day high, exit on close below the prior 10-day low, 1 contract), plus one veto:
Skip the entry if that day's VIX close ≥ its trailing 252-session 80th percentile (
vix_features(..., risk_off_quantile=0.8)).
The threshold is a fixed prior — it is deliberately not re-fitted in walk-forward, so the regime filter can't overfit alongside the Donchian lookbacks. During the filter's first 252 sessions (warmup) entries are allowed, so an unwarmed filter reproduces the baseline.
Data lineage
| Input | Storage key | Alignment rule |
|---|---|---|
| MES/MNQ daily bars | mes_1d.parquet / mnq_1d.parquet | Panama back-adjusted at load |
| VIX daily close | vix_1d.parquet | Same-day join (Cboe close ~4:15pm ET precedes the futures settlement decision point) |
COT z-scores are computed by the same feature layer but unused here — this candidate isolates the VIX regime effect.
Evaluation protocol
Per roadmap item 3, the candidate is judged only against both benchmarks over the identical engine and Wealthsimple fee model (US$1.37/contract/side):
sh
cd strategies
uv run python -m vibe_trader.backtest.evaluate --symbol MES --source databento
uv run python -m vibe_trader.backtest.evaluate --symbol MES --source databento --walk-forward- Full-period table:
buy_and_hold,swing_breakout,vix_regime_breakout× metrics (CAGR, Sharpe, Sortino, max drawdown, win rate, profit factor, fee drag, exposure). - Walk-forward: Donchian lookbacks re-fit per rolling 504-bar train window (grid 10/20/40 × 5/10/20, selected by train Sharpe), scored on stitched 126-bar out-of-sample windows. The regime rule stays fixed throughout.
Results
First real-data run: 2026-07-13, Databento MES daily bars 2019-05-06 (launch) → 2026-07-12 (2,239 bars, 30 contracts, Panama-adjusted — the window includes both the 2020 COVID and 2022 stress regimes), US$100k account, fixed 1 contract (~$30k notional — absolute percentages are small by construction; compare rows, not magnitudes).
Full period (in-sample, default 20/10 lookbacks):
| Metric | buy_and_hold | swing_breakout | vix_regime_breakout |
|---|---|---|---|
| Total return | +21.8% | +10.2% | +6.9% |
| CAGR | 2.8% | 1.4% | 0.9% |
| Sharpe | 0.67 | 0.58 | 0.41 |
| Max drawdown | −5.9% | −2.5% | −3.1% |
| Win rate | — | 53.9% | 51.3% |
| Trades | 1 | 39 | 39 |
| Exposure | 100% | 56.7% | 54.2% |
Walk-forward, out-of-sample — the number to trust (13 splits, test span 2020-12-15 → 2026-03-20; Donchian lookbacks re-fit per split for both variants, regime rule fixed; both variants chose identical lookbacks in 10 of 13 splits):
| Metric | swing_breakout | vix_regime_breakout | buy_and_hold (same span) |
|---|---|---|---|
| Total return | −0.9% | −4.3% | +12.8% |
| CAGR | −0.2% | −0.8% | 2.3% |
| Sharpe | −0.07 | −0.42 | 0.55 |
| Sortino | −0.10 | −0.52 | 0.80 |
| Max drawdown | −4.9% | −6.2% | −6.1% |
| Win rate | 43.2% | 35.1% | — |
| Profit factor | 0.90 | 0.59 | — |
| Trades | 37 | 37 | 1 |
| Exposure | 43.8% | 37.4% | 99.9% |
Reading
The hypothesis is not supported — the gate hurt on every axis. Out of sample across a window containing two genuine stress regimes (2020, 2022), the VIX filter lowered returns (−4.3% vs −0.9%), Sharpe (−0.42 vs −0.07), win rate, and max drawdown (−6.2% vs −4.9%) relative to the ungated baseline — blocking entries near vol spikes mostly skipped the recoveries it was supposed to protect against. Notably, the re-fit Donchian baseline itself does not survive out-of-sample either (Sharpe ≈ 0 vs buy-and-hold's 0.55): in-sample lookback selection did not transfer. (A shorter 2021→2026 run showed the same ordering with milder numbers.) The candidate fails the protocol's bar on both benchmarks; what this note validates is the loop — aux data → features → gated strategy → walk-forward. Next hypotheses start from scratch, not from this gate.
Caveats
- Feature transport is backtest-only: the regime gate is a precomputed date map passed via strategy config. A live deployment would publish regime updates as custom
Dataon the Nautilus message bus. - Window: the cache backfills to the 2019-05-06 micro launch (~7 years; the 10-year
LOOKBACK_YEARScap binds only for longer-listed symbols) and accumulates from there. Two sustained stress regimes (2020, 2022) is still a small regime sample. - Panama-adjusted series: levels are shifted by cumulative roll gaps; percentage-based metrics on old data inherit that distortion (small at MES/MNQ roll gaps, but real).
- The 80th-percentile threshold is a prior, not a fitted optimum; the point of this note is the loop, and the number to watch is the out-of-sample table above.