Skip to main content

Multiple Structural Break Detection in Vector Error Correction Models

Project description

vecmbreak

PyPI version Python 3.9+ License: MIT

Multiple Structural Break Detection in Vector Error Correction Models

A Python implementation of the methodology from Franjic, Mößler, and Schweikert (2025) for detecting and estimating multiple structural breaks in Vector Error Correction Models (VECMs) using Group LASSO with Backward Elimination.

Features

  • Two-Step Estimation: Group LASSO screening followed by Backward Elimination refinement
  • Two Cases Supported:
    • Case 1: Constant adjustment coefficients (α), regime-specific cointegrating vectors (β)
    • Case 2: Both α and β vary across regimes
  • Principal Component Estimation: Efficient estimation of cointegrating parameters
  • Automatic Break Detection: Data-driven selection of break locations and number of breaks
  • Impulse Response Functions: Regime-specific IRF computation
  • Monte Carlo Simulation: Tools for simulation studies and model validation
  • Paper-Style Output (NEW in v0.1.1): Publication-quality tables and figures matching the original paper

Installation

pip install vecmbreak

For plotting and DataFrame support (recommended):

pip install vecmbreak[plotting]

For development installation:

pip install vecmbreak[dev]

Quick Start

import numpy as np
from vecmbreak import VECMBreak, generate_dgp_case1

# Generate simulated data with one structural break
np.random.seed(42)
data = generate_dgp_case1(T=300, break_fractions=[0.5])
Y = data['Y']

# Detect structural breaks
model = VECMBreak(case=1, rank=1)
results = model.fit(Y)

# View results
print(results.summary())
print(f"Detected breaks: {results.breaks}")
print(f"Number of regimes: {results.n_regimes}")

Paper-Style Output (NEW in v0.1.1)

The package now provides functions to generate publication-quality output matching the tables and figures in Franjic, Mößler, and Schweikert (2025).

Monte Carlo Simulation Tables (Tables 1-2 Style)

from vecmbreak import (
    monte_carlo_simulation, 
    format_monte_carlo_table,
    create_monte_carlo_dataframe
)

# Run Monte Carlo simulation
mc_results = monte_carlo_simulation(
    n_replications=1000,
    T=200,
    case=1,
    break_fractions=[0.5],
    seed=42
)

# Format as paper-style table
true_params = {'break_fractions': [0.5]}
print(format_monte_carlo_table(mc_results, true_params, case=1))

# Or export to DataFrame for LaTeX
df = create_monte_carlo_dataframe(mc_results, true_params)
print(df.to_latex())

Output format matches Table 1 from the paper:

================================================================================
Monte Carlo Simulation Results
================================================================================
Number of replications: 1000
True number of breaks: 1
True break fractions: τ1=0.50
--------------------------------------------------------------------------------
pce: 95.4%

Estimated Break Fractions:
  τ1: 0.502 (0.037)

Coefficient Estimates (mean, std):
...
================================================================================

Estimation Results Tables (Tables 4-6 Style)

from vecmbreak import VECMBreak, format_estimation_results

# Fit model
model = VECMBreak(case=2, rank=2)
results = model.fit(Y)

# Paper-style output
print(format_estimation_results(
    results,
    variable_names=['r10y', 'r5y', 'r1y'],
    title="Term Structure Model Results"
))

Time Series Plots (Figure 1 Style)

from vecmbreak import plot_time_series

# Plot with structural break markers
fig = plot_time_series(
    Y, 
    breaks=results.breaks,
    variable_names=['10-year', '5-year', '1-year'],
    title='US Interest Rates with Structural Breaks'
)
fig.savefig('time_series.png', dpi=300)

IRF Grid Plots (Figure 2 Style)

from vecmbreak import compute_irf, plot_irf_grid

# Compute IRFs
irfs = compute_irf(results, horizon=30)

# Create N×N grid plot
fig = plot_irf_grid(
    irfs['irf'],
    variable_names=['y10', 'y5', 'y1'],
    title='Impulse Response Functions'
)
fig.savefig('irf_grid.png', dpi=300)

Full Analysis Report

from vecmbreak import generate_full_report

# Generate comprehensive report
report = generate_full_report(
    results, Y,
    variable_names=['r10y', 'r5y', 'r1y'],
    include_plots=True,
    save_dir='./results/'
)
print(report)

Detailed Usage

Basic Break Detection

from vecmbreak import VECMBreak, fit_vecm_breaks

# Method 1: Using the class
model = VECMBreak(
    case=2,              # Both α and β change
    rank=1,              # Cointegration rank
    k_ar=2,              # VAR lag order
    deterministic='c',   # Include constant
    max_breaks=5         # Maximum breaks to consider
)
results = model.fit(Y)

# Method 2: Using convenience function
results = fit_vecm_breaks(Y, rank=1, case=2)

Accessing Results

# Break locations
print(results.breaks)        # [100, 200] - break indices
print(results.break_dates)   # Alias for breaks

# Estimated parameters
print(results.alpha)         # Adjustment coefficients
print(results.beta)          # Cointegrating vectors (list, one per regime)

# Model diagnostics
print(results.n_breaks)      # Number of detected breaks
print(results.n_regimes)     # Number of regimes
print(results.ic_value)      # Information criterion value
print(results.residuals)     # Model residuals

# Export to dictionary
results_dict = results.to_dict()

Data Generation for Simulation

from vecmbreak import (
    generate_dgp_case1,
    generate_dgp_case2,
    generate_dgp_with_short_run,
    simulate_vecm_breaks
)

# Case 1: Only β changes at breaks
data1 = generate_dgp_case1(
    T=300, 
    break_fractions=[0.33, 0.67],  # Two breaks
    sigma_u=1.0,
    seed=42
)

# Case 2: Both α and β change
data2 = generate_dgp_case2(
    T=300,
    break_fractions=[0.5],
    seed=42
)

# With short-run dynamics
data3 = generate_dgp_with_short_run(
    T=300,
    break_fractions=[0.5],
    k_ar=2,  # VAR(2) short-run dynamics
    seed=42
)

# Custom simulation
alpha_list = [np.array([[-0.3], [0.2]]), np.array([[-0.5], [0.3]])]
beta_list = [np.array([[1.0], [-1.0]]), np.array([[1.0], [-0.8]])]

Y, info = simulate_vecm_breaks(
    T=300,
    N=2,
    alpha_list=alpha_list,
    beta_list=beta_list,
    breaks=[150],
    seed=42
)

Impulse Response Functions

from vecmbreak import compute_irf, plot_irf

# Compute IRFs for each regime
irfs = compute_irf(results, horizon=20)

# Plot IRFs (requires matplotlib)
plot_irf(irfs, variable_names=['y1', 'y2', 'y3'])

Monte Carlo Simulation

from vecmbreak import monte_carlo_simulation

# Run Monte Carlo study
mc_results = monte_carlo_simulation(
    n_simulations=100,
    T=300,
    case=1,
    true_breaks=[150],
    seed=42
)

print(f"Break detection rate: {mc_results['detection_rate']:.2%}")
print(f"Mean break location error: {mc_results['mean_location_error']:.2f}")

Methodology

The package implements the two-step procedure from Franjic, Mößler, and Schweikert (2025):

Step 1: Group LASSO Screening

The VECM is reformulated to allow for potential breaks at each time point:

$$\Delta Y_t = \Pi_t Y_{t-1} + \sum_{i=1}^{K-1} \Gamma_i \Delta Y_{t-i} + \mu_0 + u_t$$

where $\Pi_t = \alpha \beta'_t$ (Case 1) or $\Pi_t = \alpha_t \beta'_t$ (Case 2).

Group LASSO is applied with the modified BIC penalty:

$$\lambda_T = c \cdot T^{-3/4} \sqrt{\log(T)}$$

Step 2: Backward Elimination

Starting from Group LASSO candidates, breaks are sequentially removed if removal improves the information criterion:

$$IC(m) = \log|\hat{\Sigma}_u| + p(m) \cdot \frac{\log(T)}{T}$$

where $p(m)$ is the effective number of parameters.

Parameter Estimation

Regime-specific parameters are estimated via Principal Components:

  1. For each regime, compute $\hat{\beta}j$ from eigenvectors of $S{11,j}^{-1} S_{10,j} S_{00,j}^{-1} S_{01,j}$
  2. Estimate $\hat{\alpha}j = S{01,j} \hat{\beta}_j (\hat{\beta}'j S{11,j} \hat{\beta}_j)^{-1}$

API Reference

Main Classes

  • VECMBreak: Main estimation class
  • VECMBreakResults: Results container with summary methods

Key Functions

  • fit_vecm_breaks(): Convenience function for break detection
  • generate_dgp_case1(): Generate Case 1 DGP data
  • generate_dgp_case2(): Generate Case 2 DGP data
  • simulate_vecm_breaks(): Custom VECM simulation
  • compute_irf(): Impulse response function computation

Utility Functions

  • vec_operator(): Matrix vectorization (column-major)
  • inv_vec_operator(): Inverse vectorization
  • normalize_cointegrating_vectors(): β normalization
  • check_stationarity(): Stationarity verification

References

Franjic, D., Mößler, M., & Schweikert, K. (2025). Multiple Structural Breaks in Vector Error Correction Models. University of Hohenheim Working Paper.

Citation

If you use this package in your research, please cite:

@article{franjic2025vecmbreak,
  title={Multiple Structural Breaks in Vector Error Correction Models},
  author={Franjic, D. and M{\"o}{\ss}ler, M. and Schweikert, K.},
  journal={University of Hohenheim Working Paper},
  year={2025}
}

License

MIT License - see LICENSE for details.

Contributing

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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Changelog

See CHANGELOG.md for version history.

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

vecmbreak-0.1.1.tar.gz (70.5 kB view details)

Uploaded Source

Built Distribution

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

vecmbreak-0.1.1-py3-none-any.whl (73.1 kB view details)

Uploaded Python 3

File details

Details for the file vecmbreak-0.1.1.tar.gz.

File metadata

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

File hashes

Hashes for vecmbreak-0.1.1.tar.gz
Algorithm Hash digest
SHA256 3674a07e6e9d1a3c19ab9bc5edd2e9dc5f4c8f794824c9b8c6627ff3521af7f4
MD5 1d811c870c32b2a1cce4d52cc5dbdea4
BLAKE2b-256 cda73a9d46f707be96acce343f351ebfef610ad77dbe3e42d2fbfcb469c329f2

See more details on using hashes here.

File details

Details for the file vecmbreak-0.1.1-py3-none-any.whl.

File metadata

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

File hashes

Hashes for vecmbreak-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 db8da71851426fd13e5dd2a81b1ccbc2c0a6ca7caa1223c0b6c84862359e5c1e
MD5 61a10e7ea2492243acf8ce5b6249839e
BLAKE2b-256 c1cf35d01f3526df22f8f0a036a360d47a01eb174290370c0fe9470a8f13637e

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