Skip to main content

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

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

Metadata

Release files for tvcoint 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tvcoint 1.1.0
File Size Uploaded Provenance
tvcoint-1.1.0.tar.gz 52.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for tvcoint 1.1.0
File Interpreter ABI Platform
tvcoint-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 101.7 kB

Release files / tvcoint-1.1.0.tar.gz

Download URL tvcoint-1.1.0.tar.gz
Size 52.7 kB
Tags Source
SHA-256 checksum
How to use checksums
eb410b0146600684efc25cd2870e5f223fadc2e2dff7ed31a2ff6508139fcc01
BLAKE2b-256 checksum
How to use checksums
068b773d33ecbf313ac0c05a3c86c0abd23b7ecfb7e12a0c82df9fe614e30122
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release files / tvcoint-1.1.0-py3-none-any.whl

Download URL tvcoint-1.1.0-py3-none-any.whl
Size 49.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2291fe4de95916c042e6c8719de0eabb40d8865c15f0e09b370c454b792f5ceb
BLAKE2b-256 checksum
How to use checksums
7ed0951f550fafd029b8e37b8020f1e5a57fe07fca879c68b2b39b1897074334
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Security for tvcoint 1.1.0

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.

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page