Regime Detection — User Guide
Regime detection identifies market states (bull/bear, volatility clusters, structural breaks) for filtering signals, sizing positions, and ML feature engineering.
All algorithms live in quantwave-core/src/regimes/ and expose batch (Polars) and streaming (Next<T>) paths with identical semantics.
Algorithm overview
| Algorithm | Core type | Best for | Polars method |
|---|---|---|---|
| Volatility clustering | Online K-means on ATR | Crisis vs stable vol regimes | .ta().volatility_clusterer(...) |
| HMM (Hamilton) | Gaussian emissions + Viterbi | Bull/bear switching | .ta().hmm_bull_bear(col) |
| GMM | Multi-variate clustering | Latent factor states | .ta().gmm(cols, k) |
| PELT | Exact changepoint segmentation | Historical break dating | .ta().pelt(col, penalty, min_dist) |
Sources: Hamilton (1989); Killick et al. (2012) PELT; Prakash et al. (2021) vol clustering; Two Sigma (2021) GMM notes.
1. Volatility clustering
Identifies discrete volatility regimes (e.g. Stable → Crisis) using rolling ATR and online clustering.
Polars (batch)
import polars as pl
df = (
pl.read_csv("ohlcv.csv")
.lazy()
.ta()
.volatility_clusterer(
high="high",
low="low",
close="close",
atr_period=14,
window_size=100,
k=3,
)
.collect()
)
# Column: volatility_regime (u32 labels)
Rust (streaming)
use quantwave_core::regimes::volatility_clustering::VolatilityClusterer;
use quantwave_core::traits::Next;
let mut clusterer = VolatilityClusterer::new(14, 100, 3);
for (h, l, c) in ohlcv {
let regime = clusterer.next((h, l, c));
}
Edge cases
| Condition | Behavior |
|---|---|
window_size not filled |
Partial / default cluster assignment |
k=1 |
Degenerate — use k ≥ 2 |
| NaN OHLC | Skipped or propagates per bar |
ML integration
Join volatility_regime with PA events or .ta.features.regime_probs() for confluence filters. See ML Features guide.
2. Hidden Markov Model (bull/bear)
Regime-switching HMM with Gaussian emissions; Viterbi decoding for the most likely state path.
Polars (batch)
df = (
df.lazy()
.with_columns(pl.col("close").pct_change().alias("returns"))
.ta()
.hmm_bull_bear("returns")
.collect()
)
# Column: hmm_regime (1=Bull, 2=Bear, 0=other)
Python (streaming)
Edge cases
| Condition | Behavior |
|---|---|
| Short series | Unstable state estimates; prefer ≥ 100 bars |
| Zero variance returns | Emission collapse — watch for stuck states |
Strategy pattern
# Long only in bull regime (label 1)
df = df.with_columns(
(pl.col("hmm_regime") == 1).cast(pl.Float64).alias("regime_filter")
)
Used in ML Features → Backtest E2E.
3. Gaussian Mixture Model (GMM)
Clusters multi-column factor data into latent regimes.
Polars (batch)
Edge cases
| Condition | Behavior |
|---|---|
k > row count |
Fit degrades — reduce k |
| Correlated columns | Consider orthogonal factors first |
4. PELT changepoint detection
Pruned Exact Linear Time segmentation — finds statistically significant level shifts in a series.
Polars (batch)
df = (
df.lazy()
.ta()
.pelt("close", penalty=1.0, min_dist=10)
.collect()
)
# Changepoint flags in output column
Edge cases
| Condition | Behavior |
|---|---|
Low penalty |
Many changepoints (over-segmentation) |
High penalty |
Few or no breaks detected |
ML features shortcut
For backtest-ready regime labels without manual HMM wiring:
df = (
pl.read_csv("ohlcv.csv")
.lazy()
.ta()
.features()
.regime_features() # HMM bull/bear label column
.collect()
)
Also available: .ta.features.regime_probs() for probability struct output.
Parity & validation
Regime modules follow the Universal Indicator pattern where stateful. Use streaming wrappers in live systems:
Core tests: cargo nextest run -p quantwave-core -- regimes
Related docs
- ML Features guide
- Indicator Gallery — Regime section
- PA confluence notebook