Skip to main content

Time-Varying Cointegration Test based on Bierens and Martins (2010) with Bootstrap Tests from Martins (2016)

Project description

tvcoint: Time-Varying Cointegration Analysis in Python

Python 3.8+ License: MIT PyPI version

A comprehensive Python library implementing the time-varying cointegration framework of Bierens and Martins (2010) from Econometric Theory.

Overview

The tvcoint package provides tools for testing whether cointegrating relationships in multivariate time series are time-invariant (standard Johansen cointegration) or time-varying. The methodology allows cointegrating vectors to change smoothly over time using Chebyshev polynomial approximations.

Key Features

  • Time-Varying VECM Estimation: Estimate vector error correction models where cointegrating vectors evolve over time via Chebyshev time polynomials
  • Likelihood Ratio Test: Test H₀ (time-invariant cointegration) vs. H₁ (time-varying cointegration)
  • Bootstrap Tests (NEW): Wild and i.i.d. bootstrap tests from Martins (2016) with better finite-sample properties
  • Asymptotic Chi-Square Distribution: Under H₀, the test statistic follows χ²(mkr)
  • Standard Johansen Analysis: Full implementation of Johansen (1988, 1991) as a special case
  • Critical Values: Asymptotic and Monte Carlo simulated critical values
  • PPP Application: Specialized functions for Purchasing Power Parity testing

Installation

From PyPI (recommended)

pip install tvcoint

From Source

git clone https://github.com/merwanroudane/tvcoint.git
cd tvcoint
pip install -e .

Dependencies

  • Python ≥ 3.8
  • NumPy ≥ 1.20
  • SciPy ≥ 1.7
  • pandas ≥ 1.3
  • statsmodels ≥ 0.13
  • matplotlib ≥ 3.4
  • tabulate ≥ 0.8

Quick Start

Basic Time-Varying Cointegration Test

import numpy as np
import tvcoint

# Simulate cointegrated data
Y = tvcoint.simulate_cointegrated_system(T=200, k=2, r=1, seed=42)

# Test for time-varying cointegration
# H0: Time-invariant cointegration (m=0)
# H1: Time-varying cointegration (m>0)
result = tvcoint.lr_test_tv_cointegration(
    Y, 
    p=2,    # VAR lag order
    r=1,    # Cointegration rank
    m=2,    # Chebyshev polynomial order
    include_intercept=True
)

# Print formatted results
print(result)

Output

======================================================================
Time-Varying Cointegration Test
Bierens and Martins (2010)
======================================================================

Test Configuration:
  Sample size (T)          : 200
  Number of variables (k)  : 2
  Cointegration rank (r)   : 1
  Chebyshev order (m)      : 2
  VAR lag order (p)        : 2

Test Results:
  LR Test Statistic        : 3.4521
  Degrees of Freedom       : 4
  P-value                  : 0.4853

Critical Values:
  10% level                : 7.7794
  5% level                 : 9.4877
  1% level                 : 13.2767

Conclusion:
  Fail to reject H0 at 5% level
  Evidence supports TIME-INVARIANT cointegration
======================================================================

Detailed Usage

1. Chebyshev Time Polynomials

The cointegrating vectors are modeled as:

βₜ = Σᵢ₌₀ᵐ ξᵢ Pᵢ,ₜ(t)

where Pᵢ,ₜ(t) are Chebyshev time polynomials defined as:

  • P₀,ₜ(t) = 1
  • Pᵢ,ₜ(t) = √2 cos(iπ(t - 0.5)/T), for i ≥ 1
import tvcoint
import numpy as np

# Generate Chebyshev polynomial matrix
T = 100
m = 3  # Order
P = tvcoint.chebyshev_polynomial_matrix(T, m)

# Verify orthonormality: (1/T) Σₜ Pᵢ(t)Pⱼ(t) = δᵢⱼ
is_orthonormal = tvcoint.verify_orthonormality(P)
print(f"Orthonormality verified: {is_orthonormal}")

2. Standard Johansen VECM (m = 0)

import tvcoint

# Load or simulate data
Y = tvcoint.simulate_cointegrated_system(T=200, k=3, r=1)

# Select lag order using information criteria
best_lag = tvcoint.select_lag_order(Y, max_lag=8, criterion='bic')
print(f"Selected lag order: {best_lag}")

# Estimate standard VECM
vecm = tvcoint.JohansenVECM(Y, p=best_lag, r=1)
vecm.fit()

# Get estimated parameters
print("Eigenvalues:", vecm.eigenvalues)
print("Alpha (adjustment):\n", vecm.alpha)
print("Beta (cointegrating vectors):\n", vecm.beta)

# Trace test for cointegration rank
test_results = tvcoint.johansen_trace_test(Y, p=best_lag)
print(test_results)

3. Time-Varying VECM (m > 0)

import tvcoint

# Simulate time-varying cointegrated data
Y_tv = tvcoint.simulate_tv_cointegrated_system(
    T=300, k=2, r=1, m=2, seed=42
)

# Estimate time-varying VECM
tv_vecm = tvcoint.TimeVaryingVECM(Y_tv, p=2, r=1, m=2)
tv_vecm.fit()

# Get time-varying cointegrating vectors
beta_t = tv_vecm.get_time_varying_beta()  # Shape: (T, k, r)

# Plot evolution of cointegrating coefficients
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 6))
for i in range(tv_vecm.k):
    plt.plot(beta_t[:, i, 0], label=f'β_{i+1}(t)')
plt.xlabel('Time')
plt.ylabel('Coefficient Value')
plt.title('Time-Varying Cointegrating Vector')
plt.legend()
plt.show()

# Test for time-invariance
variation = tv_vecm.test_time_invariance()
print(f"Variation ratio: {variation['variation_ratio']:.4f}")

4. LR Test with Multiple m Values

import tvcoint

# Test across multiple Chebyshev orders
results = tvcoint.multiple_m_test(
    Y, p=2, r=1, 
    m_values=[1, 2, 3, 4, 5],
    alpha=0.05,
    correction='bonferroni'
)

for m, res in results.items():
    print(f"m={m}: LR={res.test_statistic:.3f}, p={res.p_value:.4f}")

5. Sequential Testing

# Sequential test (stops at first non-rejection)
result, m_selected = tvcoint.sequential_test(
    Y, p=2, r=1,
    m_max=5,
    alpha=0.05
)
print(f"Selected m: {m_selected}")

6. PPP Application

Testing Purchasing Power Parity with time-varying cointegration:

import pandas as pd
import tvcoint

# Load PPP data: [ln(S), ln(P_domestic), ln(P_foreign)]
# S = nominal exchange rate, P = price indices
data = pd.read_csv('ppp_data.csv')
Y = data[['log_exchange', 'log_price_us', 'log_price_uk']].values

# Test PPP with time-varying cointegration
result = tvcoint.test_ppp_application(
    Y, 
    p=2, 
    m=3,
    domestic_col=1,
    foreign_col=2
)
print(result)

7. Simulation Studies

import tvcoint
import numpy as np

# Size simulation under H0
def simulate_size(T, k, r, m, n_rep=1000, alpha=0.05):
    rejections = 0
    for i in range(n_rep):
        # Simulate under H0 (time-invariant)
        Y = tvcoint.simulate_cointegrated_system(T=T, k=k, r=r, seed=i)
        result = tvcoint.lr_test_tv_cointegration(Y, p=2, r=r, m=m)
        if result.p_value < alpha:
            rejections += 1
    return rejections / n_rep

# Run simulation
empirical_size = simulate_size(T=200, k=2, r=1, m=2)
print(f"Empirical size (nominal 5%): {empirical_size:.3f}")

# Power simulation under H1
def simulate_power(T, k, r, m, omega=0.5, n_rep=1000, alpha=0.05):
    rejections = 0
    for i in range(n_rep):
        # Simulate under H1 (time-varying)
        Y = tvcoint.simulate_tv_cointegrated_system(
            T=T, k=k, r=r, m=m, omega=omega, seed=i
        )
        result = tvcoint.lr_test_tv_cointegration(Y, p=2, r=r, m=m)
        if result.p_value < alpha:
            rejections += 1
    return rejections / n_rep

power = simulate_power(T=200, k=2, r=1, m=2, omega=0.5)
print(f"Empirical power: {power:.3f}")

8. Critical Values

import tvcoint

# Asymptotic critical values (χ² distribution)
cv_5pct = tvcoint.asymptotic_critical_value(m=2, k=2, r=1, alpha=0.05)
print(f"5% asymptotic CV: {cv_5pct:.4f}")

# Monte Carlo simulated critical values (for small samples)
simulated_cvs = tvcoint.simulate_null_distribution(
    T=100, k=2, r=1, m=2, 
    n_simulations=10000,
    alpha_levels=[0.10, 0.05, 0.01]
)
print(f"Simulated CVs: {simulated_cvs}")

# Size-adjusted critical values
cv_adjusted = tvcoint.get_size_adjusted_critical_value(
    T=100, k=2, r=1, m=2, alpha=0.05
)
print(f"Size-adjusted CV: {cv_adjusted:.4f}")

# Bootstrap p-value
p_bootstrap = tvcoint.bootstrap_p_value(
    Y, p=2, r=1, m=2, 
    observed_statistic=5.5,
    n_bootstrap=1000
)
print(f"Bootstrap p-value: {p_bootstrap:.4f}")

9. Bootstrap Tests for TVC (Martins, 2016)

The bootstrap tests provide better finite-sample size properties compared to the asymptotic chi-square test, especially when sample size is small or when there is conditional heteroskedasticity.

import numpy as np
import tvcoint

# Generate data
Y, _ = tvcoint.simulate_cointegrated_system(T=200, k=2, r=1, seed=42)

# Wild Bootstrap Test (recommended under conditional heteroskedasticity)
result_wild = tvcoint.wild_bootstrap_tvc_test(
    Y, 
    r=1,           # Cointegration rank
    m=2,           # Chebyshev order
    p=2,           # Lag order
    n_bootstrap=399,  # Number of bootstrap replications
    seed=123
)
print(result_wild)

# i.i.d. Bootstrap Test (Swensen, 2006)
result_iid = tvcoint.iid_bootstrap_tvc_test(
    Y, r=1, m=2, p=2,
    n_bootstrap=399,
    seed=123
)

# Compare asymptotic and bootstrap results
comparison = tvcoint.compare_asymptotic_bootstrap(
    Y, r=1, m=2, p=2,
    n_bootstrap=399,
    seed=456
)
print(f"Asymptotic p-value: {comparison['asymptotic']['p_value']:.4f}")
print(f"Wild bootstrap p-value: {comparison['wild_bootstrap']['p_value']:.4f}")
print(f"i.i.d. bootstrap p-value: {comparison['iid_bootstrap']['p_value']:.4f}")

# Bootstrap test for multiple m values
results = tvcoint.bootstrap_multiple_m_test(
    Y, r=1, m_values=[1, 2, 3, 4, 5], p=2,
    bootstrap_method='wild',
    n_bootstrap=199,
    bonferroni_correction=True
)
print(f"Minimum p-value: {results['min_p_value']:.4f} at m={results['min_p_value_m']}")
print(f"Bonferroni-corrected p-value: {results['bonferroni_p_value']:.4f}")

Bootstrap Methods

Method Description When to Use
Wild Bootstrap wild_bootstrap_tvc_test() Default choice; robust to conditional heteroskedasticity
i.i.d. Bootstrap iid_bootstrap_tvc_test() Standard parametric bootstrap

Residual Types

Both unrestricted (from TV-VECM) and restricted (from standard VECM) residuals can be used:

# Unrestricted residuals (default, recommended)
result_ur = tvcoint.wild_bootstrap_tvc_test(
    Y, r=1, m=2, p=2,
    residual_type='unrestricted'
)

# Restricted residuals (Cavaliere et al., 2012)
result_r = tvcoint.wild_bootstrap_tvc_test(
    Y, r=1, m=2, p=2,
    residual_type='restricted'
)

Reference: Martins, L.F. (2016). "Bootstrap tests for time varying cointegration." Econometric Reviews, DOI: 10.1080/07474938.2015.1092830

Mathematical Framework

Model Specification

The time-varying VECM(p) model is:

ΔYₜ = αβₜ'Yₜ₋₁ + Σⱼ₌₁ᵖ⁻¹ Γⱼ ΔYₜ₋ⱼ + εₜ,    t = 1, ..., T

where:

  • Yₜ ∈ ℝᵏ is a k-dimensional I(1) process
  • α is the k×r matrix of adjustment coefficients (time-invariant)
  • βₜ is the k×r matrix of time-varying cointegrating vectors
  • Γⱼ are k×k short-run coefficient matrices
  • εₜ ~ i.i.d. Nₖ(0, Ω)

Chebyshev Approximation

The time-varying cointegrating vectors are modeled as:

βₜ = Σᵢ₌₀ᵐ ξᵢ Pᵢ,ₜ(t)

Orthonormality property: For all integers i, j:

(1/T) Σₜ₌₁ᵀ Pᵢ,ₜ(t) Pⱼ,ₜ(t) = δᵢⱼ

LR Test Statistic

The likelihood ratio test statistic (equation 6) is:

LR^{tvc} = T Σⱼ₌₁ʳ ln[(1 - λ̂₀,ⱼ) / (1 - λ̂ₘ,ⱼ)]

where:

  • λ̂₀,ⱼ are eigenvalues from standard Johansen estimation (m=0)
  • λ̂ₘ,ⱼ are eigenvalues from TV-VECM estimation (m>0)

Asymptotic Distribution (Theorem 1)

Under H₀ (time-invariant cointegration):

LR^{tvc} →ᵈ χ²(mkr)

API Reference

Main Functions

Function Description
lr_test_tv_cointegration(Y, p, r, m, ...) Perform LR test for time-varying cointegration
bootstrap_tvc_test(Y, r, m, p, ...) Bootstrap test for TVC (Martins, 2016)
wild_bootstrap_tvc_test(Y, r, m, p, ...) Wild bootstrap test for TVC
iid_bootstrap_tvc_test(Y, r, m, p, ...) i.i.d. bootstrap test for TVC
bootstrap_multiple_m_test(Y, r, m_values, ...) Bootstrap test for multiple m values
compare_asymptotic_bootstrap(Y, r, m, p, ...) Compare asymptotic and bootstrap tests
multiple_m_test(Y, p, r, m_values, ...) Test across multiple Chebyshev orders
sequential_test(Y, p, r, m_max, ...) Sequential testing procedure
test_ppp_application(Y, p, m, ...) PPP-specific test

Classes

Class Description
JohansenVECM Standard Johansen VECM estimation
TimeVaryingVECM Time-varying VECM estimation
TVCointegrationTestResult Asymptotic test results container
BootstrapTVCTestResult Bootstrap test results container
CriticalValueCache Cache for critical values

Utility Functions

Function Description
chebyshev_polynomial_matrix(T, m) Generate Chebyshev polynomial matrix
construct_extended_y(Y, m) Build extended regressor Y^(m)
simulate_cointegrated_system(...) Simulate TI cointegrated data
simulate_tv_cointegrated_system(...) Simulate TV cointegrated data
select_lag_order(Y, max_lag, criterion) VAR lag selection
select_chebyshev_order(Y, p, r, m_max, ...) Chebyshev order selection

Replication of Paper Results

Table 1: Power of the LR Test

import tvcoint
import numpy as np

# Replicate Table 1 from Bierens and Martins (2010)
def replicate_table1(T, m, n_rep=10000):
    omega_values = [0, 0.01, 0.05, 0.1, 0.2, 0.5, 1.0]
    results = {}
    
    for omega in omega_values:
        rejections = 0
        for rep in range(n_rep):
            # DGP from paper (Section 4.3)
            Y = tvcoint.simulate_tv_cointegrated_system(
                T=T, k=2, r=1, m=m, omega=omega, seed=rep
            )
            result = tvcoint.lr_test_tv_cointegration(Y, p=1, r=1, m=m)
            if result.p_value < 0.05:
                rejections += 1
        results[omega] = rejections / n_rep
    
    return results

# Run for T=100, m=1
results_100_m1 = replicate_table1(T=100, m=1)
print("T=100, m=1:", results_100_m1)

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},
  doi={10.1017/S0266466609990648}
}

@article{martins2016bootstrap,
  title={Bootstrap tests for time varying cointegration},
  author={Martins, Luis F},
  journal={Econometric Reviews},
  year={2016},
  doi={10.1080/07474938.2015.1092830}
}

@software{tvcoint,
  author = {Roudane, Merwan},
  title = {tvcoint: Time-Varying Cointegration Analysis in Python},
  year = {2025},
  url = {https://github.com/merwanroudane/tvcoint}
}

License

MIT License - see LICENSE file.

Author

Dr. Merwan Roudane

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Changelog

v1.1.0 (2025)

  • NEW: Bootstrap tests for time-varying cointegration (Martins, 2016)
    • Wild bootstrap test (wild_bootstrap_tvc_test)
    • i.i.d. bootstrap test (iid_bootstrap_tvc_test)
    • Support for unrestricted and restricted residuals
    • bootstrap_multiple_m_test for testing multiple m values
    • compare_asymptotic_bootstrap for comparing methods
  • Added BootstrapTVCTestResult class for bootstrap test results
  • Improved finite-sample size properties

v1.0.0 (2025)

  • Initial release
  • Full implementation of Bierens and Martins (2010)
  • Chebyshev polynomial-based time-varying VECM
  • LR test for time-varying cointegration
  • Asymptotic and simulated critical values
  • PPP application support
  • Comprehensive documentation and examples

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tvcoint-1.1.0.tar.gz (52.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tvcoint-1.1.0-py3-none-any.whl (49.0 kB view details)

Uploaded Python 3

File details

Details for the file tvcoint-1.1.0.tar.gz.

File metadata

  • Download URL: tvcoint-1.1.0.tar.gz
  • Upload date:
  • Size: 52.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for tvcoint-1.1.0.tar.gz
Algorithm Hash digest
SHA256 eb410b0146600684efc25cd2870e5f223fadc2e2dff7ed31a2ff6508139fcc01
MD5 dd9654b87071cd9a6a431863c17f7425
BLAKE2b-256 068b773d33ecbf313ac0c05a3c86c0abd23b7ecfb7e12a0c82df9fe614e30122

See more details on using hashes here.

File details

Details for the file tvcoint-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: tvcoint-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 49.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for tvcoint-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2291fe4de95916c042e6c8719de0eabb40d8865c15f0e09b370c454b792f5ceb
MD5 ccc3d4526d206739a41275be4666c4a0
BLAKE2b-256 7ed0951f550fafd029b8e37b8020f1e5a57fe07fca879c68b2b39b1897074334

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page