Software companion
Change-Point Detector Comparison on SPX Realized Volatility
Overview
Four detectors with very different parameterizations are compared, so the package is arranged so that nothing about the comparison lives inside a detector. Each implements the same detectors/base.py interface and returns alarm dates; the realized-volatility input, the true-event set, the matching window, the latency definition and the false-alarm accounting are shared code applied identically to all four.
The second constraint is downstream. Three other studies consume this one’s alarms, so the export is a contract rather than a by-product: changepoints.csv is a point-event table keyed by ISO date with false-alarm flags preserved, and primary_regime_breaks.csv names the one recommended run for consumers that want a single canonical set. Consumers join on the date or widen the window themselves; they never parse detector-specific output.
Implementation
- Core libraries
- hmmlearn, ruptures, NumPy, pandas, SciPy, yfinance, pandas-datareader, Jinja2
- Install
- An installable package,
rsk-04-changepoint, exposing a console script of the same name - Configuration
- Two YAML files —
configs/default.yamlfor live sources,configs/fixtures_smoke.yamlfor an offline run - Protocol settings
- Sample from 2010-01-01, seed 1729, a 10-day matching window, a one-per-year false-alarm target, a 0.25 held-out fraction, and a 64-sample block bootstrap at block length 10
- Ablation grids
- Seven CUSUM thresholds crossed with three drift values, and eight PELT penalties
- Tests
- 9 modules, including one per detector and a pipeline smoke test
Components
detectors/base.py- The shared detector interface every method is measured through
detectors/hmm.py- Two-state Gaussian hidden Markov regime labelling
detectors/cusum.py- CUSUM with the bootstrap-calibrated threshold and drift grid
detectors/pelt.py- Exact penalized segmentation via ruptures
detectors/baseline.py- The fixed z-score threshold any method has to beat to be worth its complexity
realized_vol.py- Realized volatility from 5-minute bars, with a daily squared-return fallback that is flagged rather than silent
data_sources.py- Price, VIX and NBER acquisition plus the vintage record
evaluation.py- Event matching, detection latency, and false alarms per year
ablation.py- The detector sensitivity grid
schemas.py- Column contracts checked before any artifact is written
report.py, plotting.py- The methods note, the one-page summary, and the figures
Stages
download-dataAcquire prices, intraday bars, VIX and NBER recession dates, recording the vintage.build-rvBuild the canonical log realized-volatility series and flag every fallback day.run-detectorsRun all four detectors over the same input.evaluateMatch alarms to events, compute latency and false-alarm rates, and write the comparison table.run-ablationSweep the CUSUM and PELT grids to separate method from tuning.build-reportRender the methods note, the forwardable summary, and the downstream exports.
Reproducibility and validation
- Coverage of free 5-minute index data is uneven across the sample, so the pipeline records every day on which it fell back to daily squared returns instead of quietly substituting one estimator for another.
- Recession dating is revision-sensitive; the report will not build without
data_vintage.json, and the vintage is surfaced in the appendix. - The headline template reports latency and false alarms per year together, and does not assert that PELT detects earlier unless the computed metrics support it.
run_manifest.jsonrecords the effective configuration, dependency versions, and checksums for every artifact the report cites.- A fixtures configuration runs the whole pipeline offline, so the code path can be tested without network access or vendor availability.
Availability
Not publicly released. Every input comes from a free source, so the constraint here is maintenance rather than licensing: the exports are consumed by three other studies, and publishing the package would freeze a join contract that is still in internal use.