cultivars.multivariate.large_dim.student#
The Student-t Bayesian VAR: fat tails as a model, not a casualty.
Macroeconomic samples carry a handful of dates – 2008, 2020 – that a Gaussian VAR can only accommodate by inflating the covariance for every other date, which is how a few observations end up owning the error bands. Student-t innovations (Chiu, Mumtaz and Pinter, 2017) treat those dates as what they are: draws from the same system’s fatter tail. The model is
and the second line is how it is estimated (Geweke, 1993): each date carries a latent precision weight, and conditional on the weights the model is exactly the conjugate Normal-inverse-Wishart VAR on rows rescaled by \(\sqrt{w_t}\). The Gibbs sampler therefore alternates three exact conditionals – \((B, \Sigma)\) from the weighted conjugate update, the weights from \(\mathrm{Gamma}\bigl((\nu + k)/2, (\nu + u_t' \Sigma^{-1} u_t)/2\bigr)\), and \(\nu\) from its conditional on a fixed grid – and inherits the conjugate model’s prior machinery unchanged, dummy observations included. The reported \(\Sigma\) is the t scale; the innovation covariance is \(\Sigma\,\nu/(\nu - 2)\).
Two commitments shape the surface. First, the tail index can be stated or
learned, and when learned it is learned exactly: nu is drawn from its
conditional evaluated on the grid 2 .. 29, 30, 35, 40, 50, 60, 80, 100
under a prior uniform over grid points, a step with no tuning parameter
and no acceptance rate. The grid is coarse above thirty because the t and
the Gaussian are indistinguishable there; on data whose innovations are
Gaussian the posterior settles in the teens and twenties at macro sample
sizes, which is the model’s way of saying the tails were not needed, and
only a long Gaussian sample pushes it to the grid’s top. Second, the
latent weights are reported, as StudentBVARResult.weight_mean – a
per-date outlier map, and often the most readable diagnostic the fit
produces: the dates the t distribution absorbed are exactly the dates with
small weights, and StudentBVARResult.outlier_dates() lists them.
Deliberately absent: the marginal likelihood. The t likelihood breaks the conjugacy that made the Gaussian model’s evidence a closed form, and a simulated stand-in would not deserve the name; rank tail specifications by predictive performance instead.
Layout. StudentBVAR defaults the prior to
NormalInverseWishartPrior and validates
on _StudentBayesianVectorAutoRegressionModel in _internals, which
extends the conjugate _BayesianVectorAutoRegressionModel; its
_fit_student runs the three-block sampler, drawing (B, Sigma)
through _draw_conjugate on the reweighted rows and nu through
_draw_degrees on _STUDENT_DF_GRID from _core, and packs a
_VectorStudentFit. StudentBVARResult extends
_VectorPosteriorDrawsResult – credible intervals, impulse responses
with posterior bands, the predictive, the stability share, the chain
diagnostics – and overrides its three noise hooks so that the
predictive, simulate() and
posterior_replications() all draw t innovations.
The Gaussian conjugate model is
bayesian; the other account of
fat marginal tails, through time-varying volatility, is
volatility.
References
Chiu, C.-W. J., Mumtaz, H., & Pinter, G. (2017). Forecasting with VAR models: Fat tails and stochastic volatility. International Journal of Forecasting, 33(4), 1124-1143.
Geweke, J. (1993). Bayesian treatment of the independent Student-t linear model. Journal of Applied Econometrics, 8(S1), S19-S40.
Example
A bivariate VAR(1) with \(t_4\) innovations next to one with Gaussian innovations: the tail index separates them, and the weight map on the fat-tailed sample points at its largest residuals:
>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> fat, normal = np.zeros((200, 2)), np.zeros((200, 2))
>>> for t in range(1, 200):
... w = rng.gamma(2.0, 0.5)
... fat[t] = 0.5 * fat[t - 1] + rng.standard_normal(2) / np.sqrt(w)
... normal[t] = 0.5 * normal[t - 1] + rng.standard_normal(2)
>>> heavy = StudentBVAR(fat, order=1).fit(n_draws=400, n_burn=200, seed=0)
>>> light = StudentBVAR(normal, order=1).fit(n_draws=400, n_burn=200, seed=0)
>>> bool(heavy.df < 10.0 < light.df)
True
>>> bool(heavy.outlier_dates().size > light.outlier_dates().size)
True
>>> worst = int(np.argmax(np.abs(heavy.resid).max(axis=1)))
>>> bool(worst in heavy.outlier_dates())
True
>>> ratio = heavy.innovation_covariance / heavy.sigma_u
>>> bool(np.allclose(ratio, heavy.df / (heavy.df - 2.0)))
True
Classes#
Bayesian VAR with Student-t innovations, after Chiu, Mumtaz and Pinter. |
|
A fitted Student-t Bayesian VAR: the tails carry a posterior too. |