Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `AndrewsPlobergerTest.from_model()` class method for OLS, AR, and ADL models
- `.andrews_ploberger()` convenience method on OLS, AR, and ADL models
- 56 new tests (718 total)
- **Markov regime-switching models**
- `MarkovRegression` class wrapping `statsmodels.tsa.regime_switching.MarkovRegression` with regime ordering, multi-start optimization, and regimes-style results
- `MarkovAR` class wrapping `statsmodels.tsa.regime_switching.MarkovAutoregression` with switching AR coefficients
- `MarkovADL` class: Markov switching ADL model (builds ADL design matrix and passes to statsmodels `MarkovRegression`)
- `MarkovSwitchingResultsBase`, `MarkovRegressionResults`, `MarkovARResults`, `MarkovADLResults` dataclasses with transition matrix, smoothed/filtered/predicted probabilities, expected durations, regime assignments, and information criteria
- `RestrictedMarkovRegression` and `RestrictedMarkovAR` for imposing fixed transition probability entries via softmax redistribution
- `RestrictedMarkovRegression.non_recurring()` / `RestrictedMarkovAR.non_recurring()` factory methods for Chib (1998) upper-triangular non-recurring transition structure
- `NonRecurringRegimeTest`: LR test of H0 (non-recurring structural break) vs H1 (unrestricted Markov switching) with chi-bar-squared or bootstrap p-values
- `SequentialRestrictionTest`: GETS-style algorithm for sequentially restricting transition probabilities with Holm-Bonferroni multiple testing correction
- `RegimeNumberSelection`: select number of regimes K by information criteria (AIC/BIC/HQIC) or sequential LRT, with K=1 using standard OLS/AR
- 5 Markov visualization functions: `plot_smoothed_probabilities`, `plot_regime_shading`, `plot_transition_matrix`, `plot_parameter_time_series`, `plot_ic`
- `.markov_switching(k_regimes)` convenience method on `OLS`, `AR`, and `ADL` for one-step conversion to Markov switching
- `MarkovRegression.from_model()`, `MarkovAR.from_model()`, `MarkovADL.from_model()` class methods for explicit construction from existing models
- 98 new tests (816 total)

### Changed

Expand Down
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ A Python package for structural break detection and estimation in time-series ec
- **Chow Test**: Test for structural breaks at known break points with standard and predictive variants
- **CUSUM Tests**: CUSUM and CUSUM-SQ tests for parameter and variance instability (Brown, Durbin, Evans, 1975)
- **Andrews-Ploberger Test**: SupF, ExpF, and AveF tests for a structural break at unknown date (Andrews, 1993; Andrews & Ploberger, 1994)
- **Markov Regime-Switching**: Markov switching regression, AR, and ADL models with regime number selection and restricted transitions
- **Time-Series Models**: AR, ADL, OLS with HAC standard errors and known break support
- **ADL Models**: Autoregressive Distributed Lag models with flexible lag specification and distributed lag diagnostics
- **Rolling & Recursive Estimation**: Track parameter evolution with fixed or expanding windows
Expand Down Expand Up @@ -191,6 +192,64 @@ ap_results = model.andrews_ploberger()
print(ap_results.summary())
```

### Markov Regime-Switching Models

Markov switching models allow parameters to change across unobserved regimes governed by a Markov chain:

```python
import numpy as np
import regimes as rg

# Simulate data with regime-switching mean
rng = np.random.default_rng(42)
y = np.concatenate([rng.normal(0, 1, 100), rng.normal(3, 1, 100), rng.normal(0, 1, 100)])

# Fit Markov switching regression
model = rg.MarkovRegression(y, k_regimes=2)
results = model.fit()
print(results.summary())

# Visualize smoothed probabilities and regime shading
results.plot_smoothed_probabilities()
results.plot_regime_shading(y=y)
```

Use the convenience method on any existing model:

```python
# One-step conversion from OLS to Markov switching
ols_model = rg.OLS(y, np.ones((300, 1)), has_constant=False)
ms_results = ols_model.markov_switching(k_regimes=2)

# Also works with AR and ADL models
ar_model = rg.AR(y, lags=1)
ms_ar = ar_model.markov_switching(k_regimes=2)
```

Select the number of regimes using information criteria or sequential likelihood ratio tests:

```python
# Regime number selection
selection = rg.RegimeNumberSelection(y, k_max=4, method="bic")
sel_results = selection.fit()
print(sel_results.summary())
sel_results.plot_ic()
```

Test whether transitions are non-recurring (structural break) or allow recurrence:

```python
# Non-recurring regime test (Chib 1998 structure)
nr_test = rg.NonRecurringRegimeTest(y, k_regimes=2)
nr_results = nr_test.fit()
print(nr_results.summary())

# Fit with restricted (non-recurring) transitions directly
restricted = rg.RestrictedMarkovRegression.non_recurring(y, k_regimes=3)
restricted_results = restricted.fit()
results.plot_transition_matrix()
```

### OLS with HAC Standard Errors

```python
Expand Down Expand Up @@ -394,13 +453,19 @@ fig, axes = rg.plot_residual_acf(results, nlags=15)
| `OLS` | Ordinary Least Squares with robust standard errors and variable-specific breaks |
| `AR` | Autoregressive model with break support |
| `ADL` | Autoregressive Distributed Lag model with flexible lag specification |
| `MarkovRegression` | Markov regime-switching regression (wraps statsmodels) |
| `MarkovAR` | Markov regime-switching autoregression |
| `MarkovADL` | Markov regime-switching ADL model |
| `RestrictedMarkovRegression` | Markov regression with restricted transition probabilities |
| `RestrictedMarkovAR` | Markov AR with restricted transition probabilities |

All models (`OLS`, `AR`, `ADL`) have:
- `.bai_perron()` method for integrated break detection
- `.chow_test(break_points)` method for testing breaks at known dates
- `.cusum_test()` method for CUSUM parameter instability test
- `.cusum_sq_test()` method for CUSUM-SQ variance instability test
- `.andrews_ploberger()` method for testing breaks at unknown date
- `.markov_switching(k_regimes)` method for one-step Markov switching estimation
- `.rolling(window)` method for rolling window estimation
- `.recursive(min_nobs)` method for recursive (expanding window) estimation

Expand Down Expand Up @@ -432,6 +497,9 @@ All models (`OLS`, `AR`, `ADL`) have:
| `CUSUMTest` | CUSUM test for parameter instability via recursive residuals |
| `CUSUMSQTest` | CUSUM-of-squares test for variance instability |
| `AndrewsPlobergerTest` | Andrews-Ploberger SupF/ExpF/AveF test for break at unknown date |
| `NonRecurringRegimeTest` | LR test: non-recurring (structural break) vs unrestricted Markov transitions |
| `SequentialRestrictionTest` | GETS-style sequential restriction of transition probabilities |
| `RegimeNumberSelection` | Select number of regimes by IC (AIC/BIC/HQIC) or sequential LRT |

**Key methods:**
- `BaiPerronTest.from_model(model)` - Create test from OLS or AR model
Expand Down Expand Up @@ -459,6 +527,11 @@ All models (`OLS`, `AR`, `ADL`) have:
| `plot_cusum` | CUSUM statistic with critical boundaries |
| `plot_cusum_sq` | CUSUM-SQ statistic with critical boundaries |
| `plot_f_sequence` | Andrews-Ploberger F-statistic sequence with critical value line |
| `plot_smoothed_probabilities` | Smoothed regime probabilities (one subplot per regime) |
| `plot_regime_shading` | Time series with regime-colored background shading |
| `plot_transition_matrix` | Heatmap of transition probability matrix |
| `plot_parameter_time_series` | Regime-dependent parameter as step function over time |
| `plot_ic` | Information criteria vs number of regimes |

### Style Utilities

Expand All @@ -484,7 +557,7 @@ All regression models support multiple covariance estimators:

## Testing

The package includes a comprehensive test suite with 88% coverage (718 tests):
The package includes a comprehensive test suite with 816 tests:

```bash
# Run all tests
Expand Down Expand Up @@ -516,6 +589,9 @@ regimes uses [Hypothesis](https://hypothesis.readthedocs.io/) for property-based
- Chow, G. C. (1960). Tests of equality between sets of coefficients in two linear regressions. *Econometrica*, 28(3), 591-605.
- Andrews, D. W. K. (1993). Tests for parameter instability and structural change with unknown change point. *Econometrica*, 61(4), 821-856.
- Andrews, D. W. K. & Ploberger, W. (1994). Optimal tests when a nuisance parameter is present only under the alternative. *Econometrica*, 62(6), 1383-1414.
- Hamilton, J. D. (1989). A new approach to the economic analysis of nonstationary time series and the business cycle. *Econometrica*, 57(2), 357-384.
- Chib, S. (1998). Estimation and comparison of multiple change-point models. *Journal of Econometrics*, 86(2), 221-241.
- Andrews, D. W. K. (2001). Testing when a parameter is on the boundary of the maintained hypothesis. *Econometrica*, 69(3), 683-734.

## License

Expand Down
13 changes: 11 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,16 @@ Core package structure, OLS/AR models, Bai-Perron test, basic visualization, CI/
- [x] CUSUM / CUSUM-SQ tests
- [x] Andrews-Ploberger test
- [ ] Unit root tests (ADF, KPSS, Phillips-Perron)
- [ ] Markov-switching models
- [x] Markov-switching models
- [x] `MarkovRegression`, `MarkovAR`, `MarkovADL` wrapping statsmodels
- [x] `MarkovSwitchingResultsBase`, `MarkovRegressionResults`, `MarkovARResults`, `MarkovADLResults`
- [x] Restricted transition matrices (`RestrictedMarkovRegression`, `RestrictedMarkovAR`)
- [x] Non-recurring regime test (`NonRecurringRegimeTest`) — Chib (1998) structure
- [x] Sequential restriction testing (`SequentialRestrictionTest`) — GETS-style algorithm
- [x] Regime number selection (`RegimeNumberSelection`) — IC + sequential LRT
- [x] 5 Markov visualization functions: `plot_smoothed_probabilities`, `plot_regime_shading`, `plot_transition_matrix`, `plot_parameter_time_series`, `plot_ic`
- [x] `.markov_switching()` convenience methods on OLS, AR, ADL
- [x] 98 new tests (816 total)
- [ ] *(Tentative)* Time-varying parameter models
- [ ] Target: 85%+ test coverage

Expand Down Expand Up @@ -101,7 +110,7 @@ These features are deferred and may be added in future versions based on need:
|---------|--------|-------------|
| 0.1.0 | Complete | Foundation: OLS, AR, Bai-Perron, visualization, CI/CD |
| 0.2.0 | Complete | Estimation tools: rolling/recursive, ADL, diagnostics, style system, 88% coverage |
| 0.3.0 | In Progress | Structural break tests (Chow ✓, CUSUM ✓, Andrews-Ploberger ✓), Markov-switching |
| 0.3.0 | In Progress | Structural break tests (Chow ✓, CUSUM ✓, Andrews-Ploberger ✓), Markov-switching ✓, unit root tests, TVP models |
| 0.4.0 | Planned | Autometrics-inspired model selection, step-indicator saturation |
| 0.5.0 | Planned | Real data examples, example notebooks, Sphinx docs, PyPI stable release |
| 0.6.0 | Planned | Advanced models: VAR, cointegration, panel data, bootstrap |
Expand Down
40 changes: 40 additions & 0 deletions src/regimes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,29 @@
CUSUMTest,
DiagnosticsResults,
DiagnosticTestResult,
MarkovADL,
MarkovADLResults,
MarkovAR,
MarkovARResults,
MarkovRegression,
MarkovRegressionResults,
MarkovSwitchingResultsBase,
NonRecurringRegimeTest,
NonRecurringRegimeTestResults,
OLSResults,
RecursiveADL,
RecursiveADLResults,
RecursiveAR,
RecursiveARResults,
RecursiveOLS,
RecursiveOLSResults,
RegimeNumberSelection,
RegimeNumberSelectionResults,
RegimesModelBase,
RegimesResultsBase,
RegressionResultsBase,
RestrictedMarkovAR,
RestrictedMarkovRegression,
RollingADL,
RollingADLResults,
RollingAR,
Expand All @@ -69,6 +82,8 @@
RollingOLS,
RollingOLSResults,
RollingResultsBase,
SequentialRestrictionResults,
SequentialRestrictionTest,
TimeSeriesModelBase,
adl_summary_by_regime,
ar_summary_by_regime,
Expand All @@ -79,12 +94,17 @@
plot_cusum_sq,
plot_diagnostics,
plot_f_sequence,
plot_ic,
plot_parameter_time_series,
plot_params_over_time,
plot_regime_means,
plot_regime_shading,
plot_residual_acf,
plot_residual_distribution,
plot_rolling_coefficients,
plot_scaled_residuals,
plot_smoothed_probabilities,
plot_transition_matrix,
summary_by_regime,
)

Expand All @@ -108,16 +128,29 @@
"CovType",
"DiagnosticTestResult",
"DiagnosticsResults",
"MarkovADL",
"MarkovADLResults",
"MarkovAR",
"MarkovARResults",
"MarkovRegression",
"MarkovRegressionResults",
"MarkovSwitchingResultsBase",
"NonRecurringRegimeTest",
"NonRecurringRegimeTestResults",
"OLSResults",
"RecursiveADL",
"RecursiveADLResults",
"RecursiveAR",
"RecursiveARResults",
"RecursiveOLS",
"RecursiveOLSResults",
"RegimeNumberSelection",
"RegimeNumberSelectionResults",
"RegimesModelBase",
"RegimesResultsBase",
"RegressionResultsBase",
"RestrictedMarkovAR",
"RestrictedMarkovRegression",
"RollingADL",
"RollingADLResults",
"RollingAR",
Expand All @@ -127,6 +160,8 @@
"RollingOLS",
"RollingOLSResults",
"RollingResultsBase",
"SequentialRestrictionResults",
"SequentialRestrictionTest",
"TimeSeriesModelBase",
"__version__",
"adl_summary_by_regime",
Expand All @@ -138,11 +173,16 @@
"plot_cusum_sq",
"plot_diagnostics",
"plot_f_sequence",
"plot_ic",
"plot_parameter_time_series",
"plot_params_over_time",
"plot_regime_means",
"plot_regime_shading",
"plot_residual_acf",
"plot_residual_distribution",
"plot_rolling_coefficients",
"plot_scaled_residuals",
"plot_smoothed_probabilities",
"plot_transition_matrix",
"summary_by_regime",
]
44 changes: 44 additions & 0 deletions src/regimes/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@
# Diagnostics
from regimes.diagnostics import DiagnosticsResults, DiagnosticTestResult

# Markov switching models
from regimes.markov import (
MarkovADL,
MarkovADLResults,
MarkovAR,
MarkovARResults,
MarkovRegression,
MarkovRegressionResults,
MarkovSwitchingResultsBase,
NonRecurringRegimeTest,
NonRecurringRegimeTestResults,
RegimeNumberSelection,
RegimeNumberSelectionResults,
RestrictedMarkovAR,
RestrictedMarkovRegression,
SequentialRestrictionResults,
SequentialRestrictionTest,
)

# Model base classes (for extension)
from regimes.models import (
ADL,
Expand Down Expand Up @@ -73,12 +92,17 @@
plot_cusum_sq,
plot_diagnostics,
plot_f_sequence,
plot_ic,
plot_parameter_time_series,
plot_params_over_time,
plot_regime_means,
plot_regime_shading,
plot_residual_acf,
plot_residual_distribution,
plot_rolling_coefficients,
plot_scaled_residuals,
plot_smoothed_probabilities,
plot_transition_matrix,
)

__all__ = [
Expand All @@ -101,16 +125,29 @@
"CovType",
"DiagnosticTestResult",
"DiagnosticsResults",
"MarkovADL",
"MarkovADLResults",
"MarkovAR",
"MarkovARResults",
"MarkovRegression",
"MarkovRegressionResults",
"MarkovSwitchingResultsBase",
"NonRecurringRegimeTest",
"NonRecurringRegimeTestResults",
"OLSResults",
"RecursiveADL",
"RecursiveADLResults",
"RecursiveAR",
"RecursiveARResults",
"RecursiveOLS",
"RecursiveOLSResults",
"RegimeNumberSelection",
"RegimeNumberSelectionResults",
"RegimesModelBase",
"RegimesResultsBase",
"RegressionResultsBase",
"RestrictedMarkovAR",
"RestrictedMarkovRegression",
"RollingADL",
"RollingADLResults",
"RollingAR",
Expand All @@ -120,6 +157,8 @@
"RollingOLS",
"RollingOLSResults",
"RollingResultsBase",
"SequentialRestrictionResults",
"SequentialRestrictionTest",
"TimeSeriesModelBase",
"adl_summary_by_regime",
"ar_summary_by_regime",
Expand All @@ -130,11 +169,16 @@
"plot_cusum_sq",
"plot_diagnostics",
"plot_f_sequence",
"plot_ic",
"plot_parameter_time_series",
"plot_params_over_time",
"plot_regime_means",
"plot_regime_shading",
"plot_residual_acf",
"plot_residual_distribution",
"plot_rolling_coefficients",
"plot_scaled_residuals",
"plot_smoothed_probabilities",
"plot_transition_matrix",
"summary_by_regime",
]
Loading
Loading