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 inspect how local Jacobians turn a nonlinear state model into a recursive Gaussian approximation 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
Extended Kalman Filter 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. Nonlinear measurement links appear in latent volatility, rates, liquidity, and sensor-derived financial operations, but the approximation must be diagnosed.
Intuition before notation
The EKF trusts a tangent line near the current estimate; when curvature is strong or the derivative is small, that local picture can mislead. 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 first-order scalar nonlinear Gaussian filter. Scalar EKF with f(x)=a·x+b·sin(x), h(x)=x+c·x², analytic Jacobians, and known noise variances.
The core recursion is:
Fₜ=a+b cos(mₜ₋₁), Hₜ=1+2c m⁻ₜ; use Fₜ and Hₜ in the Kalman covariance and gain equations.
The package excludes automatic differentiation, iterated ekf, smoothing, parameter fitting, and claims of global optimality. Those are legitimate extensions, but adding them silently would make the arithmetic and information set ambiguous.
The package convention that changes the answer
Both Jacobians are evaluated at the stated package points: F at the prior filtered mean and H at the predicted mean.
That sentence belongs beside the formula because two implementations can both be called Extended Kalman Filter and still disagree when they initialize, index, or parameterize the recursion differently.
When this model is the right abstraction
Choose EKF when analytic or reliable automatic derivatives are available and the posterior stays inside a locally informative region. Compare with UKF when curvature dominates one tangent.
The EKF does not make a nonlinear model Gaussian or exact; it repeatedly rebuilds a local first-order approximation. 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
{
"a": 0.9,
"b": 0.22,
"c": 0.04,
"q": 0.06,
"r": 0.49,
"initial_mean": 0.0,
"initial_variance": 1.5
}
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.17405645. Applying the published recursion gives transition jacobian = 1.12000, measurement jacobian = 1.00000, innovation = 0.17406, kalman gain = 0.79849, filtered mean = 0.13898.
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 transition jacobian, measurement jacobian, innovation, kalman gain, filtered mean. 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 Extended Kalman Filter:
- Mild curvature: Matched nonlinear transition and measurement functions provide the control. The lab asks: Do the local Jacobians explain each gain update?
- Strong curvature: Truth uses a more curved measurement function than the filter. The lab asks: Where does a tangent approximation become visibly biased?
- Near-flat Jacobian: The path visits a region where the measurement slope is small. The lab asks: What happens to information flow when the local derivative approaches zero?
- Abrupt nonlinear shift: A persistent latent jump crosses a different curvature region. The lab asks: Is the delay caused by state persistence, local linearization, or both?
- Poor initialization: Truth begins far from the published Gaussian prior. The lab asks: Can repeated local updates recover from a poor starting point?
- High measurement noise: Observation noise competes with nonlinear curvature. The lab asks: Does raising measurement variance prevent overreaction?
- Single outlier: One extreme nonlinear observation tests recovery. The lab asks: Does the Jacobian amplify or damp the outlier's effect?
- Transition mismatch: Truth follows different nonlinear dynamics from the filter. The lab asks: Can good local derivatives rescue the wrong transition function?
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
Inspect the transition and measurement Jacobians before reading the gain. A poor result with a small derivative is a linearization warning, not merely a tuning problem.
The fixture-backed diagnostic is jacobian stress and local-linearization error. Tracking error, raw-observation error, normalized innovation squared, and interval hits are read beside the current Jacobians to separate noise from curvature failure.
For the canonical full path, the published values include filtered state mae 0.1829, filtered state rmse 0.2201, raw observation rmse 0.3855, rmse improvement vs raw 0.1655, mean normalized innovation squared 0.3828, synthetic 95 interval hit rate 1.0000. 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: Inspect the transition and measurement Jacobians before reading the gain. A poor result with a small derivative is a linearization warning, not merely a tuning problem.
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 Unscented Kalman Filter. 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 Extended Kalman Filter 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: An Introduction to the Kalman Filter, A New Approach to Linear Filtering and Prediction Problems, Bayesian Filtering and Smoothing, Time Series Analysis by State Space Methods. The complete source-role and limitation ledger is in the canonical package.
Rendered from the canonical Mermaid sources linked by this article.
Extended Kalman Filter 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.
WELCH2006 — An Introduction to the Kalman Filter
- Organization or authors: Greg Welch and Gary Bishop
- Source type: University technical report
- Publication or effective date: 2006-07-24
- Version: UNC-Chapel Hill TR 95-041
- URL or DOI: https://www.cs.unc.edu/~welch/media/pdf/kalman_intro.pdf
- Accessed: 2026-07-27
- Jurisdiction: Methodological; not jurisdiction-specific
- Supports: Discrete Kalman predict/update equations and EKF linearization.
- Limitations: Tutorial evidence; it does not make nonlinear EKF estimates exact.
- Evidence role: Public authoritative definition or maintained implementation context
- Publication boundary: No source dataset or copyrighted figure is redistributed
KALMAN1960 — A New Approach to Linear Filtering and Prediction Problems
- Organization or authors: R. E. Kalman
- Source type: Original peer-reviewed paper
- Publication or effective date: 1960-03-01
- Version: Journal of Basic Engineering 82(1), 35–45
- URL or DOI: https://doi.org/10.1115/1.3662552
- Accessed: 2026-07-27
- Jurisdiction: Methodological; not jurisdiction-specific
- Supports: Recursive linear state estimation and covariance propagation.
- Limitations: Original notation differs from this scalar teaching contract.
- Evidence role: Public authoritative definition or maintained implementation context
- Publication boundary: No source dataset or copyrighted figure is redistributed
SARKKA2023 — Bayesian Filtering and Smoothing
- Organization or authors: Simo Särkkä and Lennart Svensson
- Source type: Authoritative graduate textbook
- Publication or effective date: 2023-05-31
- Version: Second edition, Cambridge University Press
- URL or DOI: https://doi.org/10.1017/9781108917407
- Accessed: 2026-07-27
- Jurisdiction: Methodological; not jurisdiction-specific
- Supports: Unified Bayesian filtering terminology and modern treatment of linear, extended, and sigma-point filters.
- Limitations: The scalar functions and parameter values in this package remain local teaching choices.
- Evidence role: Public authoritative definition or maintained implementation context
- Publication boundary: No source dataset or copyrighted figure is redistributed
STATSMODELS_STATE — Time Series Analysis by State Space Methods
- Organization or authors: statsmodels developers
- Source type: Maintained official documentation
- Publication or effective date: 2025-12-05
- Version: statsmodels 0.14.6
- URL or DOI: https://www.statsmodels.org/stable/statespace.html
- Accessed: 2026-07-27
- Jurisdiction: Methodological; not jurisdiction-specific
- Supports: Current state-space terminology and the distinction between filtering and smoothing.
- Limitations: Library capabilities and defaults are not universal mathematical definitions.
- 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.
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 c=config; if(c.q<0||c.r<=0||c.initial_variance<0)throw new Error("invalid variance");
let mean=c.initial_mean, variance=c.initial_variance; const trace=[];
observations.forEach((observation,index)=>{
const transition_jacobian=c.a+c.b*Math.cos(mean);
const predicted_mean=c.a*mean+c.b*Math.sin(mean);
const predicted_variance=transition_jacobian**2*variance+c.q;
const predicted_observation=predicted_mean+c.c*predicted_mean**2;
const measurement_jacobian=1+2*c.c*predicted_mean;
const innovation=observation-predicted_observation;
const innovation_variance=measurement_jacobian**2*predicted_variance+c.r;
const kalman_gain=predicted_variance*measurement_jacobian/innovation_variance;
mean=predicted_mean+kalman_gain*innovation;
variance=(1-kalman_gain*measurement_jacobian)**2*predicted_variance+kalman_gain**2*c.r;
trace.push({index,transition_jacobian,measurement_jacobian,predicted_mean,predicted_variance,predicted_observation,innovation,innovation_variance,kalman_gain,filtered_mean:mean,filtered_variance:variance});
}); return trace;
}
The embedded lab now expands to its full document height, keeping the article as the only scroll surface.