Particle Filter

Sequential Monte Carlo for nonlinear, non-Gaussian dynamics

The Kalman filter assumes linear dynamics and Gaussian noise. Real markets have jumps, fat tails, and multimodal distributions. The particle filter drops those assumptions: it represents the posterior as a set of weighted particles, each a hypothesis about the current state.

API

python
pf = hz.ParticleFilter(
    n_particles=1000,
    initial_state=0.5,
    process_noise=0.01,
    measurement_noise=0.05,
)

pf.update(observation)

mean = pf.state_mean()          # weighted mean of particles
particles = pf.particles()     # list of (state, weight) tuples

Parameters

ParameterMeaningGuidance
n_particlesNumber of particles500-5000. More particles = better approximation, higher cost
initial_stateStarting state for all particlesSet to your prior estimate
process_noiseStd dev of state transition noiseControls how fast the state can drift
measurement_noiseStd dev of observation noiseHow noisy are your observations

Methods

python
pf.update(observation)       # incorporate new observation, resample
pf.state_mean()              # weighted average state
pf.state_std()               # weighted std dev (uncertainty)
pf.particles()               # raw particles and weights
pf.effective_sample_size()   # ESS -- below n/2 means particle degeneracy

Tracking a jumpy fair value

Prediction markets with low liquidity exhibit price jumps that a Kalman filter smooths away too aggressively. A particle filter can maintain multiple hypotheses — some particles track the old regime, others jump to the new one:

python
pf = hz.ParticleFilter(
    n_particles=2000,
    initial_state=0.50,
    process_noise=0.02,      # allows moderate jumps
    measurement_noise=0.03,
)

for tick in market_ticks:
    pf.update(tick.price)

    fair = pf.state_mean()
    uncertainty = pf.state_std()

    if uncertainty > 0.10:
        # High uncertainty = multimodal posterior = possible regime transition
        print(f"Uncertain regime: fair={fair:.3f} +/- {uncertainty:.3f}")

Stochastic volatility estimation

Track time-varying volatility where the vol process itself is stochastic:

python
pf = hz.ParticleFilter(
    n_particles=3000,
    initial_state=0.20,       # initial vol estimate (20%)
    process_noise=0.005,      # vol-of-vol
    measurement_noise=0.10,   # return observations are noisy
)

for ret in daily_returns:
    pf.update(abs(ret))       # absolute returns as vol proxy
    vol_estimate = pf.state_mean()
    # Use vol_estimate for position sizing

When to use

  • Jump-diffusion models: when fair values can jump discontinuously (event-driven markets, earnings, rulings).
  • Multimodal distributions: when there are two or more plausible states and you need to track all of them.
  • Stochastic vol: when volatility itself is a random process and you want to track it online.
  • Non-Gaussian noise: when observation noise has fat tails or skew that violate Kalman assumptions.

For purely linear/Gaussian systems, the Kalman filter is cheaper and exact. Use the particle filter when those assumptions break down.

Next