A Python package for regime detection and splitting in time series data
Project description
RegimeSplit
A volatility/regime-aware cross-validation splitter for time-series ML backtests (embargo & purge included). Avoids splitting across regime boundaries.
Install
pip install -e .
Quick Example
import pandas as pd
from regimesplit import RegimeSplit
df = pd.read_csv("examples/series.csv", parse_dates=[0], index_col=0)
rs = RegimeSplit(n_splits=5, embargo=15, purge=5, vol_window=60, k_regimes=3, method="quantiles")
for i, (tr, te) in enumerate(rs.split(df)):
print(i, len(tr), len(te), df.index[tr[0]], df.index[te[0]])
CLI Usage
regimesplit folds --csv examples/series.csv --ret-col ret --n-splits 5 --embargo 15 --purge 5 --vol-window 60 --k 3 --method quantiles --out out/
Features
- ๐ฏ Regime-Aware Splitting: Detects volatility regimes and never splits within regime segments
- โฐ Temporal Constraints: Built-in embargo and purge to prevent look-ahead bias
- ๐ Multiple Detection Methods: Quantile-based and K-means clustering for regime identification
- ๐ Realized Volatility: Uses rolling standard deviation for regime detection
- ๐ Rich Visualization: Timeline plots showing regimes and train/test splits
- ๐ HTML Reports: Professional reports with interactive visualizations
- ๐ ๏ธ CLI Interface: Easy-to-use command-line tools for analysis
- ๐ฌ sklearn Compatible: Drop-in replacement for standard cross-validation
Quick Start
Cross-Validation for ML Backtesting
import pandas as pd
from regimesplit import RegimeSplit
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score
# Load financial data with datetime index
df = pd.read_csv('financial_data.csv', index_col=0, parse_dates=True)
# Columns: 'price', 'ret', 'feature1', 'feature2', etc.
# Initialize regime-aware splitter
splitter = RegimeSplit(
n_splits=5, # Number of CV folds
embargo=24, # 24-hour embargo period
purge=12, # 12-hour purge period
vol_window=60, # 60-period volatility window
k_regimes=3, # Detect 3 volatility regimes
method="quantiles" # Use quantile-based regime detection
)
# Prepare features and target
X = df[['feature1', 'feature2', 'feature3']]
y = df['ret'].shift(-1).dropna() # Next period return
X = X.iloc[:-1] # Align with y
# Cross-validate with regime-aware splits
model = RandomForestRegressor(n_estimators=100)
scores = cross_val_score(model, X, y, cv=splitter, scoring='neg_mean_squared_error')
print(f"CV Scores: {scores}")
print(f"Mean CV Score: {scores.mean():.4f} ยฑ {scores.std():.4f}")
Command Line Interface
# Generate cross-validation folds
regimesplit folds data/prices.csv --ret-col returns --n-splits 5 --embargo 24 --purge 12
# Create HTML report with timeline visualization
regimesplit report output/folds.json output/regimes.csv --output-dir reports/
# Generate synthetic test data
python examples/synth_make.py
Generate Synthetic Data
Create realistic financial time series with regime changes:
from examples.synth_make import make_series
# Generate 5000 observations with 3 volatility regimes
df = make_series(n=5000, seed=123)
# Returns DataFrame with columns: 'price', 'ret', 'feature1', 'feature2'
print(f"Generated {len(df)} observations")
print(f"Price range: {df['price'].min():.2f} - {df['price'].max():.2f}")
API Reference
Main Classes
RegimeSplit(n_splits=5, embargo=0, purge=0, vol_window=60, k_regimes=3, method="quantiles", min_regime_len=30)
sklearn-compatible cross-validator for regime-aware time series splitting.
Parameters:
n_splits: Number of cross-validation foldsembargo: Observations to skip after each test period (prevent leakage)purge: Observations to remove before each test periodvol_window: Rolling window for realized volatility calculationk_regimes: Number of volatility regimes to detectmethod: Regime detection method ("quantiles" or "kmeans")min_regime_len: Minimum length for regime segments (shorter ones merged)
Methods:
split(X, y=None, groups=None): Generate (train_idx, test_idx) tuplesget_n_splits(): Return number of splits
Detection Functions
realized_volatility(ret, window=60)
Calculate realized volatility using rolling standard deviation.
label_regimes_from_vol(vol, k=3, method="quantiles")
Label volatility regimes using quantiles or K-means clustering.
contiguous_segments(regime_id)
Extract contiguous regime segments with start/end timestamps.
enforce_min_len(segments, min_len, freq, index)
Merge segments shorter than minimum length with neighbors.
Visualization
plot_regimes(data, regimes, change_points=None)
Plot time series with regime coloring and change points.
plot_timeline(index, regime_id, folds, path_png)
Timeline visualization showing regimes and cross-validation splits.
Utilities
apply_embargo(test_start_idx, embargo)
Apply embargo by shifting test start forward.
apply_purge(train_idx, test_idx, purge)
Remove observations from train/test boundaries.
Examples
Example 1: Financial Backtesting Pipeline
import pandas as pd
import numpy as np
from regimesplit import RegimeSplit
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
# Load or generate financial data
from examples.synth_make import make_series
df = make_series(n=2000, seed=42)
# Prepare features and target
features = ['feature1', 'feature2']
X = df[features].dropna()
y = df['ret'].shift(-1).dropna() # Predict next return
X = X.iloc[:-1] # Align indices
# Initialize regime-aware cross-validator
cv = RegimeSplit(
n_splits=5,
embargo=12, # 12-period embargo
purge=6, # 6-period purge
vol_window=48, # 48-period volatility window
k_regimes=3,
method="quantiles"
)
# Train/test with proper temporal validation
model = RandomForestRegressor(n_estimators=50, random_state=42)
oos_predictions = []
oos_actuals = []
for train_idx, test_idx in cv.split(X):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
oos_predictions.extend(y_pred)
oos_actuals.extend(y_test)
# Evaluate out-of-sample performance
oos_mse = mean_squared_error(oos_actuals, oos_predictions)
print(f"Out-of-sample MSE: {oos_mse:.6f}")
Example 2: CLI Workflow
# Step 1: Generate synthetic data
python examples/synth_make.py
# Step 2: Create cross-validation folds
regimesplit folds examples/series.csv \
--ret-col ret \
--n-splits 5 \
--embargo 24 \
--purge 12 \
--vol-window 60 \
--k-regimes 3 \
--method quantiles \
--output-dir results/
# Step 3: Generate professional HTML report
regimesplit report \
results/folds.json \
results/regimes.csv \
--output-dir reports/
# Open report in browser
open reports/folds_report.html
Example 3: Custom Regime Analysis
from regimesplit.detection import realized_volatility, label_regimes_from_vol
from regimesplit.utils import contiguous_segments
from regimesplit.plotting import plot_timeline
# Load data
df = pd.read_csv('your_data.csv', index_col=0, parse_dates=True)
returns = df['ret']
# Step-by-step regime detection
vol = realized_volatility(returns, window=60)
regime_labels = label_regimes_from_vol(vol, k=3, method="quantiles")
segments = contiguous_segments(regime_labels)
print(f"Detected {len(segments)} regime segments:")
for start_ts, end_ts, regime_id in segments[:5]:
duration = end_ts - start_ts
print(f" Regime {regime_id}: {start_ts} to {end_ts} ({duration})")
Development
Setup Development Environment
make venv # Create virtual environment
make install # Install package in development mode
Run Tests
make test # Run test suite
Create Demo
make demo # Run synthetic data generation demo
Project Structure
regimesplit/
โโโ src/regimesplit/
โ โโโ __init__.py # Package initialization
โ โโโ splitter.py # Main RegimeSplit class
โ โโโ cli.py # Command-line interface
โ โโโ detection.py # Detection algorithms
โ โโโ plotting.py # Visualization functions
โ โโโ utils.py # Utility functions
โ โโโ report.py # HTML report generation
โโโ examples/
โ โโโ synth_make.py # Synthetic data generation
โโโ tests/
โ โโโ test_basic.py # Basic test suite
โโโ pyproject.toml # Project configuration
โโโ README.md # This file
โโโ LICENSE # MIT License
โโโ Makefile # Development commands
Requirements
- Python โฅ 3.8
- pandas
- numpy
- scikit-learn
- matplotlib
- tyro
- jinja2
- pytest
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Why RegimeSplit?
Traditional time series cross-validation problems:
- โ KFold: Ignores temporal order, causes severe data leakage
- โ TimeSeriesSplit: Creates arbitrary splits that may cut through regime changes
- โ Standard methods: No consideration of market regime shifts
RegimeSplit advantages:
- โ Regime-aware: Never splits within homogeneous volatility periods
- โ Realistic backtesting: Mimics real trading constraints with embargo/purge
- โ No look-ahead bias: Strict temporal ordering with customizable gaps
- โ sklearn compatible: Drop-in replacement for existing CV workflows
Perfect for:
- ๐ฆ Financial ML: Trading strategy backtesting and model validation
- ๐ Quantitative research: Regime-aware performance evaluation
- ๐ฌ Academic studies: Robust cross-validation for financial time series
- โก Production systems: Realistic out-of-sample testing
Roadmap
- Advanced detection: PELT, Binary Segmentation, Hidden Markov Models
- Multivariate regimes: Correlation-based and PCA regime detection
- Online detection: Streaming regime identification for live trading
- Statistical testing: Regime change significance and stability tests
- Enhanced visualization: Interactive plots and regime diagnostics
- Performance optimization: Cython implementation for large datasets
- Integration: Native support for popular trading frameworks
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file regimesplit-0.1.0.tar.gz.
File metadata
- Download URL: regimesplit-0.1.0.tar.gz
- Upload date:
- Size: 557.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
beaaa9f38ca8350f7a0fa1410baaf10d757714b8cdb7c63a107c79ee7c7a544f
|
|
| MD5 |
6e8581a4169bdf1892d1c39c35347bfb
|
|
| BLAKE2b-256 |
68b612d33287a415e8ee890f10755ef1be69943ed8a0506f97b9cc698fc4d357
|
File details
Details for the file regimesplit-0.1.0-py3-none-any.whl.
File metadata
- Download URL: regimesplit-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be4aaa19b8b466f5258277382a2265bc0382e6c4f43dbef5c56f2ce018152107
|
|
| MD5 |
ff6418a47f39b0f05808c48846294b10
|
|
| BLAKE2b-256 |
3de25bfee0c707a28dab4602b043bcf16cc724ef32a45b379a8b062ccc8c66eb
|