VECMXResult#

class cultivars.multivariate.reduced_form.error_correction.VECMXResult(*, endog, names, order, trend, sigma_u, sigma_ml, resid, fittedvalues, design, llf, nobs, n_params, posterior=None, prior_label='none', rank, cointegration_trend, alpha, beta, gamma, short_run_deterministic, eigenvalues, coefficients, deterministic, exog, exog_names, impact, contemporaneous)[source]

Bases: VECMResult

A fitted conditional vector error-correction model.

The Pesaran-Shin-Smith specification: k_y modelled variables share a cointegrating space with k_x weakly exogenous integrated regressors, and only the modelled equations are estimated,

\[\Delta y_t = \alpha \beta' z_{t-1} + \sum_{i=1}^{p-1} \Gamma_i\, \Delta z_{t-i} + \Lambda_0\, \Delta x_t + \mu_t + u_t, \qquad z_t = (y_t', x_t')',\]

so beta spans the combined vector, Gamma is (k_y, k_y + k_x) because lagged differences of both blocks enter, and impact (\(\Lambda_0\)) carries the contemporaneous response to the exogenous differences. The regressors are weakly exogenous: they may share the long-run relations, but their own equations are not written and their adjustment to the disequilibria is assumed away rather than estimated.

Everything that reads the estimated regression is inherited and correct: params, bse, pvalues, conf_int, the residual diagnostics, the weak-exogeneity test, and the two-channel Granger test. What is not inherited is the half of the surface that needs a closed system. Impulse responses, the variance decomposition, the historical decomposition, the companion matrix, and the stability check all require a law of motion for every integrated variable in the system, and this model deliberately does not have one for x – that omission is the specification, not a gap. Those six raise rather than returning a number computed from the modelled block alone, which would look like an impulse response and be an artifact.

forecast() survives the same test and is kept: it needs a path for the exogenous block, not a model of it, so the caller supplies one exactly as for a VARX. to_varx() is the other thing a conditional model does have – its own equations in levels – and is what GVAR consumes to close several such units into one system.

Note

The inherited levels fields are placeholders here: coefficients is (0, k_y, k_y) and deterministic is (0, k_y), because the levels representation of a conditional model is not square and lives in to_varx() instead. n_common_trends counts the joint system, k_y + k_x - r, since the exogenous block carries its own unit roots. The rank may run to k_y rather than k_y - 1: every modelled variable cointegrating with the exogenous block is a valid conditional specification.

Variables:
  • exog – The weakly exogenous sample.

  • exog_names – Exogenous labels.

  • impact – (k_y, k_x) contemporaneous response to exogenous differences, zero-width when the model excludes it.

  • contemporaneous – Whether the current exogenous difference is included.

See also

  • VECMX – the model whose fit() returns this record.

  • VECMResult – the closed parent, whose short-run inference this result inherits and whose propagation surface it refuses.

  • VARXResult – the conditional levels model with the same refusals and the same to_varx() currency.

  • GVARResult – the closed system assembled from units like this one, where the refused members become available.

References

Pesaran, M. H., Shin, Y., & Smith, R. J. (2000). Structural analysis of vector error correction models with exogenous I(1) variables. Journal of Econometrics, 97(2), 293-343.

Harbo, I., Johansen, S., Nielsen, B., & Rahbek, A. (1998). Asymptotic inference on cointegrating rank in partial systems. Journal of Business & Economic Statistics, 16(4), 388-399.

Example

One modelled variable error-corrects toward an exogenous random walk while a second modelled variable wanders on its own. The conditional fit recovers the relation \(y_1 - x_1\), finds that y1 adjusts and y2 does not, and refuses an impulse response while forecasting along a supplied path for x:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> x = np.cumsum(rng.standard_normal(300))
>>> y1 = np.zeros(300)
>>> for t in range(1, 300):
...     y1[t] = y1[t - 1] + 0.4 * (x[t - 1] - y1[t - 1]) + 0.5 * rng.standard_normal()
>>> y = np.column_stack([y1, np.cumsum(rng.standard_normal(300))])
>>> res = VECMX(y, x[:, None], order=2, rank=1).fit()
>>> res.k_exog, res.n_common_trends, res.beta.shape
(1, 2, (3, 1))
>>> res.normalized_beta().ravel().round(1)
array([ 1.,  0., -1.])
>>> adjusts = [res.weak_exogeneity(name).pvalue < 0.05 for name in res.names]
>>> adjusts
[True, False]
>>> res.irf(4)
Traceback (most recent call last):
cultivars.exceptions.SpecificationError: a conditional vector error-correction model has ...
>>> path = res.forecast(6, exog_future=np.full((6, 1), x[-1]))
>>> gap = path[:, 0] - x[-1]
>>> path.shape, bool(abs(gap[-1]) < abs(gap[0]))
((6, 2), True)

Private methods#

_coefficient_stack()

Short-run coefficients, laid out exactly as the design columns.

_comparison_label()

Short specification label for a ranking table.

_from_fit(fit, model)

Assemble the public result from the internal fit and its model.

_lag_labels()

Lagged-difference column names over the joint vector.

_refuse(what)

Raise the shared explanation for a quantity the conditional model lacks.

_trailing_blocks()

The impact block, then the adjustment loadings, in design order.

_trailing_labels()

Contemporaneous exogenous differences, then the error-correction terms.