Skip to main content

Scope-bounded structural-break workflows for Python with inspectable result objects.

Project description

xtbreak

Structural break detection and estimation for time series and panel data in Python.

"Did the relationship change? When? By how much?" — The three questions every applied researcher faces when parameters might not be constant.


Why Structural Breaks Matter

Economic and financial time series rarely stay stable forever. Policy shifts, crises, and regime changes cause the data-generating process to break: coefficients jump, variances shift, trends bend. Ignoring these breaks means your regressions are misspecified, your confidence intervals are wrong, and policy conclusions drawn from pooled samples are unreliable.

Structural break methods ask: did something change, when, and by how much? The Bai–Perron (1998, 2003) framework answers these questions with formal test statistics, break-date estimation via global SSR minimization, and asymptotic confidence intervals for each break location.

Despite two decades of theoretical maturity, Python had no dedicated implementation that returns break dates, confidence intervals, regime coefficients, test statistics, and selection decisions as programmatic objects. xtbreak fills that gap.

What xtbreak Does

  • Test for structural breaks — SupF, UDmax, and sequential tests with known or unknown break counts
  • Estimate break dates — global SSR minimization via the Bai–Perron dynamic programming algorithm
  • Confidence intervals — asymptotic CIs for each estimated break location
  • Break number selection — sequential testing and information criteria (BIC/LWZ)
  • Regime statistics — per-regime coefficients, standard errors, and sample sizes in structured result objects
  • Panel data support — partial structural breaks with cross-sectional dependence factors
  • Serializable results — every result object is JSON-safe for logging, storage, and pipeline integration
  • Pure Python + NumPy — no compiled extensions, no Stata dependency, installs anywhere Python runs

Installation

pip install git+https://github.com/gorgeousfish/pyxtbreak.git#subdirectory=xtbreak-py

Optional dependencies for full functionality:

pip install scipy    # p-values for Chow tests
pip install pandas   # DataFrame input support

Quick Example

Detect and estimate a single mean shift in a simulated series:

import numpy as np
from xtbreak import test, estimate

# Simulate: mean jumps from 0 to 3 at observation 50
np.random.seed(2024)
y = np.concatenate([np.random.normal(0, 1, 50), np.random.normal(3, 1, 50)])
x = np.ones((100, 1))

# Test for one structural break
result = test(y, x, breaks=1, trimming=0.15, vce='ssr')
print(f"SupF statistic: {result.statistic:.2f}")
# SupF statistic: 232.47

# Estimate the break location
est = estimate(y, x, breaks=1, trimming=0.15)
brk = est.break_estimates[0]
print(f"Estimated break at index: {brk.index}")
# Estimated break at index: 50

print(f"95% confidence interval: {brk.ci_index}")
# 95% confidence interval: (48, 52)

# Inspect regime-specific parameters
for rs in est.regime_statistics:
    print(f"Regime {rs.regime_index}: coef={rs.coefficients[0]:.4f}  "
          f"se={rs.standard_errors[0]:.4f}  n={rs.n_observations}")
# Regime 0: coef=-0.0002  se=0.1369  n=50
# Regime 1: coef=3.0600  se=0.1468  n=50

The estimated break at index 50 exactly recovers the true change point. The confidence interval is tight (48–52), and regime coefficients cleanly separate the two means.

What You Get Back

Every call returns a structured result object, not a print dump. You can:

# Access break dates programmatically
est.break_estimates[0].index       # 50
est.break_estimates[0].ci_index    # (48, 52)

# Iterate over regimes
for rs in est.regime_statistics:
    rs.coefficients   # np.ndarray
    rs.standard_errors
    rs.n_observations

# Serialize for reports
import json
json.dumps(est.to_dict())  # JSON-safe dict

No screen-scraping. No regex on printed output. Just Python objects.

When to Use xtbreak

Situation xtbreak fits? Notes
Testing whether a regression relationship changed at unknown dates Yes Core use case — SupF and UDmax tests
Estimating when structural breaks occurred Yes Global SSR minimization with CIs
Choosing how many breaks best describe the data Yes Sequential tests + BIC/LWZ selection
Panel data with common break dates Yes Partial break model with factor CSD
Cointegration or unit-root break testing No Use dedicated cointegration packages
Real-time online change-point detection No Designed for retrospective full-sample analysis
Markov-switching or threshold models No Different modeling framework

API Overview

Function Purpose Returns
test() Test null of no breaks against alternative of k breaks TestResult with statistic, critical values
estimate() Estimate break locations and regime parameters EstimateResult with breaks, CIs, regime stats
select() Determine number of breaks via sequential/information criteria SelectResult with selected count, step details

All three functions accept NumPy arrays directly. Panel structure is specified via entity and time parameters.

For complete API documentation, see docs/api-reference.md.
For extended usage patterns (selection, robust VCE, panel, serialization), see docs/quickstart.md.
For runnable example scripts, see examples/.

Current Boundaries

  • Retrospective analysis only — requires the full sample; not designed for streaming/online detection
  • Linear models — breaks in linear regression coefficients; nonlinear models are out of scope
  • No unit-root/cointegration break tests — focuses on Bai–Perron structural change in stationary or trend-stationary settings
  • Trimming constraint — minimum regime length is controlled by the trimming parameter (default 15%); very short regimes near endpoints cannot be detected
  • scipy optional — without scipy, Chow test p-values are unavailable (test statistics still computed)

Methodology

This package implements the econometric framework from:

  • Bai, J., & Perron, P. (1998). Estimating and testing linear models with multiple structural changes. Econometrica, 47–78.
  • Bai, J., & Perron, P. (2003). Computation and analysis of multiple structural change models. Journal of Applied Econometrics, 18(1), 1–22.
  • Ditzen, J., Karavias, Y., & Westerlund, J. (2025). Testing and estimating structural breaks in time series and panel data in Stata. The Stata Journal, 25(3), 526–560.
  • Ditzen, J., Karavias, Y., & Westerlund, J. (2022). Multiple structural breaks in interactive effects panel data and the impact of quantitative easing on bank lending. arXiv preprint arXiv:2211.06707.

Citation

@software{xtbreak_python2026,
  title   = {xtbreak: Structural Break Detection and Estimation in Python},
  author  = {Cai, Xuanyu and Xu, Wenli},
  year    = {2026},
  version = {0.1.0},
  url     = {https://github.com/gorgeousfish/xtbreak-py}
}

@article{ditzen2025xtbreak,
  title   = {Testing and estimating structural breaks in time series and panel data in {Stata}},
  author  = {Ditzen, Jan and Karavias, Yiannis and Westerlund, Joakim},
  journal = {The Stata Journal},
  volume  = {25},
  number  = {3},
  pages   = {526--560},
  year    = {2025}
}

Authors

Python Implementation:

Methodology:

  • Jan Ditzen, Free University of Bozen-Bolzano
  • Yiannis Karavias, University of Birmingham
  • Joakim Westerlund, Lund University

License

AGPL-3.0

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

xtbreak-0.1.0.tar.gz (95.2 kB view details)

Uploaded Source

Built Distribution

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

xtbreak-0.1.0-py3-none-any.whl (101.1 kB view details)

Uploaded Python 3

File details

Details for the file xtbreak-0.1.0.tar.gz.

File metadata

  • Download URL: xtbreak-0.1.0.tar.gz
  • Upload date:
  • Size: 95.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for xtbreak-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8ade5574a3292a882701bad8108ef04668094ce00937f527a18efd97dba8b40b
MD5 de2b2755520e8bdb348374feff539276
BLAKE2b-256 a912c2c255876a4ca046f8f8868487ba4a3bd7536575069557c512b608532d23

See more details on using hashes here.

File details

Details for the file xtbreak-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: xtbreak-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 101.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for xtbreak-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4eb9a888ee24c64fa7f8a821b5411b2ded69842b5dee09d60d497fc24ab24346
MD5 0de8aa26bbca7856955d15aac2de0030
BLAKE2b-256 b5a985f6f500acd68f370b7e6939502879d04f123aadac39cb53652eac72446c

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