Skip to content

Whittle (frequency-domain) estimation

Whittle estimation fits a parametric spectral density to a series' periodogram by maximum likelihood, in the frequency domain rather than the time domain. It's the one module in this port with genuinely no equivalent in statsmodels, arch, or elsewhere in the Python ecosystem — see Library alternatives for the full survey. Everything else nearby (Kalman filtering, VAR estimation) has a stronger, more complete off-the-shelf Python alternative; this is the case where the port is filling a real gap rather than re-deriving something a mature library already does better.

Local-level model

A local-level (random-walk-plus-noise) model, y_t = mu_t + eps_t, mu_t = mu_{t-1} + eta_t, is fit by maximizing the Whittle approximate log-likelihood of the first-differenced series against the model's spectral density — recovering (sigma_epsilon, sigma_eta) without ever running a Kalman filter.

import numpy as np
from quanttoolbox.econometrics.whittle import whittle_local_level

# Simulate a local-level series with known variances
rng = np.random.default_rng(0)
n = 500
sigma_epsilon_true, sigma_eta_true = 1.0, 0.5
mu = np.cumsum(sigma_eta_true * rng.standard_normal(n))
y = mu + sigma_epsilon_true * rng.standard_normal(n)

result = whittle_local_level(y, sv=np.array([1.0, 1.0]))
print("estimated (sigma_epsilon, sigma_eta):", np.round(result.theta, 4))
print("stderr:                             ", np.round(result.stderr, 4))
print("true      (sigma_epsilon, sigma_eta):", [sigma_epsilon_true, sigma_eta_true])

Output:

estimated (sigma_epsilon, sigma_eta): [0.9144 0.5254]
stderr:                              [0.0424 0.0495]
true      (sigma_epsilon, sigma_eta): [1.0, 0.5]

Both variances land within about one standard error of their true values from a single 500-observation series — the frequency-domain likelihood is a consistent, asymptotically efficient estimator for this model, same as the time-domain (Kalman-filter-based) MLE would give, but computed entirely differently.

Custom spectral densities

whittle_estimation takes an arbitrary sdf_fn(lambda, theta) — the built-in whittle_local_level/whittle_local_linear_trend are thin wrappers around it. The econometrics.whittle API reference has a full worked example fitting a 4th-order Bloomfield exponential spectral density (Dzhaparidze, 1986) to a real 500-observation series, with an analytical Jacobian passed through for faster convergence — see the whittle2.py example there for the complete, live-synced source.

Where this comes from

Both examples above are simplified/simulated versions of the same model the original toolbox's Examples/ects/whittle1.m fits to a real 71-observation "Purse" series (Harvey, 1990, Forecasting, Structural Time Series and the Kalman Filter, pages 89-90) — see the econometrics.md API page for that exact translated example, which reproduces the textbook numbers directly.