cultivars.multivariate.large_dim.spillover#

Diebold-Yilmaz connectedness: who moves whom, in one table.

The spillover framework reads a fitted system’s generalized forecast-error variance decomposition as a weighted directed network. With \(\Psi_h\) the moving-average matrices of the fitted reduced form and \(\Sigma_u\) its innovation covariance, the share of variable \(i\)’s \(H\)-horizon forecast-error variance associated with shocks to \(j\) is

\[\theta_{ij}(H) = \frac{\sigma_{jj}^{-1} \sum_{h=0}^{H} (e_i' \Psi_h \Sigma_u e_j)^2} {\sum_{h=0}^{H} e_i' \Psi_h \Sigma_u \Psi_h' e_i}, \qquad \tilde\theta_{ij}(H) = 100\,\frac{\theta_{ij}(H)}{\sum_j \theta_{ij}(H)},\]

the generalized decomposition of Pesaran and Shin (1998), descended from Koop, Pesaran and Potter (1996), which conditions on one shock at a time without orthogonalizing and so assumes no variable ordering. The row-normalized \(\tilde\theta\) is the connectedness table: its off-diagonal mass is connectedness, column sums are the directional spillovers to others, row sums the spillovers from others, and their difference ranks transmitters against receivers. One number – the total spillover index, the average off-diagonal mass – summarizes how much of the system’s forecast uncertainty crosses variable boundaries at all, and its movement through rolling windows is the framework’s signature picture: connectedness spiking into crises.

Two commitments shape the surface. First, this is a view of a fitted reduced form, not an estimator, and it is built that way: Spillover constructs with any closed reduced-form result – an OLS VAR, a shrunk VAR, a sparse VAR estimated on a hundred series (the Demirer-Diebold-Liu-Yilmaz pipeline), the regime view of a Markov-switching VAR – and composes rather than re-estimates, so the network of a lasso VAR on wide panel is the same two lines as that of a three-variable OLS VAR. Second, the one caveat is inherited from the generalized decomposition itself and stated rather than hidden: generalized shocks are correlated, so the raw shares do not sum to one, the rows are normalized to 100, and the table is a relative attribution rather than an additive decomposition. The summary says so, and says to read the network as association, not causation.

Layout. Spillover validates its source against the ClosedSystemResult protocol from _core – names, k_endog, sigma_u and ma_representation are what it reads – refuses a horizon below one, and compute forms the decomposition directly from the moving-average matrices and the innovation covariance, cumulating \(\Psi_0, \ldots, \Psi_H\) so that horizon=H matches generalized_fevd(H)[-1] on a VAR result. SpilloverResult carries the table and its directional readings and takes its summary from _SummaryMixin. The estimators whose results feed it are vector_autoregression, bayesian, sparse and their neighbours; the sparsity-based network reading of the same system is graphical.

References

Diebold, F. X., & Yilmaz, K. (2009). Measuring financial asset return and volatility spillovers, with application to global equity markets. Economic Journal, 119(534), 158-171.

Diebold, F. X., & Yilmaz, K. (2012). Better to give than to receive: Predictive directional measurement of volatility spillovers. International Journal of Forecasting, 28(1), 57-66.

Demirer, M., Diebold, F. X., Liu, L., & Yilmaz, K. (2018). Estimating global bank network connectedness. Journal of Applied Econometrics, 33(1), 1-15.

Koop, G., Pesaran, M. H., & Potter, S. M. (1996). Impulse response analysis in nonlinear multivariate models. Journal of Econometrics, 74(1), 119-147.

Pesaran, M. H., & Shin, Y. (1998). Generalized impulse response analysis in linear multivariate models. Economics Letters, 58(1), 17-29.

Example

The same view on two estimators of one system: a four-variable VAR(1) in which y1 drives everyone, read through OLS and through the lasso. Both name y1 the transmitter, the lasso carries no more off-diagonal mass than OLS, and the identities hold on each:

>>> import numpy as np
>>> from cultivars.multivariate.reduced_form.vector_autoregression import VAR
>>> from cultivars.multivariate.large_dim.sparse import SparseVAR
>>> rng = np.random.default_rng(0)
>>> k, n = 4, 400
>>> a = 0.4 * np.eye(k)
>>> a[1:, 0] = 0.3
>>> y = np.zeros((n, k))
>>> for t in range(1, n):
...     y[t] = a @ y[t - 1] + rng.standard_normal(k)
>>> ols = Spillover(VAR(y, order=1).fit(), horizon=10).compute()
>>> lasso = Spillover(SparseVAR(y, order=1).fit(penalty="lasso"), horizon=10).compute()
>>> ols.transmitters()[0], lasso.transmitters()[0]
('y1', 'y1')
>>> bool(ols.net[0] > 20.0), bool(lasso.net[0] > 20.0)
(True, True)
>>> for spill in (ols, lasso):
...     print(bool(np.allclose(spill.table.sum(axis=1), 100.0)), bool(spill.net.sum() < 1e-9))
True True
True True
>>> bool(lasso.total <= ols.total), "Diebold-Yilmaz Connectedness" in str(lasso.summary())
(True, True)

Classes#

Spillover

Diebold-Yilmaz connectedness of any fitted closed reduced form.

SpilloverResult

A connectedness table and the directional readings it implies.