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
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#
Diebold-Yilmaz connectedness of any fitted closed reduced form. |
|
A connectedness table and the directional readings it implies. |