cultivars.diagnostics.cointegration#
Cointegration rank: the Johansen trace and maximum-eigenvalue sequences.
A system of \(k\) integrated series has cointegrating rank \(r\) when \(r\) independent linear combinations of it are stationary, and the rank is the first thing a vector error-correction model must know. Johansen’s (1991) reduced-rank regression estimates it from the squared canonical correlations \(\hat\lambda_1 \ge \cdots \ge \hat\lambda_k\) between the differences and the lagged levels, each corrected for the short-run dynamics, and tests it with two sequences read down from \(r = 0\),
the first against the alternative of full rank and the second against rank \(r + 1\). The conventional reading stops at the first \(r\) not rejected. Neither statistic has a standard limit: both are functionals of a \((k - r)\)-dimensional Brownian motion whose form depends on where the deterministic terms sit relative to the cointegrating space, the five cases of Johansen (1995), and, when weakly exogenous integrated regressors are conditioned on, on the number of them (Pesaran, Shin & Smith 2000). The record here carries the eigenvalues, both sequences, and empirical p-values from that limit simulated for the case and the exogenous count in hand.
Two commitments shape the surface. First, the test is not a function in
this module. The eigenvalue problem belongs to the model that solves
it, so rank_test()
produces the record, independent of the rank the model was constructed
with; this module is where the record is documented, its public home
under the name the rest of the package refers to. Second, the p-values
are simulated, not tabulated, and say so: the record carries the number
of replications, so the smallest resolvable p-value is visible, and the
table names the deterministic case the null was drawn under, because a
conditional statistic read against the unconditional table would
over-reject.
Layout. JohansenRankTest is the frozen record, with
selected_rank() for the sequential reading under
either statistic and to_table() for the summary.
It is defined in _internals as the return type of the
error-correction models’ rank_test and re-exported here. The
statistics are computed in _internals from the model’s
cointegration moments; the null is simulated by _core’s
simulate_cointegration_null, one Brownian-functional draw per
replication for each k - r.
References
Johansen, S. (1991). Estimation and hypothesis testing of cointegration vectors in Gaussian vector autoregressive models. Econometrica, 59(6), 1551-1580.
Johansen, S. (1995). Likelihood-Based Inference in Cointegrated Vector Autoregressive Models. Oxford University Press.
Osterwald-Lenum, M. (1992). A note with quantiles of the asymptotic distribution of the maximum likelihood cointegration rank test statistics. Oxford Bulletin of Economics and Statistics, 54(3), 461-472.
MacKinnon, J. G., Haug, A. A., & Michelis, L. (1999). Numerical distribution functions of likelihood ratio tests for cointegration. Journal of Applied Econometrics, 14(5), 563-577.
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.
Reinsel, G. C., & Ahn, S. K. (1992). Vector autoregressive models with unit roots and reduced rank structure: Estimation, likelihood ratio test, and forecasting. Journal of Time Series Analysis, 13(4), 353-375.
Example
Three integrated series of which two share a common trend, so the rank is one; both sequences find it:
>>> import numpy as np
>>> from cultivars.multivariate.reduced_form.error_correction import VECM
>>> rng = np.random.default_rng(0)
>>> common = np.cumsum(rng.standard_normal(300))
>>> y = np.column_stack([
... common + rng.standard_normal(300),
... 0.5 * common + rng.standard_normal(300),
... np.cumsum(rng.standard_normal(300)),
... ])
>>> test = VECM(y, order=1, rank=1).rank_test(simulations=2000)
>>> test
JohansenRankTest(k=3, nobs=299, deterministic='constant', trace_rank=1, max_eigenvalue_rank=1)
>>> test.trace_pvalue.round(2)
array([0. , 0.21, 0.47])
>>> isinstance(test, JohansenRankTest)
True