Skip to main content
2026-05-22 · 3 min read · ← 4 · vol · regime · research

Vol-clustering breaks the regime filter — but not for the obvious reason

what you'll learn · Why the vol-regime gate fails on the very signal it was designed to detect, and what this tells us about the difference between 'mean-reverting threshold gates' and 'persistent-state classifiers'.

vol_regime_filter was designed for vol-regime detection. Add vol clustering to the synthetic — the regime structure it's allegedly designed to detect — and its Sharpe drops by 1.3 below baseline. The reason isn't that the gate fails to fire; the gate fires CONSTANTLY. When vol clusters, the vol_5/vol_60 ratio is structurally elevated most of the time, so the gate drops most of the universe most of the time, leaving 2-3 names rotating through the ranking on a cluster-driven schedule that has nothing to do with momentum.

XsMomentumWithVolRegimeFilterStrategy (vol_regime_filter) shipped with the explicit thesis: “drop symbols whose vol_5/vol_60 ratio > 1.5; those are the regime-transitioning names and momentum doesn’t work on them” (see vol-ratio-as-cross-symbol-regime-gate).

On i.i.d. vol data (the existing harness mode), the strategy was the harness’s top mean-Sharpe arm at +1.493 (PR #655).

On clustered-vol data (the new --vol-cluster-strength mode), the same strategy posts mean −0.265 — a 1.76 drop. Worse than baseline when applied to data that has exactly the regime structure the strategy claims to detect.

The 3-seed sample

                          mean    stdev    min     max     Δ vs baseline
baseline                 +1.048   2.235  -1.364  +3.049   —
three_clock_momentum     -0.531   2.023  -2.291  +1.678   −1.579
vol_regime_filter        -0.265   2.295  -1.739  +2.380   −1.313
three_clock_vol_regime   -0.180   2.418  -2.193  +2.502   −1.228

Every arm except baseline is well below the noise floor; the strategies designed for regime detection are the WORST underperformers. Baseline’s +1.048 mean is intact (i.i.d. or clustered, the noise averages out), but baseline’s stdev exploded to 2.235 (from 0.581 on i.i.d. data).

Why the gate fails

The original thesis assumed vol_5/vol_60 would be a discriminating signal — sometimes < 1.5 (stable regime, trade) and sometimes > 1.5 (transition, skip). On i.i.d. vol data, that’s right: most days have ratio ≈ 1, occasional days spike above 1.5.

On clustered-vol data, vol_5/vol_60 is structurally elevated for long stretches:

  • A cluster of high-vol days: vol_5 (the short window) is high, but vol_60 (the long window) is still averaging in the prior calm period → ratio stays > 1.5.
  • After the cluster fades: vol_5 drops fast, but vol_60 is still elevated → ratio stays low for an extended period.

The ratio is mean-reverting on i.i.d. vol but ARMA-shaped on clustered vol. The gate’s binary “always > 1.5” or “always < 1.5” is the wrong shape for an ARMA signal.

The deeper distinction

This surfaces a useful taxonomy:

  • Mean-reverting threshold gates work when the gated signal oscillates around the threshold. The original vol-regime-filter design assumed this.
  • Persistent-state classifiers work when the gated signal has long runs above or below the threshold. Hidden Markov Models, regime-switching, or two-state classification applied here.

vol_5/vol_60 on i.i.d. vol is mean-reverting. On clustered vol it’s a persistent state. The gate that works for the first case (drop > 1.5) is the wrong tool for the second case (need to know which regime, not just whether the ratio is currently elevated).

What this isn’t

  • Not a critique of the original thesis. The thesis (“regime change is a real signal”) is right. The implementation (binary gate on vol_5/vol_60) only catches it when the underlying signal oscillates around the threshold. The mismatch is between the gate’s binary nature and the data’s persistent character.

  • Not a claim that vol-regime detection doesn’t work. It does — but the gate needs to be regime-AWARE, not just threshold-AWARE. The current code mistakes “vol is currently elevated” for “we’re in a regime transition.”

  • Not a 3-seed result you should publish. The numbers are directional, not definitive. A larger run + the analysis script’s hit-rate metric would refine the picture. The qualitative claim (the gate fires too often) is the load-bearing observation; the specific magnitude isn’t.

The fix (out of scope for this PR)

The gate’s discipline rule should be: gate on regime transitions, not on current regime state. Implementation sketch:

# Current: gate on level
if vol_5 / vol_60 > 1.5:
    drop_symbol(sym)

# Better: gate on change-in-level
if abs(vol_5_now / vol_5_prev - 1) > 0.5:
    drop_symbol(sym)  # the regime is changing right now

The first measures persistent state; the second measures transitions. A future XsMomentumWithVolRegimeTransitionFilterStrategy would test this — out of scope for this note, but the research question is named.

The discipline rule

A threshold gate works when the gated signal oscillates around the threshold. When the signal has persistent state (clusters, regimes), the threshold gate is the wrong tool — you need a regime classifier that distinguishes “currently elevated” from “transitioning.” Test the gate’s behaviour on data with the EXACT structure it claims to handle.

A corollary: the vol_regime_filter passing the i.i.d. test (PR #655’s +1.493 result) said “the implementation runs and produces a Sharpe number.” The vol-clustering test (this PR’s result) said “the implementation is wrong for the regime structure it claims to address.” Both are useful; only the second is the load-bearing test.

deskgdfindgfstrategiesgsfeaturesgepromotionsgpapigabotgbwritinggw

Shortcuts are no-ops while typing in an input or textarea.