contagionchannels operationalises a two-stage research design that is deliberately ecumenical about identification. Stage 1 detects directional information flow between markets using Wavelet-Quantile Transfer Entropy (WQTE). Stage 2 attributes the detected flow to five economically meaningful channels using a battery of estimators that lean on different identifying assumptions. This vignette walks through the conceptual machinery one layer at a time and points to the canonical references for each component.
library(contagionchannels)A perennial confusion in the contagion literature is the elision of two quite different statistical questions:
Detection is a local, model-free question best answered with a flexible information-theoretic statistic; attribution is a structural question that requires explicit identifying restrictions. Conflating the two leads to the familiar problem in which a generic correlation spike is read as evidence of a particular causal channel. The two-stage design enforces a clean separation: the WQTE step asks only whether a flow exists; the IV/LP/Rigobon step asks which structural shock the flow embeds.
The Stage 1 statistic combines three classical ingredients: Schreiber’s (2000) transfer entropy, the maximal-overlap discrete wavelet transform (MODWT) for scale-localisation, and conditional quantile filtering for tail sensitivity.
For two stationary series \(X_t\) and \(Y_t\) Schreiber’s transfer entropy from \(X\) to \(Y\) is
\[ T_{X \to Y} \;=\; \sum p\!\left(y_{t+1}, y_t^{(k)}, x_t^{(\ell)}\right) \; \log \frac{p\!\left(y_{t+1} \mid y_t^{(k)}, x_t^{(\ell)}\right)} {p\!\left(y_{t+1} \mid y_t^{(k)}\right)}, \]
with \(y_t^{(k)} = (y_t, y_{t-1}, \dots, y_{t-k+1})\) and similarly for \(x_t^{(\ell)}\). Intuitively, \(T_{X \to Y}\) measures the bits per observation that knowing the past of \(X\) adds to the prediction of \(Y\) above and beyond \(Y\)’s own past.
We pre-filter both series with the MODWT (Daubechies LA8) at dyadic scales \(s \in \{1,2,3,4,5,6\}\), producing band-pass returns \(W_{X,t}^{(s)}\) that isolate fluctuations of period \([2^{s}, 2^{s+1}]\) trading days. Scale \(s=5\) covers the 32-64 day band, which lines up with the quarterly business-cycle horizon that motivates most of the channel proxies.
Following Bekiros and co-authors, transfer entropy is computed conditional on the source being in a quantile bin. Define \(X_t^{(\tau)} = \mathbf{1}\{X_t \le Q_X(\tau)\}\) and replace \(X\) with \(X^{(\tau)}\) in the entropy expression above. We use \(\tau = 0.50\) as the default since the paper’s primary interest is the typical (median) flow, not just the tail.
The WQTE point estimate \(\widehat{T}^{(s,\tau)}_{i \to j}\) is bias-corrected by Monte Carlo shuffling. We draw \(B = 100\) permutations of the source series, recompute the statistic, and subtract the mean. Statistical significance is then assessed by comparing the corrected statistic to the upper tail of the shuffled distribution.
F_mat <- compute_wqte_matrix(
returns = my_returns_xts,
scale = 5,
tau = 0.50,
n_cores = 1,
)References. Schreiber (2000) introduced transfer entropy (doi:10.1103/PhysRevLett.85.461). The wavelet implementation borrows from the waveslim package; quantile conditioning extends the Bekiros et al. quantile-cross-spectral approach.
A point estimate from any single estimator can be artefactual. Different methods buy identification with different assumptions:
| Method | Identifying assumption | Failure mode |
|---|---|---|
| IV/2SLS | Channel-specific instruments are exogenous | Weak/invalid instruments |
| LASSO IV | Sparsity in the first stage | Approximate-sparsity violation |
| LP | Conditional mean linearity at each horizon | Non-linearity, regime change |
| Rigobon | Variance of structural shocks shifts across regimes | Insufficient variance shift |
The package reports all four in parallel. A robust finding is one in which the dominant channel agrees across estimators; a fragile finding is one that survives only under a single identifying assumption.
Following the Stock-Watson (2018) external-instruments tradition, we use one external proxy per channel:
\[ \widehat{F}_{i\to j,t} \;=\; \alpha + \sum_{c=1}^{5} \beta_c \, C_{c,t} + u_{i\to j,t}, \qquad C_{c,t} \;=\; \pi_c Z_{c,t} + v_{c,t}. \]
The instruments \(Z_{c,t}\) are: lagged Baltic Dry Index for Trade; lagged FRA-OIS spread for Financial; lagged GPR-Daily for Geopolitical; lagged VIX innovation for Behavioral; lagged shadow-rate surprises for Monetary_Policy. Standard errors are heteroskedasticity-robust and clustered at the directional-link level. Over-identification is assessed via the Sargan-Hansen J-test; periods with high rejection rates (GFC 67.3%, COVID 100%, ESDC 65.5%) are reported but demoted to exploratory.
fit <- iv_2sls_attribute(
returns_period = returns_pc,
channels_period = channels_pc,
links = links_pc,
instruments = list(
Trade = "BDI_lag",
Financial = "FRAOIS_lag",
Geopolitical = "GPR_lag",
Behavioral = "VIX_innov_lag",
Monetary_Policy = "ShadowRate_surp_lag"
),
cluster_se = TRUE
)Reference: Stock and Watson (2018) doi:10.1111/ecoj.12593.
When the candidate instrument list is long the Belloni-Chernozhukov-Hansen (2014) post-LASSO IV estimator is preferred. The first stage runs
\[ C_{c,t} = \pi_c' Z_t + v_{c,t}, \]
where \(Z_t\) is a high-dimensional vector of candidate instruments (macroeconomic surprises, policy-rate residuals, commodity shocks, etc.) and \(\pi_c\) is recovered via LASSO with the iteratively-tuned penalty loadings of Belloni et al. The selected instruments feed the second-stage 2SLS, and inference is conducted under the standard sparsity conditions \(s \log(p) / n \to 0\).
fit_lasso <- lasso_iv_attribute(
returns_period = returns_pc,
channels_period = channels_pc,
links = links_pc,
candidate_Z = candidate_instrument_grid,
selection = "post_lasso"
)Reference: Belloni, Chernozhukov and Hansen (2014) doi:10.1093/restud/rdt044.
Jordà (2005) projections estimate impulse responses by direct OLS at each horizon \(h\):
\[ \widehat{F}_{i\to j,t+h} \;=\; \alpha_h + \beta_{c,h} \, C_{c,t} + \gamma_h' X_t + u_{i\to j,t+h}, \quad h \in \{1, 5, 22\}. \]
LP avoid the recursive bias of VAR-based IRFs and are robust to dynamic mis-specification, at the cost of larger standard errors at long horizons. We follow Stock-Watson and use Newey-West HAC standard errors with bandwidth \(h+1\).
lp_fit <- local_projections(
returns_period = returns_pc,
channels_period = channels_pc,
links = links_pc,
horizons = c(1, 5, 22),
controls = c("VIX_lag", "USD_lag")
)Reference: Jordà (2005) doi:10.1257/0002828053828518.
When external instruments are unavailable but shock variances shift across regimes, Rigobon (2003) provides identification through heteroskedasticity. Partition the sample into a high-volatility regime \(H\) and a low-volatility regime \(L\) (we use VIX terciles). The reduced-form covariance matrices satisfy
\[ \Omega_H - \Omega_L = A \,(\Sigma_H - \Sigma_L)\, A', \]
so that \(A\) is identified up to sign from the difference in covariance matrices, provided the structural shock-variance ratio differs across regimes. The package’s rigobon_id() function implements the GMM estimator with the analytical Jacobian.
rig_fit <- rigobon_id(
returns_period = returns_pc,
channels_period = channels_pc,
links = links_pc,
regime_split = "vix_high_low"
)Reference: Rigobon (2003) doi:10.1162/003465303772815727.
Identification-robust point estimates are not the end of the story: unobserved confounders may still be lurking. The Cinelli-Hazlett (2020) RV quantifies the minimum strength of an unobserved confounder, expressed as its partial \(R^2\) with both the treatment and the outcome, that would push the estimated coefficient to zero (the “tipping point” RV) or to its two-sided null (the “5% RV”):
\[ \mathrm{RV}_{q} \;=\; \frac{1}{2} \left( \sqrt{f_q^{4} + 4 f_q^{2}} - f_q^{2} \right), \quad f_q \;=\; \frac{|\hat{\theta}| - q \cdot \widehat{\mathrm{SE}}} {|\hat{\theta}| + q \cdot \widehat{\mathrm{SE}}}\cdot \sqrt{\mathrm{df}}. \]
A coefficient with \(\mathrm{RV} \ge 0.20\) is one that no plausible single-confounder explanation can overturn under reasonable benchmarks.
rv <- cinelli_hazlett_rv(
theta = fit$shares,
se = fit$se,
df = fit$df_residual
)Reference: Cinelli and Hazlett (2020) doi:10.1111/rssb.12348.
The decision rule used in the paper is intentionally conservative:
Out of the eight sub-periods, only Pre-Crisis (Financial) and ESDC (Financial) clear the bar for robust; the remaining six are reported as fragile, with the exploratory flag attached to GFC and COVID.
This explicit grading is what makes the package useful for empirical work: the user is never invited to mistake a single-method point estimate for a robust attribution claim. The codebase, vignettes, and the report objects returned by run_contagion_pipeline() propagate the identification status into every downstream summary table and figure.
sessionInfo()
#> R version 4.1.2 (2021-11-01)
#> Platform: x86_64-pc-linux-gnu (64-bit)
#> Running under: Ubuntu 22.04.5 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.10.0
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0
#>
#> locale:
#> [1] LC_CTYPE=en_IN LC_NUMERIC=C LC_TIME=en_IN
#> [4] LC_COLLATE=C LC_MONETARY=en_IN LC_MESSAGES=en_IN
#> [7] LC_PAPER=en_IN LC_NAME=C LC_ADDRESS=C
#> [10] LC_TELEPHONE=C LC_MEASUREMENT=en_IN LC_IDENTIFICATION=C
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] dplyr_1.1.4 xts_0.14.1 zoo_1.8-14
#> [4] contagionchannels_0.1.3
#>
#> loaded via a namespace (and not attached):
#> [1] bslib_0.9.0 compiler_4.1.2 pillar_1.11.1 jquerylib_0.1.4
#> [5] tools_4.1.2 digest_0.6.37 tibble_3.3.0 jsonlite_2.0.0
#> [9] evaluate_1.0.5 lifecycle_1.0.5 lattice_0.20-45 pkgconfig_2.0.3
#> [13] rlang_1.2.0 Matrix_1.5-4.1 igraph_2.2.0 cli_3.6.6
#> [17] yaml_2.3.10 parallel_4.1.2 SparseM_1.84-2 xfun_0.53
#> [21] fastmap_1.2.0 multitaper_1.0-17 knitr_1.50 generics_0.1.4
#> [25] sass_0.4.10 vctrs_0.6.5 MatrixModels_0.5-1 tidyselect_1.2.1
#> [29] grid_4.1.2 glue_1.8.0 R6_2.6.1 survival_3.2-13
#> [33] waveslim_1.8.5 rmarkdown_2.30 magrittr_2.0.4 htmltools_0.5.8.1
#> [37] MASS_7.3-55 splines_4.1.2 quantreg_6.1 cachem_1.1.0