Skip to main content

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 folds
  • embargo: Observations to skip after each test period (prevent leakage)
  • purge: Observations to remove before each test period
  • vol_window: Rolling window for realized volatility calculation
  • k_regimes: Number of volatility regimes to detect
  • method: 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) tuples
  • get_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

regimesplit-0.1.0.tar.gz (557.2 kB view details)

Uploaded Source

Built Distribution

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

regimesplit-0.1.0-py3-none-any.whl (28.2 kB view details)

Uploaded Python 3

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

Hashes for regimesplit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 beaaa9f38ca8350f7a0fa1410baaf10d757714b8cdb7c63a107c79ee7c7a544f
MD5 6e8581a4169bdf1892d1c39c35347bfb
BLAKE2b-256 68b612d33287a415e8ee890f10755ef1be69943ed8a0506f97b9cc698fc4d357

See more details on using hashes here.

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

Hashes for regimesplit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 be4aaa19b8b466f5258277382a2265bc0382e6c4f43dbef5c56f2ce018152107
MD5 ff6418a47f39b0f05808c48846294b10
BLAKE2b-256 3de25bfee0c707a28dab4602b043bcf16cc724ef32a45b379a8b062ccc8c66eb

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