Kalman Filter Trend
Use a 1D Kalman filter to estimate the latent price trend; trade in the direction of the filter.
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
| Parameter | Type | Default | Description |
|---|---|---|---|
| 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
- Gradually trending markets with noise reduction benefit
- Instruments with moderate, well-behaved volatility
- Stable Q/R parameter calibration over the lookback window
- High-noise markets — filter adapts too slowly
- Sharp reversals punished by inherent filter lag
- Markets requiring fast entry and exit responses