Time-Varying Cointegration Tests - Bootstrap LR tests for time-varying cointegration based on Bierens & Martins (2010) and Martins (2015)
Project description
TVRCoint - Time-Varying Cointegration Tests
A comprehensive Python library for testing time-invariant cointegration against time-varying cointegration alternatives using bootstrap methods.
Overview
TVRCoint implements the methodology from:
- Bierens, H.J. & Martins, L.F. (2010) - "Time-Varying Cointegration" - Econometric Theory, 26(5), 1453-1490
- Martins, L.F. (2015) - "Bootstrap Tests for Time Varying Cointegration" - Econometric Reviews
Installation
pip install tvrcoint
Or install from source:
git clone https://github.com/merwanroudane/tvrcoint.git
cd tvrcoint
pip install -e .
Dependencies
numpy>=1.20.0
scipy>=1.7.0
pandas>=1.3.0
Complete API Reference with Examples
1. Chebyshev Time Polynomials
from tvrcoint.chebyshev import (
chebyshev_poly,
chebyshev_polynomials,
chebyshev_matrix,
verify_orthonormality,
fourier_coefficients,
reconstruct_beta
)
import numpy as np
# ============================================
# 1.1 chebyshev_poly(i, t, T)
# Compute single Chebyshev polynomial value
# ============================================
T = 100
# P_0(t) = 1 for all t
p0 = chebyshev_poly(0, 50, T) # Returns 1.0
# P_1(t) = sqrt(2) * cos(pi*(t-0.5)/T)
p1 = chebyshev_poly(1, 50, T) # Returns near 0 for middle of series
print(f"P_0,100(50) = {p0}") # 1.0
print(f"P_1,100(50) = {p1}") # ~0
# ============================================
# 1.2 chebyshev_polynomials(m, T)
# Get full polynomial matrix
# ============================================
m = 5 # Order of polynomials (0 to m)
P = chebyshev_polynomials(m, T)
# Shape: (m+1, T) = (6, 100)
print(f"Shape: {P.shape}") # (6, 100)
print(f"First row (P_0): {P[0, :5]}") # All 1s
# ============================================
# 1.3 verify_orthonormality(m, T)
# Check if polynomials are orthonormal
# ============================================
is_orthonormal = verify_orthonormality(m=5, T=100)
print(f"Orthonormal: {is_orthonormal}") # True
# (1/T) * sum(P_i * P_j) = 1 if i==j, 0 otherwise
# ============================================
# 1.4 chebyshev_matrix(Y, m)
# Construct extended lagged matrix Y^{(m)}_{t-1}
# ============================================
Y = np.random.randn(100, 3) # T=100, k=3 variables
m = 2
Y_m = chebyshev_matrix(Y, m)
# Shape: (T-1, (m+1)*k) = (99, 9)
print(f"Input shape: {Y.shape}") # (100, 3)
print(f"Output shape: {Y_m.shape}") # (99, 9)
# ============================================
# 1.5 fourier_coefficients(beta_t, T, m)
# Compute Chebyshev expansion coefficients
# ============================================
beta_t = np.random.randn(100, 3) # Time-varying beta
xi = fourier_coefficients(beta_t, T=100, m=2)
# Shape: (m+1, k) = (3, 3)
print(f"Coefficients shape: {xi.shape}") # (3, 3)
# ============================================
# 1.6 reconstruct_beta(xi, T)
# Reconstruct beta_t from coefficients
# ============================================
beta_reconstructed = reconstruct_beta(xi, T=100)
print(f"Reconstructed shape: {beta_reconstructed.shape}") # (100, 3)
2. Matrix Computations
from tvrcoint.matrices import (
compute_s_matrices,
solve_eigenvalue_problem,
compute_lr_statistic
)
import numpy as np
np.random.seed(42)
# ============================================
# 2.1 compute_s_matrices(delta_Y, Y_m, X, include_intercept)
# Compute S00, S11, S01, S10 matrices
# ============================================
T_eff, k, m = 98, 3, 2
delta_Y = np.random.randn(T_eff, k) # Differenced data
Y_m = np.random.randn(T_eff, (m+1)*k) # Extended lagged data
S = compute_s_matrices(delta_Y, Y_m, include_intercept=True)
print(f"S00 shape: {S['S00'].shape}") # (3, 3)
print(f"S11 shape: {S['S11'].shape}") # (9, 9)
print(f"S01 shape: {S['S01'].shape}") # (3, 9)
print(f"S10 shape: {S['S10'].shape}") # (9, 3)
# Verify properties
print(f"S00 symmetric: {np.allclose(S['S00'], S['S00'].T)}") # True
print(f"S10 = S01.T: {np.allclose(S['S10'], S['S01'].T)}") # True
# ============================================
# 2.2 solve_eigenvalue_problem(S11, S10, S00, S01, r)
# Solve generalized eigenvalue problem
# ============================================
eigenvalues, eigenvectors = solve_eigenvalue_problem(
S['S11'], S['S10'], S['S00'], S['S01'], r=3
)
print(f"Eigenvalues: {eigenvalues}") # Ordered, largest first
print(f"All in [0,1]: {np.all((eigenvalues >= 0) & (eigenvalues <= 1))}") # True
# ============================================
# 2.3 compute_lr_statistic(lambda_0, lambda_m, T, r)
# Compute LR test statistic
# ============================================
lambda_0 = np.array([0.8, 0.5, 0.3]) # Eigenvalues under H0
lambda_m = np.array([0.85, 0.55, 0.35]) # Eigenvalues under Ha
lr_stat = compute_lr_statistic(lambda_0, lambda_m, T=100, r=3)
print(f"LR statistic: {lr_stat:.4f}") # Non-negative value
3. Data Simulation
from tvrcoint.simulation import (
generate_cointegrated_data,
dgp_bm,
dgp_js,
dgp_ey,
simulate_critical_values
)
# ============================================
# 3.1 generate_cointegrated_data(T, k, r, seed)
# Generate cointegrated data under H0
# ============================================
Y = generate_cointegrated_data(T=200, k=3, r=1, seed=42)
print(f"Data shape: {Y.shape}") # (200, 3)
# Reproducible with same seed
Y2 = generate_cointegrated_data(T=200, k=3, r=1, seed=42)
print(f"Reproducible: {np.allclose(Y, Y2)}") # True
# ============================================
# 3.2 dgp_bm(T, k, p, seed)
# Bierens-Martins (2010) simulation design
# ============================================
Y_bm = dgp_bm(T=200, k=2, p=2, seed=42)
# Bivariate system with r=1, specific alpha/beta
print(f"BM DGP shape: {Y_bm.shape}") # (200, 2)
# ============================================
# 3.3 dgp_js(T, k, seed)
# Johansen-Swensen (2002) simulation design
# ============================================
Y_js = dgp_js(T=200, k=3, seed=42)
# Trivariate system with r=1
print(f"JS DGP shape: {Y_js.shape}") # (200, 3)
# ============================================
# 3.4 dgp_ey(T, k, seed)
# Engle-Yoo (1987) simulation design
# ============================================
Y_ey = dgp_ey(T=200, k=3, seed=42)
# Trivariate system with r=2
print(f"EY DGP shape: {Y_ey.shape}") # (200, 3)
# ============================================
# 3.5 simulate_critical_values(m, k, r, T, n_simulations)
# Monte Carlo simulation for critical values
# ============================================
# Note: This is computationally intensive
results = simulate_critical_values(
m=2, k=2, r=1, T=100,
n_simulations=1000, # Use 10000 for accurate results
seed=42,
verbose=True
)
print(f"Simulated 5% CV: {results.critical_values[0.05]:.4f}")
print(f"Empirical size at asymptotic 5%: {results.empirical_size[0.05]:.4f}")
4. TV-VECM Estimation
from tvrcoint import TVVECM
from tvrcoint.tvvecm import fit_vecm, fit_tvvecm
from tvrcoint.simulation import generate_cointegrated_data
import numpy as np
np.random.seed(42)
Y = generate_cointegrated_data(T=200, k=3, r=1)
# ============================================
# 4.1 TVVECM class
# Time-Varying Vector Error Correction Model
# ============================================
model = TVVECM()
# Standard VECM (m=0)
results = model.fit(Y, r=1, m=0, p=2, include_drift=True)
print(f"Sample size T: {results.T}")
print(f"Variables k: {results.k}")
print(f"Cointegration rank r: {results.r}")
print(f"Alpha (adjustment) shape: {results.alpha.shape}") # (3, 1)
print(f"Beta (cointegrating) shape: {results.beta.shape}") # (3, 1)
print(f"Residuals shape: {results.residuals.shape}") # (T_eff, 3)
print(f"Eigenvalues: {results.eigenvalues[:3]}")
print(f"Log-likelihood: {results.log_likelihood:.2f}")
# TV-VECM (m > 0)
results_tv = model.fit(Y, r=1, m=5, p=2, include_drift=True)
print(f"\nTV-VECM with m=5:")
print(f"Xi shape: {results_tv.xi.shape}") # ((m+1)*k, r) = (18, 1)
print(f"Log-likelihood: {results_tv.log_likelihood:.2f}")
# ============================================
# 4.2 Reconstruct time-varying beta
# ============================================
beta_t = results_tv.beta_t(T=200) # Shape (T, k, r)
print(f"Time-varying beta shape: {beta_t.shape}") # (200, 3, 1)
# Plot time evolution of first cointegrating vector
# import matplotlib.pyplot as plt
# plt.plot(beta_t[:, :, 0])
# ============================================
# 4.3 Convenience functions
# ============================================
# Quick standard VECM
results_quick = fit_vecm(Y, r=1, p=2)
# Quick TV-VECM
results_tv_quick = fit_tvvecm(Y, r=1, m=3, p=2)
5. LR Test for Time-Varying Cointegration
from tvrcoint import TVCTest
from tvrcoint.lr_test import tvc_test, critical_values_chi2, TVCTestResults
from tvrcoint.simulation import generate_cointegrated_data
import numpy as np
np.random.seed(42)
Y = generate_cointegrated_data(T=200, k=3, r=1)
# ============================================
# 5.1 TVCTest class
# LR test: H0 (time-invariant) vs H1 (time-varying)
# ============================================
test = TVCTest()
results = test.test(Y, r=1, m=5, p=1, include_drift=True)
print("LR Test Results:")
print(f" LR statistic: {results.statistic:.4f}")
print(f" Degrees of freedom: {results.df}") # m*k*r = 5*3*1 = 15
print(f" Asymptotic p-value: {results.pvalue:.4f}")
# Access eigenvalues
print(f" Eigenvalues under H0: {results.eigenvalues_0}")
print(f" Eigenvalues under H1: {results.eigenvalues_m}")
# ============================================
# 5.2 results.summary()
# Get formatted summary
# ============================================
print(results.summary())
# ============================================
# 5.3 test_multiple_m()
# Test multiple Chebyshev orders
# ============================================
multi_results = test.test_multiple_m(
Y, r=1, m_values=[1, 2, 5, 10], p=1
)
print("\nResults for different m values:")
for m, res in multi_results.items():
print(f" m={m:2d}: LR={res.statistic:8.2f}, df={res.df:3d}, p={res.pvalue:.4f}")
# ============================================
# 5.4 tvc_test() convenience function
# ============================================
results = tvc_test(Y, r=1, m=5, p=1)
print(f"\nQuick test: LR={results.statistic:.2f}, p={results.pvalue:.4f}")
# ============================================
# 5.5 critical_values_chi2()
# Get asymptotic critical values
# ============================================
cv = critical_values_chi2(df=15, levels=[0.10, 0.05, 0.01])
print(f"\nAsymptotic CV (df=15): 10%={cv[0.10]:.2f}, 5%={cv[0.05]:.2f}, 1%={cv[0.01]:.2f}")
6. Bootstrap Tests (Recommended)
from tvrcoint import BootstrapTVCTest
from tvrcoint.bootstrap import bootstrap_tvc_test
from tvrcoint.simulation import generate_cointegrated_data
import numpy as np
np.random.seed(42)
Y = generate_cointegrated_data(T=150, k=2, r=1)
# ============================================
# 6.1 BootstrapTVCTest class
# Wild Bootstrap (recommended)
# ============================================
test = BootstrapTVCTest()
results = test.test(
Y,
r=1, # Cointegration rank
m=5, # Chebyshev polynomial order
p=1, # VAR lag order
method='wild', # 'wild' or 'iid'
restricted=False, # Use unrestricted residuals (recommended)
B=399, # Number of bootstrap replications
include_drift=True,
seed=42 # For reproducibility
)
print("Wild Bootstrap Results:")
print(f" LR statistic: {results.statistic:.4f}")
print(f" Bootstrap p-value: {results.pvalue_bootstrap:.4f}")
print(f" Asymptotic p-value: {results.pvalue_asymptotic:.4f}")
print(f" Method: {results.method}")
print(f" Replications (B): {results.B}")
# ============================================
# 6.2 Bootstrap critical values
# ============================================
print(f"\nBootstrap Critical Values:")
print(f" 10%: {results.critical_values[0.10]:.4f}")
print(f" 5%: {results.critical_values[0.05]:.4f}")
print(f" 1%: {results.critical_values[0.01]:.4f}")
# ============================================
# 6.3 Access bootstrap distribution
# ============================================
bs_stats = results.bootstrap_statistics
print(f"\nBootstrap distribution:")
print(f" Mean: {np.mean(bs_stats):.4f}")
print(f" Std: {np.std(bs_stats):.4f}")
print(f" Min: {np.min(bs_stats):.4f}")
print(f" Max: {np.max(bs_stats):.4f}")
# ============================================
# 6.4 results.summary()
# ============================================
print(results.summary())
# ============================================
# 6.5 i.i.d. Bootstrap
# For i.i.d. Gaussian errors
# ============================================
results_iid = test.test(
Y, r=1, m=5, p=1,
method='iid',
restricted=False,
B=399,
seed=42
)
print(f"\ni.i.d. Bootstrap p-value: {results_iid.pvalue_bootstrap:.4f}")
# ============================================
# 6.6 Restricted vs Unrestricted residuals
# ============================================
# Unrestricted: residuals from TV-VECM (m>0) - recommended
results_ur = test.test(Y, r=1, m=5, p=1, method='wild', restricted=False, B=399)
# Restricted: residuals from standard VECM (m=0)
results_r = test.test(Y, r=1, m=5, p=1, method='wild', restricted=True, B=399)
print(f"\nUnrestricted p-value: {results_ur.pvalue_bootstrap:.4f}")
print(f"Restricted p-value: {results_r.pvalue_bootstrap:.4f}")
# ============================================
# 6.7 bootstrap_tvc_test() convenience function
# ============================================
results = bootstrap_tvc_test(
Y, r=1, m=5, p=1,
method='wild', B=399, seed=42
)
print(f"\nQuick bootstrap: p={results.pvalue_bootstrap:.4f}")
7. Output Formatting
from tvrcoint.output import (
format_results,
results_to_latex,
results_to_markdown,
results_to_html,
format_pvalue,
create_comparison_table
)
from tvrcoint import BootstrapTVCTest
from tvrcoint.simulation import generate_cointegrated_data
import numpy as np
np.random.seed(42)
Y = generate_cointegrated_data(T=150, k=2, r=1)
test = BootstrapTVCTest()
results = test.test(Y, r=1, m=5, p=1, method='wild', B=199, seed=42)
# ============================================
# 7.1 format_pvalue()
# Format p-values with significance stars
# ============================================
print("P-value formatting:")
print(f" 0.001 -> {format_pvalue(0.001)}") # '0.0010***'
print(f" 0.03 -> {format_pvalue(0.03)}") # '0.0300**'
print(f" 0.08 -> {format_pvalue(0.08)}") # '0.0800*'
print(f" 0.15 -> {format_pvalue(0.15)}") # '0.1500'
# ============================================
# 7.2 results_to_latex()
# Export to LaTeX table
# ============================================
latex_output = results_to_latex(results)
print("\nLaTeX output:")
print(latex_output)
# Save to file
with open('results_table.tex', 'w') as f:
f.write(latex_output)
# ============================================
# 7.3 results_to_markdown()
# Export to Markdown table
# ============================================
md_output = results_to_markdown(results)
print("\nMarkdown output:")
print(md_output)
# ============================================
# 7.4 results_to_html()
# Export to HTML with styling
# ============================================
html_output = results_to_html(results)
# Save to file
with open('results_table.html', 'w') as f:
f.write(html_output)
print("\nHTML output saved to results_table.html")
# ============================================
# 7.5 format_results()
# Generic formatter
# ============================================
text_output = format_results(results, 'text')
latex_output = format_results(results, 'latex')
md_output = format_results(results, 'markdown')
html_output = format_results(results, 'html')
# ============================================
# 7.6 create_comparison_table()
# Compare multiple test results
# ============================================
results_list = []
for m in [1, 2, 5]:
r = test.test(Y, r=1, m=m, p=1, method='wild', B=199, seed=m)
results_list.append(r)
comparison_latex = create_comparison_table(
results_list,
labels=['m=1', 'm=2', 'm=5']
)
print("\nComparison table:")
print(comparison_latex)
8. Critical Values
from tvrcoint.critical_values import (
get_critical_value,
get_asymptotic_critical_value,
print_critical_value_table,
compare_with_asymptotic,
CRITICAL_VALUES
)
# ============================================
# 8.1 get_asymptotic_critical_value()
# Chi-square critical values
# ============================================
cv = get_asymptotic_critical_value(m=5, k=2, r=1, level=0.05)
# df = m*k*r = 10
print(f"Asymptotic 5% CV (df=10): {cv:.4f}")
cv_01 = get_asymptotic_critical_value(m=5, k=3, r=1, level=0.01)
# df = 15
print(f"Asymptotic 1% CV (df=15): {cv_01:.4f}")
# ============================================
# 8.2 get_critical_value()
# Get pre-computed or asymptotic CV
# ============================================
# Pre-computed (if available)
cv = get_critical_value(m=2, k=2, r=1, T=100, level=0.05)
print(f"CV for m=2, k=2, r=1, T=100: {cv:.4f}")
# Falls back to asymptotic if not pre-computed
cv_fallback = get_critical_value(m=15, k=5, r=2, T=500, level=0.05)
print(f"CV (fallback): {cv_fallback:.4f}")
# ============================================
# 8.3 print_critical_value_table()
# Display pre-computed tables
# ============================================
print_critical_value_table(k=2, r=1)
# ============================================
# 8.4 compare_with_asymptotic()
# Compare simulated vs asymptotic CVs
# ============================================
compare_with_asymptotic(k=2, r=1)
# ============================================
# 8.5 Access raw data
# ============================================
# CRITICAL_VALUES[k][r][T][m] = {'cv_10': ..., 'cv_05': ..., 'cv_01': ...}
if 2 in CRITICAL_VALUES and 1 in CRITICAL_VALUES[2]:
print("\nAvailable T values for k=2, r=1:")
print(list(CRITICAL_VALUES[2][1].keys()))
Complete Workflow Example
"""
Complete workflow: PPP Hypothesis Testing
"""
import numpy as np
from tvrcoint import BootstrapTVCTest
from tvrcoint.simulation import generate_cointegrated_data
from tvrcoint.output import results_to_latex
# 1. Load or simulate data
np.random.seed(42)
Y = generate_cointegrated_data(T=200, k=3, r=1)
# Or: Y = pd.read_csv('ppp_data.csv').values
# 2. Set test parameters
r = 1 # Cointegration rank
m = 5 # Chebyshev polynomial order
p = 1 # VAR lag order
B = 399 # Bootstrap replications
# 3. Perform bootstrap test
test = BootstrapTVCTest()
results = test.test(
Y, r=r, m=m, p=p,
method='wild',
restricted=False,
B=B,
seed=42
)
# 4. Display results
print(results.summary())
# 5. Interpretation
if results.pvalue_bootstrap < 0.05:
print("Conclusion: Reject H0 -> Time-varying cointegration")
else:
print("Conclusion: Cannot reject H0 -> Time-invariant cointegration")
# 6. Export for publication
latex_table = results_to_latex(results)
with open('ppp_results.tex', 'w') as f:
f.write(latex_table)
Citation
If you use this library in your research, please cite:
@article{bierens2010time,
title={Time-Varying Cointegration},
author={Bierens, Herman J. and Martins, Luis F.},
journal={Econometric Theory},
volume={26},
number={5},
pages={1453--1490},
year={2010},
publisher={Cambridge University Press}
}
@article{martins2015bootstrap,
title={Bootstrap Tests for Time Varying Cointegration},
author={Martins, Luis F.},
journal={Econometric Reviews},
year={2015},
publisher={Taylor \& Francis}
}
Author
Dr Merwan Roudane
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tvrcoint-1.0.0.tar.gz.
File metadata
- Download URL: tvrcoint-1.0.0.tar.gz
- Upload date:
- Size: 38.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae59dffbd1f6d1476e2f0624b002c980cd8c26e71a5972d70870fd365354ea65
|
|
| MD5 |
de072bd29b8acced28e88ad8e8e33bdd
|
|
| BLAKE2b-256 |
fd5228ff9961ace0dea61da48a28d7d5af9845d0a20e77766e922bccac8ae262
|
File details
Details for the file tvrcoint-1.0.0-py3-none-any.whl.
File metadata
- Download URL: tvrcoint-1.0.0-py3-none-any.whl
- Upload date:
- Size: 36.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89095bc38180ad545f4233ce20fbdd1877951aba55a302692e9379676067f238
|
|
| MD5 |
ba9dcba01e04a8ad067705b4f3d095fd
|
|
| BLAKE2b-256 |
de5011fb73fe47281840b08ad617ce1d79732c1bc8ce7d7512967d073a862113
|