VECMXResult.granger_causality#

VECMXResult.granger_causality(cause, effect)[source]#

Test that one variable drives another through neither channel.

An error-correction model transmits through two routes and a test that checks one of them is not a test of Granger causality. The lagged differences carry the short-run route; the error-correction term carries the long-run route, where cause moves effect by shifting a disequilibrium that effect adjusts to. The null restricts both:

\[H_0 : \Gamma_{i}[e, c] = 0 \;\; (i = 1, \dots, p - 1) \quad\text{and}\quad \sum_{j=1}^{r} \alpha_{ej}\, \beta_{cj} = 0 .\]

The second restriction is linear in alpha once beta is treated as known, which is exactly the conditioning the rest of this result’s inference already rests on, so the pair goes into one Wald statistic rather than two that would have to be combined by hand.

Warning

Conditioning on beta makes the long-run restriction scale-free in it. With a single relation the restriction is \(\alpha_{e}\,\beta_{c} = 0\), and for any numerically non-zero \(\hat\beta_{c}\) – however small – its Wald contribution is exactly the squared z-statistic of \(\alpha_{e}\). The test therefore rejects whenever effect adjusts to the relation at all, whatever share of it cause carries. Read a rejection as “no short-run channel and effect does not adjust” and check \(\beta_{c} = 0\) – normalized_beta() and a restricted re-estimate – before calling it causality.

Parameters:
  • cause (str) – The variable whose influence is restricted.

  • effect (str) – The equation the restriction applies to.

Return type:

_WaldTest

Returns:

A _WaldTest with order - 1 + (rank > 0) degrees of freedom.

Raises:

SpecificationError – If either name is unknown, they are the same variable, or the specification has neither channel to restrict.

Example

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> common = np.cumsum(rng.standard_normal(300))
>>> y = np.column_stack([common, 2 * common + rng.standard_normal(300)])
>>> res = VECM(y, order=2, rank=1).fit()
>>> forward, back = res.granger_causality("y1", "y2"), res.granger_causality("y2", "y1")
>>> forward.df, bool(forward.pvalue < 0.001), bool(back.pvalue > 0.1)
(2, True, True)
>>> forward.null
'y1 does not Granger-cause y2'
>>> res.granger_causality("y1", "y1")
Traceback (most recent call last):
cultivars.exceptions.SpecificationError: a variable cannot Granger-cause itself.
>>> VECM(y, order=1, rank=0).fit().granger_causality("y1", "y2")
Traceback (most recent call last):
cultivars.exceptions.SpecificationError: this specification has no lagged ...