cultivars.multivariate.large_dim.factor_volatility#

Factor stochastic volatility: a moving covariance matrix built from a few paths.

A multivariate GARCH of even moderate dimension drowns in parameters; a factor model with stochastic volatility does not. Let \(r\) common factors drive the panel’s comovement and give each factor and each idiosyncratic error its own latent log-AR(1) variance,

\[y_t = \Lambda f_t + \varepsilon_t, \qquad f_t \sim N(0, H_t), \quad \varepsilon_t \sim N(0, D_t),\]
\[h_{j,t+1} = \mu_j + \phi_j (h_{jt} - \mu_j) + \sigma_j \xi_{jt}, \qquad H_t = \operatorname{diag}(e^{h_{1t}}, \ldots, e^{h_{rt}}),\]

and likewise for the \(k\) idiosyncratic log variances in \(D_t\). The \((k, k)\) covariance at every date is then \(\Sigma_t = \Lambda H_t \Lambda' + D_t\) – \(k + r\) volatility paths and one loading matrix, whatever \(k\) is (Pitt and Shephard, 1999; Chib, Nardari and Shephard, 2006). The time variation in correlations comes from the factor volatilities moving against the idiosyncratic ones, which is the mechanism, and it is readable rather than implicit: when a factor’s variance rises relative to the noise, every pair that loads on it correlates more.

Estimation is Bayesian, by an exact Gibbs sampler. Given everything else the factors are one Gaussian per period, the free loadings one weighted regression per row, and every log-variance path with its three parameters follows the Kim-Shephard-Chib mixture blocks the univariate model uses, so no step is approximate beyond the seven-component mixture for \(\log \chi^2_1\). What comes back is a posterior and the record holds it as draws; the conditional covariance, the correlations and the communalities are posterior means computed from those draws on demand.

Two commitments shape the surface. First, identification is stated, not hidden: \(\Lambda\) is lower triangular with a unit diagonal in its leading \(r\) rows, so factor \(j\) is scaled and signed by series \(j\), the order of the columns is a modelling choice the user makes and the summary says so. Second, mixing is reported rather than assumed. The sampler is the plain Gibbs scheme without the ancillary interweaving of Kastner, Fruehwirth-Schnatter and Lopes (2017), so loadings and factor scales mix slowly; every *_draws field shares the kept-draw axis and convergence assesses all of them, the fixed loadings are reported as never having moved rather than flagged, and the summary carries no likelihood or information criteria because a posterior has none.

Layout. FactorSV validates the panel through _validate_wide_panel and the factor count and labels on _FactorVolatilityModel in _internals; its _sample runs the sweep with _draw_factors, _draw_loading_rows, _draw_stationary_volatility_path and _draw_volatility_parameters from _core._samplers under a VolatilityPrior, and packs a _FactorVolatilityFit. FactorSVResult takes the summary from _SummaryMixin and the chain diagnostics from _ConvergenceMixin. The constant-variance factor model is dynamic_factor; the vector autoregression with stochastic volatility on its innovations is volatility; the univariate law each path follows is stochastic_volatility.

References

Pitt, M. K., & Shephard, N. (1999). Time-varying covariances: A factor stochastic volatility approach. In Bayesian Statistics 6 (pp. 547-570). Oxford University Press.

Chib, S., Nardari, F., & Shephard, N. (2006). Analysis of high dimensional multivariate stochastic volatility models. Journal of Econometrics, 134(2), 341-371.

Kim, S., Shephard, N., & Chib, S. (1998). Stochastic volatility: Likelihood inference and comparison with ARCH models. Review of Economic Studies, 65(3), 361-393.

Kastner, G., Fruehwirth-Schnatter, S., & Lopes, H. F. (2017). Efficient Bayesian inference for multivariate factor stochastic volatility models. Journal of Computational and Graphical Statistics, 26(4), 905-917.

Example

One factor whose variance swells in the middle of the sample; the posterior conditional correlation between two series that load on it rises there and falls back:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> n, k = 300, 4
>>> h = np.where((n // 3 <= np.arange(n)) & (np.arange(n) < 2 * n // 3), 2.0, 0.0)
>>> f = np.exp(h / 2) * rng.standard_normal(n)
>>> y = np.outer(f, [1.0, 0.8, 0.6, 0.4]) + 0.6 * rng.standard_normal((n, k))
>>> res = FactorSV(y, n_factors=1).fit(n_draws=300, n_burn=100, seed=0)
>>> corr = res.conditional_correlation()[:, 0, 1]
>>> middle = corr[n // 3 : 2 * n // 3].mean()
>>> edges = np.r_[corr[: n // 3], corr[2 * n // 3 :]].mean()
>>> bool(middle > edges + 0.1)
True
>>> vol = res.factor_volatility(0)[1]
>>> bool(vol[n // 3 : 2 * n // 3].mean() > 1.5 * vol[: n // 3].mean())
True

Classes#

FactorSV

Factor stochastic volatility, Chib-Nardari-Shephard.

FactorSVResult

The posterior of a factor stochastic-volatility model.