State models are attractive because they turn a noisy sequence into an interpretable evolving belief. They are dangerous for exactly the same reason: a smooth line or decisive regime label can look like fact even when it is only the output of a fragile configuration. This tutorial shows how to update causal probabilities for two hidden regimes from transitions and Gaussian emissions while keeping the information boundary and uncertainty visible.
The deliverable is practical. You will calculate one update, implement the recursion in Python and TypeScript, inspect eight model-specific scenarios, and use a guided lab to see why the state changes. The result is an educational filter, not an investment recommendation or a claim of predictive power.
The question this model answers
Hidden Markov Model answers a conditional question: given the selected model, its fixed configuration, and observations available through the current timestamp, what state summary should be carried forward?
That wording matters. It does not ask what the state “really was” after seeing the future. It does not estimate parameters. It does not decide whether a trading action is profitable. The filter can organize uncertainty about volatility or liquidity regimes without declaring an unobservable label as fact.
Intuition before notation
The prior asks where the chain is likely to move; the emission asks which state better explains today's observation; normalization combines them. Every update therefore needs a ledger. The learner should be able to point to the prior, the prediction, the new evidence, and the posterior. If one of those pieces is hidden, the final curve becomes an assertion rather than an explanation.
The state update visual is canonical: its labels correspond directly to the trace produced by both implementations.
The frozen definition
This article uses a two-state Gaussian-emission forward filter. Two-state Gaussian HMM forward filtering with a row-stochastic transition matrix and fixed emission parameters.
The core recursion is:
ξₜ(j)=Σᵢ αₜ₋₁(i)pᵢⱼ; αₜ(j)∝ξₜ(j)·N(yₜ|μⱼ,σ²ⱼ).
The package excludes baum-welch fitting, viterbi decoding, backward smoothing, duration models, and trading labels. Those are legitimate extensions, but adding them silently would make the arithmetic and information set ambiguous.
The package convention that changes the answer
Transition rows map previous state i to current state j, and only forward-filtered probabilities are published.
That sentence belongs beside the formula because two implementations can both be called Hidden Markov Model and still disagree when they initialize, index, or parameterize the recursion differently.
When this model is the right abstraction
Use an HMM when the latent representation is discrete and the observation distribution depends on the current state. Use MSAR when lagged observations also drive regime-specific conditional means.
The most likely state is not an observed regime and high classification accuracy can hide badly calibrated probabilities. The practical selection test is not which curve looks smoothest. It is which latent representation, observation model, and information clock match the decision you must audit.
Filtering is not smoothing
A filtered state at t conditions on observations through t. A smoothed state at t can use later observations. Current statsmodels state-space documentation makes the same distinction between filtered and smoothed output (statsmodels 0.14.6 state-space guide).
For an online dashboard, alert, or causal backtest, silently substituting smoothed probabilities is a future-information leak. A retrospective research chart may use smoothing, but it must say so.
Inputs and clocks
The fixture uses 160 daily UTC observations per scenario. They are deterministic and synthetic. The timestamps are observation and availability times together; no exchange calendar, revision process, or vendor feed is implied.
The latent truth or true regime appears only for evaluation. The filter receives the observation values and fixed configuration. This separation lets us ask whether the recursion detects the generator without giving it the answer.
The configuration is part of the claim
{
"transition": [
[
0.94,
0.06
],
[
0.12,
0.88
]
],
"means": [
-0.15,
0.25
],
"stds": [
0.55,
1.45
],
"initial": [
0.8,
0.2
]
}
These values are implementation choices, not fitted estimates. A production claim would need a pre-declared fitting sample, likelihood or loss, optimizer, parameter constraints, model-selection process, and out-of-sample evaluation.
One update by hand
The first canonical observation is -0.91833690. Applying the published recursion gives predicted state 0 = 0.77600, predicted state 1 = 0.22400, posterior state 0 = 0.82646, posterior state 1 = 0.17354, most likely state = 0.
The calculation retains full precision and rounds only for display. The validator repeats this first update independently rather than asking the Python implementation to grade itself.
What the state ledger reveals
For this topic, the most useful fields are predicted state 0, predicted state 1, posterior state 0, posterior state 1, most likely state. Read them together. A posterior without its predicted state hides persistence; a posterior without its innovation or likelihood hides the observation's contribution.
Eight scenarios that target this algorithm
The scenarios are not a generic family checklist. Each one changes a specific assumption that matters to Hidden Markov Model:
- Separated emissions: Different means and scales make the forward recursion easy to inspect. The lab asks: Does probability move because of transition memory and emission evidence?
- Overlapping emissions: Both states generate similar observations. The lab asks: Does the posterior remain mixed instead of inventing certainty?
- Sticky regimes: Long runs test whether persistence delays a genuine switch. The lab asks: How much evidence is needed to overcome a sticky prior?
- Fast switching: Short runs conflict with the frozen transition matrix. The lab asks: When does transition mismatch dominate emission evidence?
- Variance-only regimes: Both states share a mean but differ in scale. The lab asks: Can a single observation identify a variance regime reliably?
- Rare regime: A short state-1 interval challenges prior odds. The lab asks: Does a rare state need stronger evidence than a common one?
- Single outlier: One extreme observation resembles the high-variance state. The lab asks: Does one spike create a transient regime story?
- Transition mismatch: Truth switches more often than the published chain expects. The lab asks: How do Brier score and entropy reveal stale persistence?
This is the difference between a demo and a learning reference: the reader can name the stressed assumption before looking at the output.
The algorithm
- Validate observations and the fixed configuration.
- Initialize the declared prior.
- Predict before reading the current observation.
- score the observation under the predicted state or hypotheses.
- update and normalize.
- record diagnostics and carry the filtered state forward.
This order is causal. Reordering it can leak the observation into the prior or apply the transition twice.
A diagnostic that can disagree with the picture
Read the transition prior first, then the two emission likelihoods, and judge the full posterior with Brier/log score and entropy rather than hard-label accuracy alone.
The fixture-backed diagnostic is probability quality beyond hard labels. Causal Brier score, negative log score, mean posterior entropy, and a 0.5-probability benchmark use known synthetic regimes without exposing them to the filter.
For the canonical full path, the published values include brier score state 1 0.0213, neutral probability brier 0.2500, negative log score 0.1198, mean posterior entropy nats 0.2940, most likely state accuracy 0.9938. They are author-derived from known synthetic truth after the causal trace is complete. They are not an empirical result and do not establish usefulness on market data.
The interactive version recalculates the same quantities only over rows revealed through the current step. That makes the score informative without letting future truth influence an earlier filter state.
Python and TypeScript parity
Both implementations accept the same observation list and configuration object. Both return one trace row per observation. The shared JSON fixture compares every primary state field at a tolerance of 1e-9.
Readable reference arithmetic is intentional. Optimization comes after the contract is proved. In production, use stable matrix decompositions, monitored conditioning, and explicit schema/version controls appropriate to the chosen extension.
The model-specific audit discipline
Continuous-state filters must keep covariance nonnegative. Discrete-state filters must keep probabilities normalized. The UKF must publish its sigma-point weights. BOCPD must define whether a change occurs before or after the current observation. MSAR must define transition direction and which regime owns each AR coefficient.
These are not cosmetic implementation details. They determine the answer.
For this topic, the decisive audit is: Read the transition prior first, then the two emission likelihoods, and judge the full posterior with Brier/log score and entropy rather than hard-label accuracy alone.
The outlier trap
A single extreme observation can produce a large innovation, strong emission ratio, or elevated change probability. That evidence is not the same as a persistent break. Compare the next several observations before attaching a narrative.
The failure map is a decision aid: check model assumptions, sensitivity, and subsequent evidence before treating a filtered state as operational truth.
Weak separation is an honest result
When regimes have similar emissions or observation noise dominates state movement, posterior probabilities should remain mixed. Forcing a hard label throws away the most important output: the model is uncertain.
The same principle applies to continuous filters. A narrow covariance is only meaningful if process and measurement variances are credible.
Historical evidence decision
The historical-example decision is not useful. A named market episode would add narrative appeal but would not validate a frozen latent-state model. The package therefore uses deterministic synthetic data so the hidden truth, observation clock, and state transition are auditable. A historical case would require licensed point-in-time observations, a parameter-estimation cutoff, revision policy, benchmark, and redistribution permission.
This avoids an especially common error: showing one named episode, fitting or tuning with knowledge of the whole episode, and then presenting a hindsight state chart as causal detection evidence.
Explore the guided lab
The self-contained playground implements choose → step → observe → explain → compare. Change the scenario and one meaningful parameter, then watch the trace, chart, guidance, and audit table recompute together.
Reset restores the canonical first row. Step and Play share one transition. In reduced-motion mode, Play advances exactly one observation and never starts a timer.
How to validate a production extension
- Freeze the information clock and revision policy.
- Separate fitting, filtering, forecasting, smoothing, and decision evaluation.
- Use time-ordered holdouts or rolling origins.
- Benchmark against a simpler state or regime rule.
- Test parameter sensitivity and transition/emission misspecification.
- Monitor numerical conditioning and probability normalization.
- Keep uncertainty visible in the user interface.
- Never convert a latent-state estimate directly into a performance claim.
Family map
The next tutorial is Markov-Switching Autoregression. The representation changes, but the audit question stays constant: exactly what did the model know when it produced this state?
Summary
You can now implement and audit Hidden Markov Model under a precise selected contract. The essential skill is not drawing a smooth state line. It is preserving the prior, prediction, evidence, posterior, and information boundary so another analyst can reproduce the result and challenge the assumptions.
Primary methodological records: A Tutorial on Hidden Markov Models and Selected Applications in Speech Recognition, Strictly Proper Scoring Rules, Prediction, and Estimation. The complete source-role and limitation ledger is in the canonical package.
Rendered from the canonical Mermaid sources linked by this article.
Hidden Markov Model Causal Update Flow
This diagram shows the information order that every implementation and visual preserves.
Takeaway: the observation enters after prediction, and no row after t can affect the filtered state at t.
ReferencesPrimary sources and evidence notesExpand the source trail, evidence role, and limitations behind the engineering choices.
Expand the source trail, evidence role, and limitations behind the engineering choices.
The canonical package distinguishes sourced definitions from implementation choices and synthetic inputs.
RABINER1989 — A Tutorial on Hidden Markov Models and Selected Applications in Speech Recognition
- Organization or authors: Lawrence R. Rabiner
- Source type: Peer-reviewed tutorial paper
- Publication or effective date: 1989-02
- Version: Proceedings of the IEEE 77(2)
- URL or DOI: https://doi.org/10.1109/5.18626
- Accessed: 2026-07-27
- Jurisdiction: Methodological; not jurisdiction-specific
- Supports: Discrete-state HMM transition, emission, and forward recursions.
- Limitations: Applications are not financial and do not validate regime profitability.
- Evidence role: Public authoritative definition or maintained implementation context
- Publication boundary: No source dataset or copyrighted figure is redistributed
GNEITING2007 — Strictly Proper Scoring Rules, Prediction, and Estimation
- Organization or authors: Tilmann Gneiting and Adrian E. Raftery
- Source type: Peer-reviewed methodological paper
- Publication or effective date: 2007-03
- Version: Journal of the American Statistical Association 102(477), 359-378
- URL or DOI: https://doi.org/10.1198/016214506000001437
- Accessed: 2026-07-27
- Jurisdiction: Methodological; not jurisdiction-specific
- Supports: Use of proper scoring rules to evaluate probabilistic predictions.
- Limitations: Synthetic Brier and log scores diagnose the teaching fixture; they do not establish market usefulness.
- Evidence role: Public authoritative definition or maintained implementation context
- Publication boundary: No source dataset or copyrighted figure is redistributed
Full dependency-light reference implementations in both supported languages.
const logSumExp=values=>{const m=Math.max(...values);return m+Math.log(values.reduce((s,v)=>s+Math.exp(v-m),0));};
export function runFilter(observations, config) {
if(!Array.isArray(observations)||!observations.length||observations.some(v=>!Number.isFinite(v)))throw new Error("observations must be finite and non-empty");
const {transition,means,stds}=config;let posterior=[...config.initial];const trace=[];
if(transition.length!==2||transition.some(row=>row.length!==2||Math.abs(row.reduce((a,b)=>a+b,0)-1)>1e-12)||stds.some(v=>v<=0))throw new Error("invalid two-state configuration");
observations.forEach((observation,index)=>{
const predicted=[0,1].map(j=>posterior.reduce((s,p,i)=>s+p*transition[i][j],0));
const lj=[0,1].map(j=>Math.log(Math.max(predicted[j],1e-300))-Math.log(stds[j]*Math.sqrt(2*Math.PI))-.5*((observation-means[j])/stds[j])**2);
const z=logSumExp(lj);posterior=lj.map(v=>Math.exp(v-z));
trace.push({index,predicted_state_0:predicted[0],predicted_state_1:predicted[1],posterior_state_0:posterior[0],posterior_state_1:posterior[1],most_likely_state:posterior[0]>=posterior[1]?0:1,log_predictive_density:z});
});return trace;
}
The embedded lab now expands to its full document height, keeping the article as the only scroll surface.