GVARResult.weak_exogeneity_test#

GVARResult.weak_exogeneity_test(unit)[source]#

Test that a unit’s foreign aggregates ignore its disequilibria.

The condition the whole construction rests on. If a unit’s foreign aggregate responds to that unit’s own error-correction terms then the aggregate is not weakly exogenous, the unit’s least-squares estimates are inconsistent, and every global quantity assembled from them inherits the bias.

The auxiliary regression puts each foreign variable’s change on the unit’s own short-run design with the contemporaneous foreign block removed, and tests that the error-correction coefficients are jointly zero,

\[\Delta y^*_{jt} = c + \gamma'\, \hat\xi_{i,t-1} + \text{(lagged differences)} + \epsilon_t, \qquad H_0 : \gamma = 0,\]

with \(\hat\xi_{i,t-1} = \hat\beta_i' z_{i,t-1}\) the unit’s estimated disequilibria and the statistic \(n\,(\mathrm{RSS}_0 - \mathrm{RSS}_1)/\mathrm{RSS}_1\) referred to \(\chi^2(r)\). Removing the contemporaneous block is not a detail: leaving it in would put the dependent variable on both sides, and testing the unit’s residuals instead – the obvious first idea – has no power at all, because least squares already made them orthogonal to the foreign block by construction.

Parameters:

unit (str) – One of unit_names.

Return type:

tuple[_WaldTest, ...]

Returns:

One test per foreign variable, in that unit’s exogenous order.

Raises:

SpecificationError – If the unit is unknown, was fitted in levels rather than as an error-correction model, or has rank zero.

Note

The test conditions on the unit’s own estimate of \(\beta_i\), so it is only as good as that estimate: a unit whose own variables do not error-correct identifies its cointegrating vector weakly, and its test should be read with that in mind. In the example the satellite error-corrects, its vector is well identified, the world price ignores the gap, and the test passes; for the world unit the satellite does respond to the gap and the test rejects, which is the right answer – a unit that conditions on a variable adjusting to its own disequilibrium is misspecified as a conditional model, and only the satellite’s estimates can be trusted as they stand.

Example

>>> import numpy as np
>>> from cultivars.multivariate.reduced_form.error_correction import VECMX
>>> rng = np.random.default_rng(0)
>>> world, sat = np.zeros(400), np.zeros(400)
>>> for t in range(1, 400):
...     world[t] = world[t - 1] + rng.standard_normal()
...     gap = world[t - 1] - sat[t - 1]
...     sat[t] = sat[t - 1] + 0.3 * gap + 0.5 * rng.standard_normal()
>>> units = [
...     VECMX(world[:, None], sat[:, None], order=1, rank=1).fit(),
...     VECMX(sat[:, None], world[:, None], order=1, rank=1).fit(),
... ]
>>> swap = np.array([[0.0, 1.0], [1.0, 0.0]])
>>> names = ("world", "sat")
>>> res = GVAR(units, swap, variable_labels=["p", "p"], unit_names=names).solve()
>>> (test,) = res.weak_exogeneity_test("sat")
>>> test.df, bool(test.pvalue > 0.05), test.null
(1, True, 'x1 is weakly exogenous for sat')
>>> (test,) = res.weak_exogeneity_test("world")
>>> bool(test.reject(alpha=0.05))
True
>>> from cultivars.multivariate.reduced_form.vector_autoregression import VARX
>>> levels = VARX(world[:, None], sat[:, None], order=1, exog_order=1).fit()
>>> mixed = GVAR([levels, units[1]], swap, variable_labels=["p", "p"]).solve()
>>> mixed.weak_exogeneity_test("unit1")
Traceback (most recent call last):
cultivars.exceptions.SpecificationError: unit 'unit1' was fitted in levels, which ...