Heavy-Tailed Observation Errors#
Macroeconomic samples contain observations that a Gaussian VAR cannot accommodate: 2020Q2, the 2008 collapse, a devaluation, a data revision. Under Gaussian errors a single such quarter pulls the coefficient estimates and inflates the estimated covariance for the whole sample. Student-t observation errors downweight it automatically, inside the model, with no dummies to pick by hand.
When to reach for it#
The residuals from a Gaussian fit have a few very large values rather than a uniformly wide spread.
You would otherwise be adding outlier dummies, and would rather not choose the dates yourself.
The estimated innovation covariance looks implausibly large relative to the bulk of the sample.
If instead the volatility level drifts over the sample — quiet decades and turbulent ones — that is a stochastic-volatility problem, not a heavy-tails problem. The two cannot currently be combined (see Limitations).
Fitting#
The string shorthand infers the degrees of freedom from the data:
Fixing them instead is the robust choice on short samples, where nu is only
weakly identified:
nu must be strictly greater than 2 — below that the t has infinite variance
and forecast bands, variance decompositions and the innovation covariance all
stop being defined. Values around 4–6 are aggressively robust; above roughly 30
the fit is indistinguishable from Gaussian.
Reading the posterior for nu#
The degrees of freedom land in the posterior as nu under both
parameterisations, so the same code reads them either way:
import arviz as az
az.summary(fitted.idata, var_names=["nu"])
Under inference the free parameter is nu_excess and nu = 2 + nu_excess.
The shift means the prior has zero density at the boundary, so the sampler is
never dragged toward the infinite-variance edge. A posterior median below about
10 says the data genuinely want heavy tails; a median that has drifted up toward
the prior mean (22 by default) says the sample carries little information about
the tail, and a fixed nu is the more honest specification.
If you see divergences
nu and the innovation scale trade off against each other, which can make the
posterior geometry awkward on short samples. Raise target_accept to 0.9, or
fix nu rather than inferring it.
What changes, and what does not#
Under the t, Ω = L Lᵀ is the scale matrix, not the covariance. The
distinction propagates predictably:
Quantity |
Effect |
|---|---|
|
Returns Ω unchanged — the scale matrix, no longer the covariance |
|
New accessor; returns |
|
Innovations follow the t, so bands have fatter tails at the same interquartile width |
|
A “unit shock” is one scale unit = |
|
Exactly unchanged — shares are ratios, and the scale cancels |
|
Exactly unchanged — shocks are backed out and re-propagated |
|
Exactly unchanged for zero edits; non-zero |
|
Raises |
|
Raises |
To restore the one-standard-deviation shock convention for impulse responses,
scale them by sqrt(nu/(nu−2)):
Is it worth it? Comparing against the Gaussian fit#
Both fits carry a pointwise log-likelihood, so ArviZ compares them directly:
import arviz as az
from impulso import VAR, NUTSSampler
sampler = NUTSSampler(nuts_sampler="pymc")
gaussian = VAR(lags=4).fit(data, sampler=sampler)
student = VAR(lags=4, error_dist="student_t").fit(data, sampler=sampler)
az.compare({"gaussian": gaussian.idata, "student_t": student.idata})
Sampler backend
nuts_sampler="pymc" is required here. The nutpie backend ignores the
idata_kwargs that request the log_likelihood group, so a nutpie-sampled
InferenceData has nothing for az.compare to read. This is a pre-existing
quirk of the nutpie integration, not specific to Student-t errors.
Limitations#
Two combinations are refused outright rather than approximated.
Stochastic volatility. VAR(volatility="sv", error_dist="student_t") raises
at construction. The degrees of freedom and the log-volatility innovation
variance both absorb outliers, so the two are weakly identified jointly and
NUTS mixes poorly. Pick the mechanism that matches the problem: drifting
volatility level → stochastic volatility with Gaussian errors; isolated extreme
observations → constant volatility with Student-t errors.
Conditional forecasts and structural scenarios. Both raise
NotImplementedError under heavy tails. The Waggoner–Zha constrained draw and
the ADPRR three-way partition are Gaussian conditional-law results, and the
plausibility statistic’s χ² reference assumes Gaussian shocks. Under a t the
conditional law has updated degrees of freedom and a Mahalanobis-inflated scale,
and the plausibility reference becomes an F/Hotelling statistic. Returning a
half-valid scenario would be worse than the error, because nothing in the output
would look wrong. Unconditional density forecasts via forecast() and in-sample
counterfactual() are both fully valid under the t.
See ADR-0007 for the full derivation of which quantities change and which are exactly invariant.