using Pkg;
Pkg.add("TransmissionChannelAnalysis")
using TransmissionChannelAnalysis
TransmissionChannelAnalysis.jl
TransmissionChannelAnalysis.jl
provides a suite of functions for conducting transmission channel analysis (TCA) in Julia. It includes both standard methods—which are sufficient for most users—and more advanced, customisable techniques.
This overview focuses on the standard methods. Advanced options are described in the Advanced Section, and implementation details can be found in the Internals Section.
Before performing any transmission analysis, add TransmissionChannelAnalysis.jl
to Julia and load it.
A typical transmission channel analysis workflow consists of the following steps:
- Define and estimate a model
- Compute total effects
- Construct a transmission matrix
- Define transmission channels
- Compute transmission effects
- Visualise results
The sections that follow document the functions available for each of these steps.
1. Defining and estimating a model.
The first step in transmission channel analysis is to define a model. This model will be used to derive total dynamic causal effects (impulse response functions) and to decompose these effects into effects along transmission channels. Currently supported are the following model types:
- Vector Autoregressions (VARs) for baseline dynamic modelling, estimated via OLS without structural identification.
- Structural VARs (SVARs) with recursive and internal identification schemes.
- Local projections with recursive and external instrument identification schemes.
Each model class extends the abstract type Model
. Detailed descriptions of each model follow.
Vector Autoregressions (VARs)
A VAR model can be defined using the VAR
type and its constructor as shown below:
The constructor requires a dataset of type DataFrame
, with each column representing a variable and each row an observation, ordered from earliest to latest. For example:
# data is some dataset of type DataFrame
= 2 # lag length
p = VAR(data, p) model
You can include deterministic trends by specifying the trend_exponents
keyword-argument:
= 2
p = VAR(data, p; trend_exponents = 0:1) model
Here, 0:1
indicates a constant (\(t^0\)) and a linear trend (\(t^1\)). For constant, linear, and quadratic trends use 0:2
, or [0,2]
to skip the linear term.
To estimate the VAR via OLS, call fit!
:
fit!(model)
Retrieve estimates with coeffs
, fitted
, and residuals
:
# All coefficients
coeffs(model)
# Excluding constants/trends
coeffs(model, true)
# Fitted values
fitted(mdoel)
# Residuals
residuals(model)
To select the optimal lag length automatically, use fit_and_select!
. This method fits VAR
models for every lag length from 0 up to the specified maximum, evaluates each using the chosen information criterion (AIC by default), and returns the model with the lowest criterion value along with a table of lag lengths and their corresponding scores:
= VAR(data, 20)
model = fit_and_select!(model) model_best, ic_table
fit_and_select!
supports AIC (aic
), BIC/SIC (bic
/sic
), and HQC (hqc
). For example, to use the Bayesian Information Criteria (BIC), call:
= VAR(data, 20)
model = fit_and_select!(model, bic) model_best, ic_table
To compute information criteria for an already estimated model over its own sample, call aic
, bic
, sic
, or hqc
on the model. Note these values may differ from those returned by fit_and_select!
, which evaluates ICs over a common sample across all models:
aic(model) # computes the AIC of the model
bic(model) # computes the BIC of the model
sic(model) # computes the SIC of the model
hqc(model) # computes the HQC of the model
Structural Vector Autoregressions (SVARs)
An SVAR can be defined using the SVAR
class and its constructor as documented below:
The constructor requires a dataset of type DataFrame
, with each column representing a variable and each row an observation, ordered from earliest to latest. For example:
= 4 # lag length
p = SVAR(data, p) model
You may include deterministic trends as for the VAR case, using the trend_exponents
keyword-argument:
= SVAR(data, p; trend_exponents=0:1) model
To estimate the structural model, an identification method must be provided. Only recursive identification (Recursive
) is supported for full structural identification. Note that InternalInstrument
methods can be used to identify impulse responses to a single shock; these are documented in a later section. Fit the model as follows:
fit!(model, Recursive())
After estimation, coefficients, fitted values, and residuals can be obtained analogenously to the VAR
using coeffs
, fitted
, and residuals
, respectively.
# All coefficients (A0, A_plus)
coeffs(model)
# Excluding constants/trends
coeffs(model, true)
# Fitted values
fitted(mdoel)
# Residuals
residuals(model)
You can select the lag length automatically via fit_and_select!
, which first determines the best reduced‑form VAR
up to the maximum lag and then applies the chosen identification method:
Using AIC (default) with a maximum of twenty lags:
= SVAR(data, 20)
model = fit_and_select!(model, Recursive()) model_best, ic_table
Or using HQC for model selection:
= SVAR(data, 20)
model = fit_and_select!(model, Recursive(), hqc) model_best, ic_table
All VAR
information‑criterion functions may be supplied to choose the optimal SVAR
.
Local Projections
A local projection model can be specified using the LP
class and its constructor:
The constructor requires:
- A dataset of type
DataFrame
- A
treatment
variable (either the shock itself or the instrumented variable) - The number of lags (
p
) - A vector of horizons (
horizons
)
For example, to estimate horizons \(0\)–\(20\) with 4 lags for treatment variable Y1
:
= LP(data, :Y1, 4, 0:20) model
By default, each regression includes a constant term. To omit the constant, set the include_constant
keyword-argument to false
:
= LP(data, :Y1, 4, 0:20; include_constant=false) model
Use the fit!
method to estimate the projections:
fit!(model)
By default, fit!
applies a recursive (conditional ignorability) assumption. These estimates are structural only if the true shock satisfies this assumption; otherwise, they should be treated as reduced‐form.
Available identification schemes:
Recursive
(default)ExternalInstrument
(for external‐instrument IV)
To use an external instrument, first instantiate ExternalInstrument
with the treatment name and an instrument dataset, then supply it to fit!
:
= ExternalInstrument(:Y1, data_instruments)
method fit!(model, method)
After estimation, extract the results (note the differing dimensions) via:
coeffs
: (response variable × regressors × horizons)fitted
,residuals
: (observations × variables × horizons)
# All coefficients
coeffs(model)
# Excluding the constant
coeffs(model, true)
# Fitted values
fitted(model)
# Residuals
residuals(model)
To select the lag order automatically, use fit_and_select!
, which fits VAR
s internally to choose the optimal p
:
All VAR
information‑criterion functions (e.g., aic
, bic
, sic
, hqc
) may be used for LP
lag selection. For example, using BIC:
= LP(data, :Y1, 20, 0:20)
model = fit_and_select!(model, Recursive(), bic) model_best, ic_table
2. Obtaining the effects – structural IRFs
Once a model is defined, you can compute total dynamic causal effects—impulse response functions (IRFs)—using the IRF
method. The IRF function always requires a maximum horizon (max_horizon
) for computation. For reduced‐form models, you may also supply an IdentificationMethod
to derive structural IRFs.
Details on IRF computation for all models are provided below.
VARs
Obtain reduced‐form IRFs from a VAR model by specifying the maximal horizon:
= 20
max_horizon = IRF(model, max_horizon) irf_obj
Here, irf_obj
is of type IRF
holding both the IRF array and related metadata:
Printing irf_obj
displays a 3D array with dimensions:
- Response variable
- Shock
- Horizon (starting at 0 for contemporaneous effect)
To retrieve the raw IRF array, call:
irf_obj.irfs
To compute structural IRFs, supply an IdentificationMethod
. Supported methods include recursive (Recursive
) and internal‐instrument (InternalInstrument
) schemes:
For a recursive identification:
= IRF(model, Recursive(), max_horizon) irf_obj
To use an internal instrument, specify a normalising_variable
, then optionally override the instrument
(default: first variable) or the normalising_horizon
(default: 0 for contemporaneous effect):
= :Y1
normalising_variable = InternalInstrument(normalising_variable)
method = IRF(model, method, max_horizon) irf_obj
# Changing the instrument to be Y2 and the normalising variable to be Y3
= InternalInstrument(:Y3; instrument=:Y2) method
# Changing the normalising horizon to period 1
= InternalInstrument(:Y3; instrument=:Y2, normalising_horizon=1) method
SVARs
Impulse response functions can be obtained from an SVAR
using the IRF
function, analogous to the VAR
case. Since an SVAR is already structurally identified, IRF
returns structural IRFs directly:
# model is a SVAR
= IRF(model, max_horizon) # will be structural IRFs irf_obj
The IRF
method for SVAR
s requires a fully identified SVAR
. Internal instruments (via InternalInstrument
) cannot be applied to an SVAR
directly. To use internal instruments for IRF identification, first define a reduced‐form VAR
, apply the instrument there, and then compute IRFs:
= VAR(data, p);
model = :Y2;
normalising_variable = InternalInstrument(normalising_variable);
method = IRF(model, method, max_horizon) irf_obj
Local Projections
Impulse response functions can be obtained from an estimated LP
model using IRF
and providing an identification method. Available methods are:
- Recursive identification via
Recursive
- External instruments via
ExternalInstrument
For example, using Recursive
:
# model is an LP model
= IRF(model, Recursive(), max_horizon) irf_obj
Or using external instruments
= ExternalInstrument(:Y1, data_instruments)
method = IRF(model, method, max_horizon) irf_obj
3. Defining a transmission matrix.
The next step in a transmission channel analysis is to define the transmission matrix, which specifies the ceteris paribus ordering of variables in the chosen equilibrium representation. The simplest way is to list the variables in the desired order as a vector of Symbol
. For example, if your data contains Y1
, Y2
, Y3
, and Y4
, and you wish to order them as Y4
, Y2
, Y1
, Y3
, define:
= [:Y3, :Y2, :Y1, :Y3] transmission_order
Advanced methods for constructing transmission matrices are covered in the Advanced Methods Section.
4. Definining transmission channels.
With the transmission matrix in place, you can specify transmission channels of interest via the helper functions through
and not_through
. These functions return a transmission condition Q
that you can combine to define complex transmission channels. More advanced methods for defining transmission channels are documented in the Advanced Methods Section.
through
Use through
to define channels that pass through specified variables at given horizons.
Examples (for any model — VAR, SVAR, or LP — with transmission_order
or a transmission matrix as described above):
- Channel through
Y1
at horizons 0-3:
= through(model, :Y1, 0:0, transmission_order) q
- Channel through
Y1
andY2
contemporaneously:
= through(model, [:Y1, :Y2], 0:0, transmission_order) q
- Channel through
Y1
at horizon 0 andY2
at horizons 0–1:
= through(model, [:Y1, :Y2], [0:0, 0:1]) q
not_through
Use not_through
to define channels that do not go through specified variables at given horizons.
Examples (for any model — VAR, SVAR, or LP — with transmission_order
a transmission matrix as described above):
- Channel not through
Y1
at horizons 0-20:
= not_through(model, :Y1, 0:20, transmission_order) q
- Channel not through
Y1
and not throughY2
at horizons 0-20:
= not_through(model, [:Y1, :Y2], 0:20, transmission_order) q
- Channel not through
Y1
contemporaneously and not throughY2
at horizon 1:
= not_through(model, [:Y1, :Y2], [0:0, 1:1], transmission_order) q
Combining through and not_through
Both through
and not_through
return a transmission condition Q
, which can be combined using logical operators:
- AND (
&
): both conditions must be satisfied. - OR (
|
): at least one condition must be satisfied. - NOT (
!
): negates a condition.
Examples:
- Channel through
Y1
contemporaneously but not throughY2
contemporaneously:
= through(model, :Y1, 0:0, transmission_order)
q1 = not_through(model, :Y2, 0:0, transmission_order)
q2 = q1 & q2 q
- Channel through
Y1
contemporaneously or not throughY2
contemporaneously:
= through(model, :Y1, 0:0, transmission_order)
q1 = not_through(model, :Y2, 0:0, transmission_order)
q2 = q1 | q2 q
- Channel not through
Y1
contemporaneously (both ways are equivalent):
= through(model, :Y1, 0:0, transmission_order)
q1 = !q1
q = not_through(model, :Y1, 0:0, transmission_order) q
- Channel through
Y1
in at least one period between horizons 0-20:
= not_through(model, :Y1, 0:20, transmission_order)
q = !q q
By combining through
and not_through
with logical operators, you can flexibly construct any complex transmission channel you require.
5. Obtaining transimssion effects.
Once you have defined a model
(VAR, SVAR, or LP), the transmission
method computes transmission effects along your specified channel. Below are the details for each model type.
For VAR
and LP
, or for SVAR
models that have not yet been structurally estimated, you must provide an identification method (since at least one structural shock is required). As outlined above, this can be Recursive
for SVAR
and LP
, InternalInstrument
for SVAR
, or ExternalInstrument
for LP
.
For example, to compute the effects of the first structural shock up to max_horizon
, using a transmission matrix transmission_order
and a transmission condition q
:
= transmission(
effects # VAR, SVAR, or LP
model, # identification method
method, 1, # shock index
# transmission condition
q, # transmission matrix
transmission_order, # maximum horizon
max_horizon )
The returned effects
array has dimensions:
- Response variables (in original data order)
- Transmission effect of the chosen shock
- Horizons (starting at 0)
If your SVAR
model is already structurally identified (fitted with a structural identification scheme), you may omit the identification argument:
= transmission(model, 1, q, transmission_order, max_horizon) effects