The Intuition

The Kalman filter, developed by Rudolf Kálmán (1960), is a recursive Bayesian estimator for the hidden state of a linear dynamical system observed with noise. In trading, we treat the true latent price trend as the hidden state, and the observed price as a noisy measurement of that state. The filter optimally combines the prior state estimate with the new observation, weighted by their relative uncertainties.

The key innovation over simple moving averages: the Kalman gain K(t) is adaptive. When observations are very noisy (high R) relative to the state process noise (Q), K is small — the filter trusts its prior prediction more than the new data. When observations are relatively reliable, K is large — the filter updates heavily on the new price. This makes the Kalman filter a self-calibrating smoother that is optimal in the least-squares sense under Gaussian assumptions.

In equities and futures, the Kalman filter has been used for: (1) Pairs trading — estimating a time-varying hedge ratio between two assets (Chan 2013); (2) Kalman-smoothed trend signals — the filtered state estimate is a more principled trend estimate than a fixed-window moving average; (3) Online regression — the filter can track the OLS relationship between two series in real time without a fixed window.

Key assumptions: (1) The system is linear: the state transition (x(t) = x(t-1) + w) and observation model (z(t) = x(t) + v) are both linear. (2) Noise is Gaussian: process noise w and observation noise v follow normal distributions. (3) Q and R are correctly specified. In practice, these noise variances must be estimated or tuned — wrong values give systematically over- or under-reactive filters. (4) The state dimension is appropriate — our 1D filter tracks a single latent price level; real implementations often use 2D state (level + trend).

The main failure mode: if Q/R is too high (too much trust in the state model), the filter is slow to respond to genuine price moves — it lags badly. If Q/R is too low, the filter is very noisy and essentially tracks the raw price. Tuning Q and R is an empirical problem; many practitioners use expectation-maximisation (EM) algorithms to estimate them from the data. Without careful calibration, Kalman filter trading signals can underperform simple moving averages.

The Math

Read this as a compact model summary: what the signal sees, what it ignores, and where fragility can creep in.

State:  x(t) = x(t-1) + w(t),    w ~ N(0, Q)
Obs:    z(t) = x(t) + v(t),      v ~ N(0, R)

Predict:  x̂(t|t-1) = x̂(t-1)
          P(t|t-1)  = P(t-1) + Q
Update:   K(t) = P(t|t-1) / (P(t|t-1) + R)
          x̂(t) = x̂(t|t-1) + K(t) × (z(t) - x̂(t|t-1))
          P(t)  = (1 - K(t)) × P(t|t-1)

Signal(t) = +1  if Δx̂(t) > 0   [trend rising]
          = -1  if Δx̂(t) < 0   [trend falling]

Parameters

ParameterTypeDefaultDescription
process_noise float 0.0001 Kalman process noise variance (Q)
obs_noise float 0.01 Kalman observation noise variance (R)

Source Code

def run(ticker: str, start: str, end: str, **params) -> dict:
    Q = float(params.get("process_noise", 1e-4))
    R = float(params.get("obs_noise", 1e-2))
    df = fetch_ohlcv(ticker, start, end)

    log_prices = np.log(df["Close"].values)
    filtered = _kalman_filter(log_prices, Q, R)

    trend = np.diff(filtered, prepend=filtered[0])
    pos = pd.Series(0.0, index=df.index)
    pos[trend > 0] = 1.0
    pos[trend < 0] = -1.0

    return run_backtest(df, pos, ticker=ticker, start=start, end=end,
                        strategy=METADATA["slug"],
                        params={"process_noise": Q, "obs_noise": R})

Further Reading

  • Kalman, R. (1960). A New Approach to Linear Filtering and Prediction Problems. ASME Journal of Basic Engineering, 82(1), 35–45.
  • Chan, E. (2013). Algorithmic Trading, Ch. 6. Wiley.
  • Welch, G. & Bishop, G. (2006). An Introduction to the Kalman Filter. UNC Technical Report TR 95-041.

When It Works / When It Fails

Works
  • Gradually trending markets with noise reduction benefit
  • Instruments with moderate, well-behaved volatility
  • Stable Q/R parameter calibration over the lookback window
Fails
  • High-noise markets — filter adapts too slowly
  • Sharp reversals punished by inherent filter lag
  • Markets requiring fast entry and exit responses

Regime Fit

Bull / Calm or Normal vol
Best conditions at 1.0 long. Kalman state estimate tracks trend smoothly with minimal lag.
Bull / Stressed vol
Scaled to 0.75 long. Filter may lag sharp moves but signal direction remains valid.
Transition / any vol
0.0–0.35. State estimate oscillates; no reliable directional output.
Bear side
Mirrors Bull profile on short scale. Filter tracks downtrends with the same lag structure.

Compared to Alternatives

vs MA Crossover
Both track trend direction; Kalman adapts smoothing dynamically via the Q/R noise ratio. MA Crossover uses a fixed window — Kalman is more sophisticated but harder to calibrate.
vs Bollinger
Bollinger detects price deviation from a statistical mean; Kalman tracks the underlying price state via a model. Kalman is model-driven; Bollinger is a statistical rule.
vs TSMOM
TSMOM is a simple lookback return sign; Kalman is a continuous state estimate of price. Kalman provides a smoother, model-driven trend signal with more parameters to calibrate.
Run This Strategy →