Skip to content

Regime numbering is inverted between training labeler and inference factory — bull strategies recommended in bear markets #15

@bradsmithmba

Description

@bradsmithmba

Summary

src/data/regime_labeler.py and src/strategies/factory.py use the same regime integers (0–7) but assign them completely different semantics. The neural network is trained on labels from the regime labeler, then makes inferences interpreted by the factory. The result: bull-market strategies are recommended in bear markets, and vice versa.

The mismatch

Regime int Labeler (training labels) Factory (inference mapping)
0 BULL_TRENDING Deep Bear Market ← INVERTED
1 BEAR_TRENDING Bull Trending ← INVERTED
2 HIGH_VOLATILITY High Volatility Trending
3 LOW_VOLATILITY Uncertain/Transitional
4 SIDEWAYS_RANGING Low Volatility/Sideways
5 RECOVERY Moderate Bull/Sideways
6 DISTRIBUTION Recovery Phase
7 CRISIS High Volatility Uncertain
8 (never produced) Extreme Volatility ← unreachable

When the labeler identifies a BULL_TRENDING market (regime 0) and trains the neural net on it, the factory maps regime 0 to "Deep Bear Market" and recommends LONG_PUT, BEAR_PUT_SPREAD, BEAR_CALL_SPREAD. The model has learned to output 0 for bull markets, but the factory deploys bear strategies when it sees 0.

The inversion is not just regimes 0 and 1. Almost every regime name is misaligned: RECOVERY (5) maps to "Moderate Bull/Sideways" instead of recovery strategies; DISTRIBUTION (6) maps to "Recovery Phase"; CRISIS (7) maps to "High Volatility Uncertain" instead of crisis/defensive strategies.

Secondary bug

The factory defines 9 regime mappings (0–8), but RegimeDetector outputs exactly 8 regime probabilities (num_regimes=8). Regime 8 ("Extreme Volatility") is structurally unreachable during inference — the argmax of 8 outputs is always 0–7.

Correct fix

One of the two systems must be updated to match the other. The labeler's naming reflects standard financial terminology (BULL_TRENDING, BEAR_TRENDING, CRISIS) and was presumably designed first. The factory mapping should be updated:

# Correct factory mappings aligned with labeler RegimeType
0: [LONG_CALL, BULL_CALL_SPREAD, BULL_PUT_SPREAD],         # BULL_TRENDING
1: [LONG_PUT, BEAR_PUT_SPREAD, BEAR_CALL_SPREAD],          # BEAR_TRENDING
2: [LONG_STRADDLE, LONG_STRANGLE, ...],                    # HIGH_VOLATILITY
3: [CALENDAR_CALL, CALENDAR_PUT, SHORT_STRADDLE, ...],     # LOW_VOLATILITY
4: [IRON_CONDOR, SHORT_STRANGLE, BUTTERFLY],               # SIDEWAYS_RANGING
5: [BULL_CALL_SPREAD, LONG_CALL, BULL_PUT_SPREAD],         # RECOVERY
6: [BEAR_CALL_SPREAD, IRON_CONDOR, ...],                   # DISTRIBUTION
7: [LONG_STRADDLE, LONG_PUT, BEAR_PUT_SPREAD],             # CRISIS

Also reduce factory._regime_mappings to 8 entries (0–7) and remove the unreachable regime 8, or update RegimeDetector to num_regimes=9.

Any trained model weights produced under the current misalignment should be discarded — they have learned to associate features with the wrong regime integers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions