Skip to main content

dyncusum

Python 3.8+ License: MIT

CUSUM Tests for Structural Change in Dynamic Models

A Python library implementing the CUSUM tests for structural change as described in:

Krämer, W., Ploberger, W., & Alt, R. (1988). "Testing for Structural Change in Dynamic Models." Econometrica, Vol. 56, No. 6, pp. 1355-1369.

Overview

This library provides a complete implementation of the CUSUM (Cumulative Sum) test for detecting structural change in linear regression models with lagged dependent variables. The implementation follows the original paper exactly, including:

  1. Dynamic CUSUM Test: The straightforward CUSUM test applied to dynamic models
  2. Dufour Test: Modified procedure from Dufour (1982) that first eliminates dynamics
  3. Critical Values: Both tabulated values from BDE (1975) and Monte Carlo simulation
  4. Power Analysis: Tools to analyze test power under various structural change scenarios
  5. Visualization: Publication-quality plots for CUSUM processes
  6. Tables: Formatted output for academic publications

Installation

pip install dyncusum

Or install from source:

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

Quick Start

Basic Usage

import numpy as np
from dyncusum import dynamic_cusum_test, plot_cusum, print_test_summary

# Generate sample data
np.random.seed(42)
T = 100
y = np.zeros(T)
u = np.random.normal(0, 1, T)

# AR(1) process: y_t = 0.5 * y_{t-1} + u_t
for t in range(1, T):
    y[t] = 0.5 * y[t-1] + u[t]

# Prepare data for test
y_current = y[1:]
y_lagged = y[:-1]
X = np.ones((T-1, 1))  # Constant term

# Perform the Dynamic CUSUM test
result = dynamic_cusum_test(y_current, X, y_lagged, significance_level=0.05)

# Print summary
print_test_summary(result)

# Plot the CUSUM process
fig = plot_cusum(result, save_path='cusum_plot.png')

Comparing Tests

from dyncusum import dynamic_cusum_test, dufour_test, plot_comparison

# Run both tests
result_dc = dynamic_cusum_test(y_current, X, y_lagged)
result_duf = dufour_test(y_current, X, y_lagged)

# Compare results
fig = plot_comparison(result_dc, result_duf, save_path='comparison.png')

Theoretical Background

The Model

The library considers the dynamic linear regression model:

$$y_t = \gamma y_{t-1} + \beta_1 x_{t1} + \cdots + \beta_K x_{tK} + u_t \quad (t = 1, \ldots, T)$$

where:

  • $y_t$ is the dependent variable
  • $\gamma$ is the coefficient of the lagged dependent variable ($|\gamma| < 1$)
  • $x_{t1}, \ldots, x_{tK}$ are exogenous regressors
  • $u_t$ are iid$(0, \sigma^2)$ disturbances

The CUSUM Test

The test statistic is based on recursive residuals:

$$w_r = (y_r - z'_r \hat{\delta}^{(r-1)}) / f_r$$

where $f_r = (1 + z'_r(Z^{(r-1)'}Z^{(r-1)})^{-1}z_r)^{1/2}$

The CUSUM process is:

$$W^{(r)} = \frac{1}{\hat{\sigma}} \sum_{t=K+2}^{r} w_t$$

And the test statistic is:

$$S = \max_{K+1 < r \leq T} \left| \frac{W^{(r)}}{\sqrt{T-K-1}} \right| / \left(1 + 2\frac{r-K-1}{T-K-1}\right)$$

Key Theorems

Theorem 1: Both the Dynamic CUSUM test and the Dufour test retain their asymptotic nominal significance levels in dynamic models.

Theorem 2: If the structural change $g(z)$ is orthogonal to the mean regressor $d$ for almost all $z$, the limiting rejection probability equals the nominal significance level (no power).

Critical Values

Critical values are based on Brownian motion theory (BDE, 1975):

Significance Level Critical Value
0.01 1.143
0.025 1.035
0.05 0.948
0.10 0.850

API Reference

Core Functions

dynamic_cusum_test(y, X, y_lagged=None, significance_level=0.05, sigma_method='harvey', n_simulations=10000)

Perform the Dynamic CUSUM test for structural change.

Parameters:

  • y: Dependent variable vector
  • X: Exogenous regressors matrix
  • y_lagged: Lagged dependent variable (optional)
  • significance_level: Significance level (default: 0.05)
  • sigma_method: 'harvey' or 'ols' (default: 'harvey')
  • n_simulations: Monte Carlo simulations for p-value

Returns: CUSUMResult object

dufour_test(y, X, y_lagged, significance_level=0.05, sigma_method='harvey', n_simulations=10000)

Perform the Dufour (1982) modified CUSUM test.

Power Analysis

monte_carlo_power(gamma, psi, z_star, b, T=120, n_simulations=1000, ...)

Compute power via Monte Carlo simulation.

replicate_table_1(T=120, n_simulations=1000, ...)

Replicate Table I from the original paper.

Visualization

plot_cusum(result, title=None, figsize=(10, 6), ...)

Plot the CUSUM process with critical boundaries.

create_summary_figure(result, figsize=(14, 10), ...)

Create a comprehensive summary figure with multiple panels.

Examples

Detecting Structural Change

import numpy as np
from dyncusum import dynamic_cusum_test, generate_dynamic_data

# Generate data with a structural break at t = 60
T = 100
gamma = 0.5
beta = np.array([2, 10])

# Break: coefficients change by 20% after observation 60
structural_break = {
    'z_star': 0.6,  # Break at 60% of sample
    'delta_delta': np.array([0, 0.4, 2.0])  # Change in [γ, β_1, β_2]
}

y, X, y_lagged = generate_dynamic_data(T, gamma, beta, 
                                        structural_break=structural_break, 
                                        seed=42)

# Test for structural change
result = dynamic_cusum_test(y, X, y_lagged)
print(f"Test Statistic: {result.statistic:.4f}")
print(f"P-value: {result.p_value:.4f}")
print(f"Reject H0: {result.reject_null}")

Power Analysis

from dyncusum import analyze_power_vs_angle, plot_power_curve

# Analyze how power varies with the angle ψ
results = analyze_power_vs_angle(gamma=0.0, z_star=0.5, b=12, T=120)

# Plot power curve
fig = plot_power_curve(
    np.array(results['angles']), 
    np.array(results['powers']),
    save_path='power_curve.png'
)

Replicating Paper Results

The library can replicate the Monte Carlo results from Table I:

from dyncusum import replicate_table_1, format_power_table

# Note: This takes several hours with default settings
results = replicate_table_1(
    T=120,
    n_simulations=100,  # Use 1000 for publication
    gamma_values=[-0.5, 0, 0.5],
    psi_values=[0, 30, 60, 90],
    z_star_values=[0.3, 0.5, 0.7],
    b_values=[3, 6, 12]
)

# Format as table
print(format_power_table(results))

Citation

If you use this library in your research, please cite both the original paper and this implementation:

@article{kramer1988testing,
  title={Testing for Structural Change in Dynamic Models},
  author={Kr{\"a}mer, Walter and Ploberger, Werner and Alt, Raimund},
  journal={Econometrica},
  volume={56},
  number={6},
  pages={1355--1369},
  year={1988}
}

@software{roudane2024dyncusum,
  author = {Roudane, Merwan},
  title = {dyncusum: CUSUM Tests for Structural Change in Dynamic Models},
  year = {2024},
  url = {https://github.com/merwanroudane/cusum}
}

References

  • Brown, R. L., Durbin, J., & Evans, J. M. (1975). Techniques for Testing the Constancy of Regression Relationships over Time. Journal of the Royal Statistical Society, Series B, 37, 149-163.
  • Dufour, J. M. (1982). Recursive Stability Analysis of Linear Regression Relationships. Journal of Econometrics, 19, 31-76.
  • Harvey, A. (1975). Comment on the Paper by Brown, Durbin and Evans. Journal of the Royal Statistical Society, B, 37, 179-180.

License

MIT License - see LICENSE file for details.

Author

Dr. Merwan Roudane

Contributing

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

Metadata

Release files for dyncusum 1.0.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 dyncusum 1.0.0
File Size Uploaded Provenance
dyncusum-1.0.0.tar.gz 32.6 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 63.4 kB

Release files / dyncusum-1.0.0.tar.gz

Download URL dyncusum-1.0.0.tar.gz
Size 32.6 kB
Tags Source
SHA-256 checksum
How to use checksums
84feb0c0fb540f6ec17fc25318e299039b9bfb2d11c13a468b6b2ed992d42d32
BLAKE2b-256 checksum
How to use checksums
2a33f64295a35e7995d995d4f44502c2ca5b1e00b930dd3f9f160d73bd4e9e6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release files / dyncusum-1.0.0-py3-none-any.whl

Download URL dyncusum-1.0.0-py3-none-any.whl
Size 30.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4a25555823c62e35ed6a2387da052fbf575cb5303ed9e254e4c4ec62f8878ed9
BLAKE2b-256 checksum
How to use checksums
f767101bfce58d9a3d97524c357445623fbc78060a750eaba97504a829b2c914
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Security for dyncusum 1.0.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.0.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