pycointbreak 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycointbreak-0.1.0/LICENSE +21 -0
- pycointbreak-0.1.0/MANIFEST.in +10 -0
- pycointbreak-0.1.0/PKG-INFO +316 -0
- pycointbreak-0.1.0/README.md +272 -0
- pycointbreak-0.1.0/pycointbreak/__init__.py +237 -0
- pycointbreak-0.1.0/pycointbreak/breakpoint.py +238 -0
- pycointbreak-0.1.0/pycointbreak/critical_values.py +281 -0
- pycointbreak-0.1.0/pycointbreak/fracdiff.py +244 -0
- pycointbreak-0.1.0/pycointbreak/gph.py +127 -0
- pycointbreak-0.1.0/pycointbreak/hassler_breitung.py +456 -0
- pycointbreak-0.1.0/pycointbreak/plots.py +363 -0
- pycointbreak-0.1.0/pycointbreak/reporting.py +240 -0
- pycointbreak-0.1.0/pycointbreak/rsv_tests.py +531 -0
- pycointbreak-0.1.0/pycointbreak/simulate.py +194 -0
- pycointbreak-0.1.0/pycointbreak.egg-info/PKG-INFO +316 -0
- pycointbreak-0.1.0/pycointbreak.egg-info/SOURCES.txt +20 -0
- pycointbreak-0.1.0/pycointbreak.egg-info/dependency_links.txt +1 -0
- pycointbreak-0.1.0/pycointbreak.egg-info/requires.txt +15 -0
- pycointbreak-0.1.0/pycointbreak.egg-info/top_level.txt +1 -0
- pycointbreak-0.1.0/pyproject.toml +60 -0
- pycointbreak-0.1.0/setup.cfg +4 -0
- pycointbreak-0.1.0/setup.py +72 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Dr. Merwan Roudane
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pycointbreak
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tests for breaks in fractional cointegration (Hassler & Breitung 2006; Rodrigues, Sibbertsen & Voges 2019).
|
|
5
|
+
Home-page: https://github.com/merwanroudane/pycointbreak
|
|
6
|
+
Author: Dr. Merwan Roudane
|
|
7
|
+
Author-email: "Dr. Merwan Roudane" <merwanroudane920@gmail.com>
|
|
8
|
+
Maintainer-email: "Dr. Merwan Roudane" <merwanroudane920@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Project-URL: Homepage, https://github.com/merwanroudane/pycointbreak
|
|
11
|
+
Project-URL: Repository, https://github.com/merwanroudane/pycointbreak
|
|
12
|
+
Project-URL: Bug Tracker, https://github.com/merwanroudane/pycointbreak/issues
|
|
13
|
+
Keywords: econometrics,fractional cointegration,structural breaks,time series,Hassler-Breitung,Rodrigues-Sibbertsen-Voges,long memory,LM test
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: numpy>=1.22
|
|
28
|
+
Requires-Dist: pandas>=1.4
|
|
29
|
+
Requires-Dist: scipy>=1.8
|
|
30
|
+
Requires-Dist: statsmodels>=0.13
|
|
31
|
+
Requires-Dist: matplotlib>=3.5
|
|
32
|
+
Requires-Dist: seaborn>=0.12
|
|
33
|
+
Provides-Extra: examples
|
|
34
|
+
Requires-Dist: yfinance>=0.2; extra == "examples"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
38
|
+
Requires-Dist: build; extra == "dev"
|
|
39
|
+
Requires-Dist: twine; extra == "dev"
|
|
40
|
+
Dynamic: author
|
|
41
|
+
Dynamic: home-page
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
Dynamic: requires-python
|
|
44
|
+
|
|
45
|
+
# pycointbreak
|
|
46
|
+
|
|
47
|
+
> **Tests for breaks in fractional cointegration** — a Python
|
|
48
|
+
> implementation of the Hassler & Breitung (2006) residual-based LM
|
|
49
|
+
> test and the Rodrigues, Sibbertsen & Voges (2019) supremum tests
|
|
50
|
+
> for breaks in the cointegrating relationship.
|
|
51
|
+
|
|
52
|
+
[]()
|
|
53
|
+
[]()
|
|
54
|
+
[]()
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Author
|
|
59
|
+
|
|
60
|
+
**Dr. Merwan Roudane**
|
|
61
|
+
Email: [merwanroudane920@gmail.com](mailto:merwanroudane920@gmail.com)
|
|
62
|
+
GitHub: <https://github.com/merwanroudane/pycointbreak>
|
|
63
|
+
|
|
64
|
+
If you use this library in academic work, please cite both the
|
|
65
|
+
underlying papers and this package — see `pycointbreak.cite()` for
|
|
66
|
+
ready-made BibTeX entries.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## What does it do?
|
|
71
|
+
|
|
72
|
+
Given a scalar series `y` and a (possibly multivariate) series `x`,
|
|
73
|
+
each integrated of order `d`, `pycointbreak` lets you test whether
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
y_t = β' x_t + z_t, z_t ~ I(d − b), b ≥ 0,
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
is
|
|
80
|
+
|
|
81
|
+
1. **Cointegrated at all** (`b > 0`) — using the Hassler & Breitung
|
|
82
|
+
(2006) LM-type test.
|
|
83
|
+
2. **Cointegrated only on part of the sample** (segmented fractional
|
|
84
|
+
cointegration) — using the Rodrigues, Sibbertsen & Voges (2019)
|
|
85
|
+
sup-tests over split, forward incremental, backward incremental,
|
|
86
|
+
and rolling sub-samples.
|
|
87
|
+
|
|
88
|
+
When the answer is "yes, breaks", the library can also estimate the
|
|
89
|
+
**break date** via the consistent estimator of RSV (2019, eq. 19).
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Installation
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
git clone https://github.com/merwanroudane/pycointbreak.git
|
|
97
|
+
cd pycointbreak
|
|
98
|
+
pip install -e .
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Dependencies: `numpy`, `pandas`, `scipy`, `statsmodels`,
|
|
102
|
+
`matplotlib`, `seaborn`. Optional: `yfinance` (for the real-data
|
|
103
|
+
example).
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Quick start
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
import pycointbreak as pcb
|
|
111
|
+
|
|
112
|
+
# 1. Generate (or load) two I(d) series.
|
|
113
|
+
y, x = pcb.simulate_segmented_cointegration(
|
|
114
|
+
T=500, d=1.0, b=0.4, break_frac=0.5,
|
|
115
|
+
regime="coint_then_spurious", seed=2024,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# 2. Full-sample Hassler-Breitung test.
|
|
119
|
+
hb = pcb.hassler_breitung_test(y, x, d=1.0, p=0)
|
|
120
|
+
print(hb.summary())
|
|
121
|
+
|
|
122
|
+
# 3. RSV (2019) sup-test battery.
|
|
123
|
+
battery = pcb.rsv_battery(y, x, d=1.0, lam0=0.5, n_grid=120)
|
|
124
|
+
for k, r in battery.items():
|
|
125
|
+
print(r.summary())
|
|
126
|
+
|
|
127
|
+
# 4. Estimate the break date.
|
|
128
|
+
bp = pcb.estimate_break_point(y, x, d=1.0, direction="auto")
|
|
129
|
+
print(bp.summary())
|
|
130
|
+
|
|
131
|
+
# 5. Paper-style summary table.
|
|
132
|
+
df = pcb.battery_to_dataframe(battery, hb=hb, label="my pair")
|
|
133
|
+
print(pcb.render_table(df, fmt="text",
|
|
134
|
+
title="Tests for segmented cointegration"))
|
|
135
|
+
|
|
136
|
+
# 6. Plots.
|
|
137
|
+
import matplotlib.pyplot as plt
|
|
138
|
+
pcb.plot_series_with_breaks({"y": y, "x": x}, {"break": bp.obs})
|
|
139
|
+
pcb.plot_rsv_battery(battery)
|
|
140
|
+
pcb.plot_breakpoint_objective(bp)
|
|
141
|
+
plt.show()
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Mathematical compatibility with the papers
|
|
147
|
+
|
|
148
|
+
The implementation follows the papers' notation closely. The key
|
|
149
|
+
ingredients are exposed as building blocks:
|
|
150
|
+
|
|
151
|
+
| Symbol | Function |
|
|
152
|
+
| ------------------------------------------------------------------ | ----------------------------------------- |
|
|
153
|
+
| `Δ⁺ᵈ x_t` — truncated (Type II) fractional difference | `pcb.fdiff(x, d)` |
|
|
154
|
+
| `π_j(d)` — coefficients of `(1−L)^d` | `pcb.frac_coefs(d, n)` |
|
|
155
|
+
| `x*_{t−1} = Σ_{j=1}^{t−1} x_{t−j}/j` — harmonic-weighted sum (HB eq. 3) | `pcb.harmonic_running_sum(x)` |
|
|
156
|
+
| `(1−L)^{−d} x_t` — fractional integration | `pcb.frac_integrate(x, d)` |
|
|
157
|
+
| Geweke & Porter-Hudak `d̂` | `pcb.gph(x)` |
|
|
158
|
+
| HB test (eqs. 12–14, iid case) | `pcb.hassler_breitung_test(..., p=0)` |
|
|
159
|
+
| HB test with AR(p) augmentation (eqs. 17–18) | `pcb.hassler_breitung_test(..., p=p)` |
|
|
160
|
+
| RSV sub-sample t-stat (eq. 5) | internal `_hb_subsample_tstat` |
|
|
161
|
+
| Sup-statistics 𝒯_S, 𝒯_S*, 𝒯_If, 𝒯_Ib, 𝒯_R (eqs. 10–15) | `pcb.rsv_sup_test(..., kind=...)` |
|
|
162
|
+
| RSV break-point estimator (eq. 19) | `pcb.estimate_break_point(...)` |
|
|
163
|
+
| Critical values from Table 1 of RSV2019 | `pcb.rsv_critical_value(test, T, alpha)` |
|
|
164
|
+
|
|
165
|
+
### On the eq.-(5) `√n` factor
|
|
166
|
+
|
|
167
|
+
Equation (5) of RSV (2019) literally reads
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
t(ê(λ₁,λ₂)) = √(⌊λ₂T⌋ − ⌊λ₁T⌋) · Σ ê_t · ê*_{t−1}
|
|
171
|
+
/ [√Σ ê*²_{t−1} · √((T−1)⁻¹ · Σ ê²_t)]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
With the full-sample variance `1/(T−1)` and the extra `√n` factor,
|
|
175
|
+
under H0 this statistic is `O_p(√T)` rather than asymptotically
|
|
176
|
+
N(0,1), which would contradict Theorem 2 (`sup χ²₁` limit) and the
|
|
177
|
+
critical values of Table 1 (5%-cv ≈ 4–6 for `T = 500`). We
|
|
178
|
+
therefore interpret the formula as the standard HB statistic
|
|
179
|
+
(eq. 14 of HB2006) applied to the **sub-sample**, with sub-sample
|
|
180
|
+
variance `1/(n−1)` — the same convention used by Davidson &
|
|
181
|
+
Monticini (2010). This yields `t → N(0, 1)` per Proposition 3 of
|
|
182
|
+
HB2006 and `T_K → sup χ²₁` consistent with Theorem 2 of RSV2019.
|
|
183
|
+
|
|
184
|
+
### On the critical values
|
|
185
|
+
|
|
186
|
+
The tabulated critical values in `pcb.RSV_TABLE1` reproduce Table 1
|
|
187
|
+
of RSV (2019) for `T = 250` and `T = 500` with `λ₀ = 0.5`. They are
|
|
188
|
+
the result of a Monte Carlo simulation in the paper averaged over
|
|
189
|
+
several values of `d`. For configurations far from
|
|
190
|
+
`(T ∈ {250, 500}, λ₀ = 0.5, d ≈ 1)`, use the bootstrap helper
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
cv = pcb.bootstrap_rsv_cv(
|
|
194
|
+
T=1000, d=1.0, kind="T_If", n_sim=2000,
|
|
195
|
+
)
|
|
196
|
+
print(cv) # {0.10: ..., 0.05: ..., 0.01: ...}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
which returns critical values calibrated to your exact sample size,
|
|
200
|
+
grid, and `d`.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Modules
|
|
205
|
+
|
|
206
|
+
| Module | Contents |
|
|
207
|
+
| ------------------------------- | ------------------------------------------------------ |
|
|
208
|
+
| `pycointbreak.fracdiff` | Truncated (Type II) fractional difference operators |
|
|
209
|
+
| `pycointbreak.gph` | GPH log-periodogram estimator of `d` |
|
|
210
|
+
| `pycointbreak.hassler_breitung` | HB (2006) LM-type test |
|
|
211
|
+
| `pycointbreak.rsv_tests` | RSV (2019) sup-tests for breaks |
|
|
212
|
+
| `pycointbreak.breakpoint` | Break-fraction estimator (RSV eq. 19) |
|
|
213
|
+
| `pycointbreak.critical_values` | Tabulated and bootstrap critical values |
|
|
214
|
+
| `pycointbreak.simulate` | DGPs from HB2006 §5 and RSV2019 §4 (Experiments 1–3) |
|
|
215
|
+
| `pycointbreak.reporting` | Paper-style tables (text/markdown/LaTeX/HTML) |
|
|
216
|
+
| `pycointbreak.plots` | Publication-quality figures (matplotlib + seaborn) |
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## API at a glance
|
|
221
|
+
|
|
222
|
+
### Tests
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
hassler_breitung_test(y1, y2, d=1.0, p=0, include_const=True)
|
|
226
|
+
→ HBResult(t_stat, p_value, reject, ...)
|
|
227
|
+
|
|
228
|
+
rsv_sup_test(y1, y2, d=1.0, kind="T_If", lam0=0.5, n_grid=200, ...)
|
|
229
|
+
→ RSVResult(statistic, argmax, t2_path, ...)
|
|
230
|
+
|
|
231
|
+
rsv_battery(y1, y2, d=1.0, tests=("T_S_star", "T_If", "T_Ib", "T_R"), ...)
|
|
232
|
+
→ dict[str, RSVResult]
|
|
233
|
+
|
|
234
|
+
estimate_break_point(y1, y2, d=1.0, delta=0.05, direction="auto", ...)
|
|
235
|
+
→ BreakPointResult(tau_hat, obs, date, objective, ...)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Reporting
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
df = battery_to_dataframe(battery, hb=hb, label="series")
|
|
242
|
+
print(render_table(df, fmt="text")) # ASCII
|
|
243
|
+
print(render_table(df, fmt="markdown")) # for GitHub / Jupyter
|
|
244
|
+
print(render_table(df, fmt="latex")) # for papers
|
|
245
|
+
print(render_table(df, fmt="html")) # for web
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Plots
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
plot_series_with_breaks(series_dict, breaks_dict)
|
|
252
|
+
plot_rsv_profile(rsv_result) # one sup-test
|
|
253
|
+
plot_rsv_battery(battery_dict) # 2×2 panel of all four
|
|
254
|
+
plot_breakpoint_objective(bp_result)
|
|
255
|
+
plot_residual_diagnostics(residuals)
|
|
256
|
+
plot_split_heatmap(y, x, d=1.0, ...) # 2-D (λ₁, λ₂) heatmap
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Simulation
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
simulate_rsv_dgp(T=500, d=1.0, b=0.0)
|
|
263
|
+
# Experiment 1 — constant cointegration over the whole sample
|
|
264
|
+
|
|
265
|
+
simulate_segmented_cointegration(
|
|
266
|
+
T=500, d=1.0, b=0.4, break_frac=0.5,
|
|
267
|
+
regime="coint_then_spurious",
|
|
268
|
+
)
|
|
269
|
+
# Experiments 2 & 3 — one break in the cointegrating relationship
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Examples
|
|
275
|
+
|
|
276
|
+
The `examples/` folder contains three runnable scripts:
|
|
277
|
+
|
|
278
|
+
* `example_basic.py` — minimal end-to-end demo on simulated data.
|
|
279
|
+
* `example_real_data.py` — applies the full pipeline to real stock
|
|
280
|
+
prices (Coca-Cola / Pepsi by default via `yfinance`; falls back to
|
|
281
|
+
simulated data when the network is unavailable).
|
|
282
|
+
* `example_simulation_study.py` — small Monte Carlo reproducing a
|
|
283
|
+
slice of Table 2 of RSV (2019).
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## References
|
|
288
|
+
|
|
289
|
+
* Hassler, U. and Breitung, J. (2006). *A Residual-Based LM Type
|
|
290
|
+
Test Against Fractional Cointegration*. **Econometric Theory**,
|
|
291
|
+
22(6), 1091–1111.
|
|
292
|
+
* Rodrigues, P. M. M., Sibbertsen, P. and Voges, M. (2019).
|
|
293
|
+
*Testing for breaks in the cointegrating relationship: On the
|
|
294
|
+
stability of government bond markets' equilibrium*. Discussion
|
|
295
|
+
Paper 656.
|
|
296
|
+
* Davidson, J. and Monticini, A. (2010). *Tests for cointegration
|
|
297
|
+
with structural breaks based on subsamples*.
|
|
298
|
+
**Computational Statistics & Data Analysis**, 54(11), 2498–2511.
|
|
299
|
+
* Robinson, P. M. (1991). *Testing for strong serial correlation and
|
|
300
|
+
dynamic conditional heteroskedasticity in multiple regression*.
|
|
301
|
+
**Journal of Econometrics**, 47, 67–84.
|
|
302
|
+
* Breitung, J. and Hassler, U. (2002). *Inference on the cointegration
|
|
303
|
+
rank in fractionally integrated processes*. **Journal of
|
|
304
|
+
Econometrics**, 110, 167–185.
|
|
305
|
+
* Demetrescu, M., Kuzin, V. and Hassler, U. (2008). *Long memory
|
|
306
|
+
testing in the time domain*. **Econometric Theory**, 24(1),
|
|
307
|
+
176–215.
|
|
308
|
+
* Geweke, J. and Porter-Hudak, S. (1983). *The Estimation and
|
|
309
|
+
Application of Long Memory Time Series Models*. **Journal of Time
|
|
310
|
+
Series Analysis**, 4, 221–238.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## License
|
|
315
|
+
|
|
316
|
+
MIT
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# pycointbreak
|
|
2
|
+
|
|
3
|
+
> **Tests for breaks in fractional cointegration** — a Python
|
|
4
|
+
> implementation of the Hassler & Breitung (2006) residual-based LM
|
|
5
|
+
> test and the Rodrigues, Sibbertsen & Voges (2019) supremum tests
|
|
6
|
+
> for breaks in the cointegrating relationship.
|
|
7
|
+
|
|
8
|
+
[]()
|
|
9
|
+
[]()
|
|
10
|
+
[]()
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Author
|
|
15
|
+
|
|
16
|
+
**Dr. Merwan Roudane**
|
|
17
|
+
Email: [merwanroudane920@gmail.com](mailto:merwanroudane920@gmail.com)
|
|
18
|
+
GitHub: <https://github.com/merwanroudane/pycointbreak>
|
|
19
|
+
|
|
20
|
+
If you use this library in academic work, please cite both the
|
|
21
|
+
underlying papers and this package — see `pycointbreak.cite()` for
|
|
22
|
+
ready-made BibTeX entries.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## What does it do?
|
|
27
|
+
|
|
28
|
+
Given a scalar series `y` and a (possibly multivariate) series `x`,
|
|
29
|
+
each integrated of order `d`, `pycointbreak` lets you test whether
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
y_t = β' x_t + z_t, z_t ~ I(d − b), b ≥ 0,
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
is
|
|
36
|
+
|
|
37
|
+
1. **Cointegrated at all** (`b > 0`) — using the Hassler & Breitung
|
|
38
|
+
(2006) LM-type test.
|
|
39
|
+
2. **Cointegrated only on part of the sample** (segmented fractional
|
|
40
|
+
cointegration) — using the Rodrigues, Sibbertsen & Voges (2019)
|
|
41
|
+
sup-tests over split, forward incremental, backward incremental,
|
|
42
|
+
and rolling sub-samples.
|
|
43
|
+
|
|
44
|
+
When the answer is "yes, breaks", the library can also estimate the
|
|
45
|
+
**break date** via the consistent estimator of RSV (2019, eq. 19).
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git clone https://github.com/merwanroudane/pycointbreak.git
|
|
53
|
+
cd pycointbreak
|
|
54
|
+
pip install -e .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Dependencies: `numpy`, `pandas`, `scipy`, `statsmodels`,
|
|
58
|
+
`matplotlib`, `seaborn`. Optional: `yfinance` (for the real-data
|
|
59
|
+
example).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import pycointbreak as pcb
|
|
67
|
+
|
|
68
|
+
# 1. Generate (or load) two I(d) series.
|
|
69
|
+
y, x = pcb.simulate_segmented_cointegration(
|
|
70
|
+
T=500, d=1.0, b=0.4, break_frac=0.5,
|
|
71
|
+
regime="coint_then_spurious", seed=2024,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# 2. Full-sample Hassler-Breitung test.
|
|
75
|
+
hb = pcb.hassler_breitung_test(y, x, d=1.0, p=0)
|
|
76
|
+
print(hb.summary())
|
|
77
|
+
|
|
78
|
+
# 3. RSV (2019) sup-test battery.
|
|
79
|
+
battery = pcb.rsv_battery(y, x, d=1.0, lam0=0.5, n_grid=120)
|
|
80
|
+
for k, r in battery.items():
|
|
81
|
+
print(r.summary())
|
|
82
|
+
|
|
83
|
+
# 4. Estimate the break date.
|
|
84
|
+
bp = pcb.estimate_break_point(y, x, d=1.0, direction="auto")
|
|
85
|
+
print(bp.summary())
|
|
86
|
+
|
|
87
|
+
# 5. Paper-style summary table.
|
|
88
|
+
df = pcb.battery_to_dataframe(battery, hb=hb, label="my pair")
|
|
89
|
+
print(pcb.render_table(df, fmt="text",
|
|
90
|
+
title="Tests for segmented cointegration"))
|
|
91
|
+
|
|
92
|
+
# 6. Plots.
|
|
93
|
+
import matplotlib.pyplot as plt
|
|
94
|
+
pcb.plot_series_with_breaks({"y": y, "x": x}, {"break": bp.obs})
|
|
95
|
+
pcb.plot_rsv_battery(battery)
|
|
96
|
+
pcb.plot_breakpoint_objective(bp)
|
|
97
|
+
plt.show()
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Mathematical compatibility with the papers
|
|
103
|
+
|
|
104
|
+
The implementation follows the papers' notation closely. The key
|
|
105
|
+
ingredients are exposed as building blocks:
|
|
106
|
+
|
|
107
|
+
| Symbol | Function |
|
|
108
|
+
| ------------------------------------------------------------------ | ----------------------------------------- |
|
|
109
|
+
| `Δ⁺ᵈ x_t` — truncated (Type II) fractional difference | `pcb.fdiff(x, d)` |
|
|
110
|
+
| `π_j(d)` — coefficients of `(1−L)^d` | `pcb.frac_coefs(d, n)` |
|
|
111
|
+
| `x*_{t−1} = Σ_{j=1}^{t−1} x_{t−j}/j` — harmonic-weighted sum (HB eq. 3) | `pcb.harmonic_running_sum(x)` |
|
|
112
|
+
| `(1−L)^{−d} x_t` — fractional integration | `pcb.frac_integrate(x, d)` |
|
|
113
|
+
| Geweke & Porter-Hudak `d̂` | `pcb.gph(x)` |
|
|
114
|
+
| HB test (eqs. 12–14, iid case) | `pcb.hassler_breitung_test(..., p=0)` |
|
|
115
|
+
| HB test with AR(p) augmentation (eqs. 17–18) | `pcb.hassler_breitung_test(..., p=p)` |
|
|
116
|
+
| RSV sub-sample t-stat (eq. 5) | internal `_hb_subsample_tstat` |
|
|
117
|
+
| Sup-statistics 𝒯_S, 𝒯_S*, 𝒯_If, 𝒯_Ib, 𝒯_R (eqs. 10–15) | `pcb.rsv_sup_test(..., kind=...)` |
|
|
118
|
+
| RSV break-point estimator (eq. 19) | `pcb.estimate_break_point(...)` |
|
|
119
|
+
| Critical values from Table 1 of RSV2019 | `pcb.rsv_critical_value(test, T, alpha)` |
|
|
120
|
+
|
|
121
|
+
### On the eq.-(5) `√n` factor
|
|
122
|
+
|
|
123
|
+
Equation (5) of RSV (2019) literally reads
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
t(ê(λ₁,λ₂)) = √(⌊λ₂T⌋ − ⌊λ₁T⌋) · Σ ê_t · ê*_{t−1}
|
|
127
|
+
/ [√Σ ê*²_{t−1} · √((T−1)⁻¹ · Σ ê²_t)]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
With the full-sample variance `1/(T−1)` and the extra `√n` factor,
|
|
131
|
+
under H0 this statistic is `O_p(√T)` rather than asymptotically
|
|
132
|
+
N(0,1), which would contradict Theorem 2 (`sup χ²₁` limit) and the
|
|
133
|
+
critical values of Table 1 (5%-cv ≈ 4–6 for `T = 500`). We
|
|
134
|
+
therefore interpret the formula as the standard HB statistic
|
|
135
|
+
(eq. 14 of HB2006) applied to the **sub-sample**, with sub-sample
|
|
136
|
+
variance `1/(n−1)` — the same convention used by Davidson &
|
|
137
|
+
Monticini (2010). This yields `t → N(0, 1)` per Proposition 3 of
|
|
138
|
+
HB2006 and `T_K → sup χ²₁` consistent with Theorem 2 of RSV2019.
|
|
139
|
+
|
|
140
|
+
### On the critical values
|
|
141
|
+
|
|
142
|
+
The tabulated critical values in `pcb.RSV_TABLE1` reproduce Table 1
|
|
143
|
+
of RSV (2019) for `T = 250` and `T = 500` with `λ₀ = 0.5`. They are
|
|
144
|
+
the result of a Monte Carlo simulation in the paper averaged over
|
|
145
|
+
several values of `d`. For configurations far from
|
|
146
|
+
`(T ∈ {250, 500}, λ₀ = 0.5, d ≈ 1)`, use the bootstrap helper
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
cv = pcb.bootstrap_rsv_cv(
|
|
150
|
+
T=1000, d=1.0, kind="T_If", n_sim=2000,
|
|
151
|
+
)
|
|
152
|
+
print(cv) # {0.10: ..., 0.05: ..., 0.01: ...}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
which returns critical values calibrated to your exact sample size,
|
|
156
|
+
grid, and `d`.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Modules
|
|
161
|
+
|
|
162
|
+
| Module | Contents |
|
|
163
|
+
| ------------------------------- | ------------------------------------------------------ |
|
|
164
|
+
| `pycointbreak.fracdiff` | Truncated (Type II) fractional difference operators |
|
|
165
|
+
| `pycointbreak.gph` | GPH log-periodogram estimator of `d` |
|
|
166
|
+
| `pycointbreak.hassler_breitung` | HB (2006) LM-type test |
|
|
167
|
+
| `pycointbreak.rsv_tests` | RSV (2019) sup-tests for breaks |
|
|
168
|
+
| `pycointbreak.breakpoint` | Break-fraction estimator (RSV eq. 19) |
|
|
169
|
+
| `pycointbreak.critical_values` | Tabulated and bootstrap critical values |
|
|
170
|
+
| `pycointbreak.simulate` | DGPs from HB2006 §5 and RSV2019 §4 (Experiments 1–3) |
|
|
171
|
+
| `pycointbreak.reporting` | Paper-style tables (text/markdown/LaTeX/HTML) |
|
|
172
|
+
| `pycointbreak.plots` | Publication-quality figures (matplotlib + seaborn) |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## API at a glance
|
|
177
|
+
|
|
178
|
+
### Tests
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
hassler_breitung_test(y1, y2, d=1.0, p=0, include_const=True)
|
|
182
|
+
→ HBResult(t_stat, p_value, reject, ...)
|
|
183
|
+
|
|
184
|
+
rsv_sup_test(y1, y2, d=1.0, kind="T_If", lam0=0.5, n_grid=200, ...)
|
|
185
|
+
→ RSVResult(statistic, argmax, t2_path, ...)
|
|
186
|
+
|
|
187
|
+
rsv_battery(y1, y2, d=1.0, tests=("T_S_star", "T_If", "T_Ib", "T_R"), ...)
|
|
188
|
+
→ dict[str, RSVResult]
|
|
189
|
+
|
|
190
|
+
estimate_break_point(y1, y2, d=1.0, delta=0.05, direction="auto", ...)
|
|
191
|
+
→ BreakPointResult(tau_hat, obs, date, objective, ...)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Reporting
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
df = battery_to_dataframe(battery, hb=hb, label="series")
|
|
198
|
+
print(render_table(df, fmt="text")) # ASCII
|
|
199
|
+
print(render_table(df, fmt="markdown")) # for GitHub / Jupyter
|
|
200
|
+
print(render_table(df, fmt="latex")) # for papers
|
|
201
|
+
print(render_table(df, fmt="html")) # for web
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Plots
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
plot_series_with_breaks(series_dict, breaks_dict)
|
|
208
|
+
plot_rsv_profile(rsv_result) # one sup-test
|
|
209
|
+
plot_rsv_battery(battery_dict) # 2×2 panel of all four
|
|
210
|
+
plot_breakpoint_objective(bp_result)
|
|
211
|
+
plot_residual_diagnostics(residuals)
|
|
212
|
+
plot_split_heatmap(y, x, d=1.0, ...) # 2-D (λ₁, λ₂) heatmap
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Simulation
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
simulate_rsv_dgp(T=500, d=1.0, b=0.0)
|
|
219
|
+
# Experiment 1 — constant cointegration over the whole sample
|
|
220
|
+
|
|
221
|
+
simulate_segmented_cointegration(
|
|
222
|
+
T=500, d=1.0, b=0.4, break_frac=0.5,
|
|
223
|
+
regime="coint_then_spurious",
|
|
224
|
+
)
|
|
225
|
+
# Experiments 2 & 3 — one break in the cointegrating relationship
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Examples
|
|
231
|
+
|
|
232
|
+
The `examples/` folder contains three runnable scripts:
|
|
233
|
+
|
|
234
|
+
* `example_basic.py` — minimal end-to-end demo on simulated data.
|
|
235
|
+
* `example_real_data.py` — applies the full pipeline to real stock
|
|
236
|
+
prices (Coca-Cola / Pepsi by default via `yfinance`; falls back to
|
|
237
|
+
simulated data when the network is unavailable).
|
|
238
|
+
* `example_simulation_study.py` — small Monte Carlo reproducing a
|
|
239
|
+
slice of Table 2 of RSV (2019).
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## References
|
|
244
|
+
|
|
245
|
+
* Hassler, U. and Breitung, J. (2006). *A Residual-Based LM Type
|
|
246
|
+
Test Against Fractional Cointegration*. **Econometric Theory**,
|
|
247
|
+
22(6), 1091–1111.
|
|
248
|
+
* Rodrigues, P. M. M., Sibbertsen, P. and Voges, M. (2019).
|
|
249
|
+
*Testing for breaks in the cointegrating relationship: On the
|
|
250
|
+
stability of government bond markets' equilibrium*. Discussion
|
|
251
|
+
Paper 656.
|
|
252
|
+
* Davidson, J. and Monticini, A. (2010). *Tests for cointegration
|
|
253
|
+
with structural breaks based on subsamples*.
|
|
254
|
+
**Computational Statistics & Data Analysis**, 54(11), 2498–2511.
|
|
255
|
+
* Robinson, P. M. (1991). *Testing for strong serial correlation and
|
|
256
|
+
dynamic conditional heteroskedasticity in multiple regression*.
|
|
257
|
+
**Journal of Econometrics**, 47, 67–84.
|
|
258
|
+
* Breitung, J. and Hassler, U. (2002). *Inference on the cointegration
|
|
259
|
+
rank in fractionally integrated processes*. **Journal of
|
|
260
|
+
Econometrics**, 110, 167–185.
|
|
261
|
+
* Demetrescu, M., Kuzin, V. and Hassler, U. (2008). *Long memory
|
|
262
|
+
testing in the time domain*. **Econometric Theory**, 24(1),
|
|
263
|
+
176–215.
|
|
264
|
+
* Geweke, J. and Porter-Hudak, S. (1983). *The Estimation and
|
|
265
|
+
Application of Long Memory Time Series Models*. **Journal of Time
|
|
266
|
+
Series Analysis**, 4, 221–238.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
MIT
|