Haldrup: Cointegration Analysis with I(1) and I(2) Variables
A Python implementation of the econometric methods from:
Haldrup, N. (1994). "The asymptotics of single-equation cointegration regressions with I(1) and I(2) variables." Journal of Econometrics, 63(1), 153-181.
Overview
This package provides tools for cointegration analysis when the underlying data-generating process contains both I(1) and I(2) variables. The key contributions include:
- Critical values for residual-based cointegration tests (Table 1 from Haldrup 1994)
- Cointegration regression with proper handling of mixed integration orders
- Spurious regression diagnostics following Theorem 1
- Unit root tests including tests for double unit roots (I(2))
- Monte Carlo simulation for computing critical values
Installation
pip install haldrup
Or install from source:
git clone https://github.com/merwanroudane/haldrup.git
cd haldrup
pip install -e .
Quick Start
Basic Cointegration Test
import numpy as np
from haldrup import cointegration_regression, get_critical_value
# Generate example data
np.random.seed(42)
n = 100
# I(2) process
x2 = np.cumsum(np.cumsum(np.random.randn(n)))
# Cointegrated y (I(2) + I(0) error)
y = 2.0 * x2 + np.random.randn(n)
# Run cointegration regression
result = cointegration_regression(y, x2=x2)
print(result.summary())
Get Critical Values
from haldrup import get_critical_value, get_critical_values_table
# Get specific critical value
cv = get_critical_value(m1=1, m2=1, n=100, alpha=0.05)
print(f"5% critical value: {cv.critical_value}")
# Print full table
print(get_critical_values_table(m2=1))
Test for Cointegration
from haldrup import adf_test
# Compute residuals from your cointegration regression
residuals = result.residuals
# Perform ADF test with proper critical values
test_result = adf_test(residuals, m1=1, m2=1)
print(test_result.summary())
Determine Integration Order
from haldrup import determine_integration_order, generate_i2_process
# Generate an I(2) process
x = generate_i2_process(n=500, seed=42)
# Determine integration order
order = determine_integration_order(x)
print(f"Estimated integration order: {order}")
Theoretical Background
The Model (Haldrup 1994, Section 2)
The package handles the general regression model:
$$y_t = \hat{\beta}'_0 c_t + \hat{\beta}'1 x{1t} + \hat{\beta}'2 x{2t} + \hat{u}_t$$
where:
- $c_t$ = deterministic components (constant, trend)
- $x_{1t}$ = I(1) regressors (m₁ variables)
- $x_{2t}$ = I(2) regressors (m₂ variables)
Key Results from Theorem 1 (pp. 159-161)
For the conditional model with integration order $d$:
| Statistic | d = 0 (full cointegration) | d = 1 (partial) | d = 2 (no cointegration) |
|---|---|---|---|
| $\hat{\beta}_1$ | $O_p(n^{-1})$-consistent | Nondegenerate | Diverges $O_p(n)$ |
| $\hat{\beta}_2$ | $O_p(n^{-2})$-superconsistent | $O_p(n^{-1})$-consistent | Nondegenerate |
| F-test | $\chi^2$ | Diverges $O_p(n)$ | Diverges $O_p(n)$ |
| DW | → 0 rate $O_p(n^{-3})$ | → 0 rate $O_p(n^{-1})$ | → 0 rate $O_p(n^{-1})$ |
| R² | → 1 rate $O_p(n^{-3})$ | → 1 rate $O_p(n^{-2})$ | Nondegenerate |
Critical Values (Table 1, p. 168)
The ADF test critical values depend on both m₁ (I(1) regressors) and m₂ (I(2) regressors). For large samples, critical values are similar for given m₁ + m₂, but for small samples, critical values become more negative as m₂ increases.
API Reference
Critical Values
from haldrup import get_critical_value, get_critical_values_table
# Get single critical value
cv = get_critical_value(m1, m2, n, alpha=0.05, deterministic="intercept")
# Get formatted table
table = get_critical_values_table(m2=1)
Cointegration Tests
from haldrup import adf_test, phillips_z_test, cointegration_test
# ADF test (Theorem 4)
result = adf_test(residuals, m1, m2, lags=None, deterministic="intercept")
# Phillips Z test
result = phillips_z_test(residuals, m1, m2, truncation=None)
# Unified interface
result = cointegration_test(residuals, m1, m2, method="adf")
Regression
from haldrup import cointegration_regression, polynomial_cointegration_regression
# Basic cointegration regression
result = cointegration_regression(y, x1=None, x2=x2, deterministic="c")
# Polynomial cointegration (with differences of I(2) variables)
result = polynomial_cointegration_regression(y, x2=x2, include_dx2=True)
Unit Root Tests
from haldrup import (
adf_unit_root_test,
hasza_fuller_test,
double_unit_root_test,
determine_integration_order
)
# Standard ADF test
result = adf_unit_root_test(y, deterministic="ct")
# Hasza-Fuller test for I(2)
result = hasza_fuller_test(y, deterministic="ct")
# Sequential testing for double unit root
test_i2, test_i1 = double_unit_root_test(y)
# Automatic integration order determination
order = determine_integration_order(y, max_order=2)
Diagnostics
from haldrup import durbin_watson, r_squared, spurious_regression_diagnostics
# Durbin-Watson statistic
dw = durbin_watson(residuals)
# R-squared
r2 = r_squared(y, y_fitted)
# Comprehensive spurious regression check
diagnostics = spurious_regression_diagnostics(y, y_fitted, k=2)
print(diagnostics.summary())
Simulation
from haldrup import (
simulate_critical_values,
generate_i1_process,
generate_i2_process
)
# Generate processes
x1 = generate_i1_process(n=100, seed=42)
x2 = generate_i2_process(n=100, seed=42)
# Simulate critical values
result = simulate_critical_values(m1=1, m2=1, n=100, replications=10000)
print(result.summary())
Examples
Example 1: UK Money Demand (Section 5)
Replicating the empirical application from Haldrup (1994, pp. 169-173):
import numpy as np
from haldrup import (
cointegration_regression,
determine_integration_order,
adf_test
)
# Load your data (m_t, p_t, y_t, R_t*)
# m_t and p_t are I(2), y_t and R_t* are I(1)
# Test integration orders
order_m = determine_integration_order(m)
order_p = determine_integration_order(p)
print(f"m_t integration order: {order_m}")
print(f"p_t integration order: {order_p}")
# Run cointegration regression
# y_t = m_t, x2 = p_t, x1 = [y_t, R_t*]
result = cointegration_regression(
m,
x1=np.column_stack([y, R]),
x2=p.reshape(-1, 1),
deterministic="ct"
)
print(result.summary())
Example 2: Detecting Spurious Regression
import numpy as np
from haldrup import (
generate_i1_process,
cointegration_regression,
spurious_regression_diagnostics
)
np.random.seed(42)
n = 200
# Generate independent random walks
y = generate_i1_process(n)
x = generate_i1_process(n)
# Run regression (spurious!)
result = cointegration_regression(y, x1=x.reshape(-1, 1))
# Check for spurious regression
diag = spurious_regression_diagnostics(
y, result.fitted_values, k=1
)
print(diag.summary())
# Note: Low DW + high R² = likely spurious!
Example 3: Monte Carlo Replication of Table 1
from haldrup import simulate_critical_values, get_critical_value
# Replicate Table 1 entry for m1=1, m2=1, n=100
simulated = simulate_critical_values(
m1=1, m2=1, n=100,
replications=10000,
seed=42
)
# Compare with published values
table_cv = get_critical_value(m1=1, m2=1, n=100, alpha=0.05)
print(f"Table 1 (5%): {table_cv.critical_value:.4f}")
print(f"Simulated (5%): {simulated.critical_values[0.05]:.4f}")
Citation
If you use this package in your research, please cite both the original paper and this implementation:
@article{haldrup1994asymptotics,
title={The asymptotics of single-equation cointegration regressions with {I}(1) and {I}(2) variables},
author={Haldrup, Niels},
journal={Journal of Econometrics},
volume={63},
number={1},
pages={153--181},
year={1994},
publisher={Elsevier}
}
@software{roudane2024haldrup,
author = {Roudane, Merwan},
title = {haldrup: Python Implementation of Haldrup (1994) Cointegration Methods},
year = {2024},
url = {https://github.com/merwanroudane/haldrup}
}
References
-
Haldrup, N. (1994). The asymptotics of single-equation cointegration regressions with I(1) and I(2) variables. Journal of Econometrics, 63(1), 153-181.
-
Phillips, P.C.B. and Ouliaris, S. (1990). Asymptotic properties of residual based tests for cointegration. Econometrica, 58(1), 165-193.
-
Engle, R.F. and Granger, C.W.J. (1987). Co-integration and error correction: Representation, estimation and testing. Econometrica, 55(2), 251-276.
-
Hasza, D.P. and Fuller, W.A. (1979). Estimation of autoregressive processes with unit roots. Annals of Statistics, 7(5), 1106-1120.
-
Dickey, D.A. and Pantula, S.G. (1987). Determining the order of differencing in autoregressive processes. Journal of Business and Economic Statistics, 5(4), 455-461.
License
MIT License - see LICENSE file.
Author
Dr Merwan Roudane
- Email: merwanroudane920@gmail.com
- GitHub: merwanroudane
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Changelog
Version 0.0.1 (Initial Release)
- Implementation of Table 1 critical values
- ADF and Phillips Z cointegration tests
- Cointegration regression with diagnostics
- Unit root tests including Hasza-Fuller
- Monte Carlo simulation for critical values
- Comprehensive test suite
Metadata
Release files for haldrup 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| haldrup-0.0.2.tar.gz | 41.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| haldrup-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 84.0 kB
Release files / haldrup-0.0.2.tar.gz
| Download URL | haldrup-0.0.2.tar.gz |
|---|---|
| Size | 41.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4d58e609943ca2bd8097c8795717d834540f35803fa4d31a055020480e86ca57
|
|
BLAKE2b-256 checksum How to use checksums |
1cab3aae24dac18d386cbb5c5b5c2e14f5b372d8dd3e9fe6e13f0a5744bcafc5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Release files / haldrup-0.0.2-py3-none-any.whl
| Download URL | haldrup-0.0.2-py3-none-any.whl |
|---|---|
| Size | 42.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
89f050965b37b6972927b57ef80af38be585d342cddd5db3492bc9f05a4c320d
|
|
BLAKE2b-256 checksum How to use checksums |
f4130a84f18313891a29a993535e6178151609ca46ad38d4f7f03f1dbfb5de89
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Security for haldrup 0.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.