TCA is a Python library for Transmission Channel Analysis of structural macro-econometric models. It is a careful Python port of the MATLAB reference toolbox accompanying
Wegner, Lieb, Smeekes & Wilms (2024). Transmission Channel Analysis in Dynamic Models. arXiv:2405.18987.
with first-class plotting and reporting helpers.
- Author: Dr Merwan Roudane — merwanroudane920@gmail.com
- GitHub: https://github.com/merwanroudane/tca
For a structural shock
-
graphically, as a subset of paths in the DAG induced by the
systems-form representation
$x = Bx + \Omega \varepsilon$ , or - via potential outcomes, as an assignment vector that turns some intermediate dependencies on or off.
TCA is fully compatible with traditional impulse-response analysis: it
needs only the structural identification of the shock of interest plus a
transmission ordering (a permutation of the variables encoded in the
transmission matrix
pip install tca-channels
# Optional pretty terminals + DAG layouts:
pip install "tca-channels[all]"Editable install for development:
git clone https://github.com/merwanroudane/tca
cd tca
pip install -e .[dev]The distribution name on PyPI is tca-channels, but the Python package is imported as tca:
import tca
from tca.models import SVARPure-Python dependencies: numpy, scipy, pandas, matplotlib.
Optional: rich (coloured tables), networkx (alternative DAG layout),
statsmodels (alternative VAR estimators).
| Module | What lives there |
|---|---|
tca.q |
Q Boolean condition algebra; make_condition, make_condition_y |
tca.transmission |
transmission, transmission_BOmega, transmission_irfs, through, not_through |
tca.systems |
make_B, make_Omega, make_systems_form, to_transmission_irfs |
tca.models |
VAR, SVAR, LP, DSGE, Recursive, ExternalInstrument, InternalInstrument |
tca.viz |
plot_decomposition, plot_compare_decompositions, plot_irf_grid, plot_dag, plot_heatmap |
tca.tables |
decomposition_table, channel_share_table, render_rich, style_dataframe |
import numpy as np, pandas as pd
from tca.models import SVAR, Recursive
from tca import transmission, through, not_through
from tca.systems import make_systems_form, to_transmission_irfs
from tca.viz import plot_decomposition, plot_dag
from tca.tables import decomposition_table, render_rich
# 1) Estimate a structural VAR (recursive / Cholesky identification).
data = pd.DataFrame(np.random.RandomState(0).randn(200, 3),
columns=["x", "pi", "i"])
m = SVAR(data, p=2, identification=Recursive()).fit()
# 2) Choose a transmission ordering (here: x -> pi -> i).
order = m.define_order(["x", "pi", "i"])
# 3) Build channels:
direct = m.not_through("pi", horizons=[0], order=["x", "pi", "i"])
indirect = m.through ("pi", horizons=[0], order=["x", "pi", "i"])
# 4) Compute total IRFs and channel decompositions.
H = 12
irfs = m.irfs(H) # structural IRFs
B, Om = make_systems_form(m.Phi0, m.var.coeff_lags(), [], order, H)
eff_dir = transmission(from_=1, arr1=B, arr2=Om, q=direct, method="BOmega", order=order)
eff_ind = transmission(from_=1, arr1=B, arr2=Om, q=indirect, method="BOmega", order=order)
# 5) Pretty table + decomposition plot.
df = decomposition_table(idx_outcome=3, irfs_total=irfs,
channel_effects=[eff_dir, eff_ind],
channel_names=["Direct", "Indirect (via π)"])
render_rich(df, title="Demand shock → interest rate")
plot_decomposition(idx_outcome=3, irfs_total=irfs,
channel_effects=[eff_dir, eff_ind],
channel_names=["Direct", "Indirect (via π)"],
ylabel="Response of i").show()from tca import Q, make_condition
# Atomic variables follow systems-form ordering: x1 = first variable at horizon 0.
q = (Q(1) | Q(2)) & ~Q(3)
q = make_condition("(x1 | x2) & !x3")from tca import through, not_through
order = [1, 2, 3]
contemporaneous = through(1, [0], order) # paths through x_{1,0}
no_inflation = not_through(2, range(0, 13), order)
combined = through([1, 2], [0, 1], order) # joint conditionfrom tca import transmission
# Systems-form method.
eff = transmission(from_=1, arr1=B, arr2=Omega, q=q, method="BOmega", order=order)
# IRF method (works with local projections).
eff = transmission(from_=1, arr1=irfs_T, arr2=ortho_T, q=q, method="irf", order=order)eff is a 3-D array of shape (K, 1, H+1) when order is supplied.
from tca.models import VAR, SVAR, LP, DSGE, Recursive, ExternalInstrument
VAR(data, p=4).fit_and_select(max_p=8, ic="aic")
SVAR(data, p=4, identification=Recursive()).fit()
SVAR(data, p=4, identification=ExternalInstrument(z, normalize_to=2)).fit()
LP(data, shock=z, max_horizon=20, n_lags=4).fit()
# DSGE: provide a *linearised* model in VARMA form, an ABCD state-space
# (Morris 2016 conversion is built in), or pre-extracted Dynare structs.
DSGE.from_varma(Phi0, As=[A1], Psis=[])
DSGE.from_state_space(A, B, C, D, Sigma_e=Sigma_e)
DSGE.from_dynare(M_, options_, oo_)tca.viz provides four chart families:
plot_decomposition— stacked-bar decomposition of an IRF.plot_compare_decompositions— side-by-side comparison of two decompositions.plot_irf_grid— IRF panel with optional confidence bands.plot_heatmap— heatmap ofB/Omega/Phi.plot_dag— DAG of the systems form across horizons.
A consistent TCA_PALETTE and set_style() helper keep plots
publication-ready.
from tca.tables import decomposition_table, channel_share_table, render_rich, style_dataframe
df = decomposition_table(2, irfs_total, [eff_dir, eff_ind], ["Direct", "Indirect"])
render_rich(df, title="Demand shock → inflation") # coloured terminal table
style_dataframe(df, cmap="RdBu_r") # notebook / HTML / LaTeXrender_rich requires the optional rich dependency and falls back to
plain text otherwise.
If you use this library, please cite both the original framework and this implementation:
@article{wegner2024tca,
title = {Transmission Channel Analysis in Dynamic Models},
author = {Wegner, Enrico and Lieb, Lenard and Smeekes, Stephan and Wilms, Ines},
journal= {arXiv preprint arXiv:2405.18987},
year = {2024}
}
@misc{roudane2026tcapy,
title = {tca: Transmission Channel Analysis in Python},
author = {Roudane, Merwan},
year = {2026},
url = {https://github.com/merwanroudane/tca}
}MIT — see LICENSE.