Methodology

How the model is trained, what it learns from, and how the live probabilities are computed each day.

The model

A 3-state Gaussian Hidden Markov Model with a full covariance matrix per state, trained via the Baum-Welch (Expectation-Maximization) algorithm using hmmlearn's GaussianHMM implementation. Hyperparameters:

Three states is the right count for a regime indicator: more states tend to overfit on daily data (the model finds spurious sub-regimes that don't generalize), and fewer collapses the meaningful bull/bear distinction.

Features (observations)

Each trading day is represented by up to four features. Features are z-scored (standardized to mean=0, std=1) before feeding into the HMM so no single feature dominates.

FeatureFormulaWhy it matters
log_return ln(P[t] / P[t-1]) Day-over-day directional move. Captures bull/bear drift.
realized_vol std(log_return, window=10) Backward-looking 10-day volatility. The primary axis separating regimes.
fx_change ln(FX[t] / FX[t-1]) Local currency move vs USD (or DXY for SPX). Currency stress often leads equity stress.
vix raw level (India VIX, CBOE VIX) Forward-looking implied vol from options. Available only for Nifty + S&P.

KOSPI and Shanghai use 3 features (no VIX). For those indices, realized_vol carries the vol signal alone — and is more than sufficient: see the regime tables on the dashboard.

State labeling (the bear/neutral/bull rule)

The HMM returns three states numbered 0, 1, 2 — but the numbering is arbitrary and can flip between training runs. To make labels stable, after each fit we compute a composite score for each state:

score[i] = -z(mean_realized_vol[i]) + 0.25 * z(mean_log_return[i])

Where z(.) is z-scoring across the three states. States are then sorted by score:

Why vol-dominant? Returns alone barely separate regimes in long-bull markets like India — all three states tend to have positive mean returns. Volatility is the actionable axis: when vol is high, be defensive, regardless of price direction. The 0.25 weight on returns acts as a tiebreaker for ambiguous cases.

Filtered vs smoothed probabilities

The single most important methodological choice in a live dashboard is using filtered probabilities, not smoothed ones.

This dashboard always shows filtered probabilities. We compute them via a direct forward-pass implementation of the log-alpha recursion:

log_alpha[t, j] = log_emission[t, j]
                + logsumexp_i( log_alpha[t-1, i] + log_transmat[i, j] )

filtered[t] = exp(log_alpha[t] - logsumexp(log_alpha[t]))

This means: today's probabilities will be revised tomorrow as new data arrives, but past displayed probabilities are never revised retroactively using future data.

Mathematical guarantee: at the final timestep, filtered and smoothed are mathematically identical (no future data exists to smooth with). This is used as a sanity check in the audit — we verified that they match to machine precision at the last row across all four models.

Data sources and dates

Daily OHLC data is pulled from Yahoo Finance via the yfinance library:

IndexPrice tickerFX tickerVIX tickerRange
Nifty 50^NSEIINR=X^INDIAVIXJan 2010 – today
S&P 500^GSPCDX-Y.NYB (DXY)^VIXJan 2010 – today
KOSPI^KS11KRW=XJan 2010 – today
Shanghai000001.SSCNY=XJan 2010 – today

All indices have ~4,000 trading days of data. Yahoo Finance is a free, unofficial data source — adequate for personal research, but commercial deployments should use a paid feed (e.g., EOD Historical Data, Alpha Vantage). The DataSource abstraction in the code makes the swap a one-file change.

Update cadence

JobWhenWhat it does
daily Mon–Fri 16:30 IST (after NSE close) Refetches the latest day's prices/FX/VIX, recomputes features, runs filtered inference, writes today's probability row to SQLite.
weekly Sundays Retrains all four HMMs on the full dataset including the just-finished week. Saves a timestamped model backup. Rebuilds the full probability history.
api Always FastAPI server serves the dashboard, with auto-restart if it ever crashes.

What we deliberately don't do

Honest limitations

Verification

A comprehensive audit was performed across seven layers — raw data accuracy, alignment and gaps, feature math, HMM training internals, label permutation, filtered probability correctness, and database parity. All 100+ assertions passed; the math is fully reproducible and the probabilities are provably causal (verified by truncating data and confirming byte-identical past values).

Known data-quality concerns: 4 days of suspect USD/INR ticks in Jan-Feb 2012 and 2 days of suspect USD/CNY ticks in Jul 2011 from Yahoo Finance. Impact is bounded to ~2 weeks of localized regime classification near those dates and does not affect any high-confidence historical calls (COVID, 2017–18 bull, 2022 bear, etc.).