Statistical Arbitrage · Johansen Cointegration · Bayesian Portfolio Optimization · Walk-Forward Validation
This project implements a complete, research-grade basket trading strategy that improves upon the classical Johansen cointegration baseline using Bayesian Optimization (BO) to find superior portfolio weights that maximize a risk-adjusted objective.
Data Collection (yfinance)
↓
Cointegration Analysis (Johansen Test)
↓
Spread Construction & Verification (ADF + Half-life)
↓
Baseline Trading Strategy (Z-Score Mean Reversion)
↓
Bayesian Optimization (scikit-optimize GP)
↓
Rolling Walk-Forward Backtest (Out-of-Sample)
↓
Evaluation & Reporting
BasketBO/
├── data/ # Downloaded price CSVs (gitignored)
├── results/ # Output plots and metrics (gitignored)
├── notebooks/ # Jupyter notebooks for exploration
├── src/
│ ├── __init__.py
│ ├── data_loader.py # yfinance downloader + config loader
│ ├── cointegration.py # Johansen test & baseline weights
│ ├── spread.py # ADF test, half-life estimation
│ ├── strategy.py # Z-score, signal generation
│ ├── backtester.py # Vectorized PnL backtester
│ ├── metrics.py # Sharpe, CAGR, max drawdown, win rate
│ ├── optimizer.py # Bayesian Optimization (BO) engine
│ ├── rolling_window.py # Walk-forward optimization framework
│ └── visualization.py # Equity curves, spread, weight plots
├── configs/
│ └── config.yaml # All parameters in one place
├── main.py # Pipeline entry point (all 7 phases)
├── requirements.txt
└── README.md
pip install -r requirements.txtEdit configs/config.yaml to set your universe, strategy parameters, and BO settings:
data:
tickers: [AAPL, MSFT, NVDA, ORCL, CSCO]
start_date: '2015-01-01'
strategy:
rolling_window: 30
entry_z: -2.0
exit_z: 0.5
optimizer:
init_points: 15
n_iter: 35python main.pyResults are saved to results/:
| File | Description |
|---|---|
in_sample_equity.png |
Equity curves (in-sample, Johansen vs BO) |
out_of_sample_equity.png |
Walk-forward equity curves |
johansen_spread.png |
Spread with entry/exit signals |
bo_spread.png |
BO-optimized spread with signals |
johansen_weights.png |
Weight evolution per window (Johansen) |
bo_weights.png |
Weight evolution per window (BO) |
evaluation_metrics.csv |
Metrics comparison table |
Downloads adjusted close prices for 5 technology stocks using yfinance.
- Runs the Johansen cointegration test to find the first cointegrating vector.
- Constructs the basket spread
S_t = wᵀ · Pₜ. - Validates mean reversion via Augmented Dickey-Fuller (ADF) test.
- Estimates half-life of mean reversion using an Ornstein-Uhlenbeck model.
- Computes a rolling z-score of the spread.
- Enters long when
z < −2, short whenz > +2, exits when|z| < 0.5. - Tracks Sharpe, CAGR, max drawdown, win rate, profit factor.
- Uses Gaussian Process-based BO (
scikit-optimize) to search for weightsw ∈ [−1, 1]⁵. - Objective: maximize
Sharpe − 0.5 × |MaxDrawdown|subject to a stationarity penalty (ADF p-value). - Normalizes weights to
Σ|wᵢ| = 1.
- Train window: 2 years. Test window: 3 months (quarterly).
- Each window independently runs Johansen and BO, then trades out-of-sample.
- Provides an honest, leak-free evaluation over the full history.
- Compares Johansen vs BO across all rolling windows.
- Saves plots and a metrics CSV.
| Metric | Description |
|---|---|
| Sharpe Ratio | Annualized risk-adjusted return |
| CAGR | Compound Annual Growth Rate |
| Max Drawdown | Largest peak-to-trough decline |
| Win Rate | % of profitable trading days |
| Profit Factor | Gross profits / gross losses |
| Half-life | Mean reversion speed (days) |
- Deep Kernel Learning BO (replace GP with DKL for non-stationary objectives)
- Regime Detection (separate BO for bull/bear markets via HMM)
- Multi-Objective BO (Pareto frontier: Sharpe vs Drawdown via Optuna/BoTorch)
- Dynamic Threshold Optimization (add
entry_zandexit_zto BO search space) - Transaction Cost Modeling (commission + bid-ask + slippage)
| Tool | Purpose |
|---|---|
yfinance |
Market data |
statsmodels |
Johansen test, ADF test |
scikit-optimize |
Gaussian Process Bayesian Optimization |
pandas / numpy |
Data manipulation |
matplotlib |
Visualization |
sklearn |
OU half-life regression |
- Johansen, S. (1991). Estimation and Hypothesis Testing of Cointegration Vectors. Econometrica.
- Avellaneda, M. & Lee, J.H. (2010). Statistical Arbitrage in the US Equities Market. Quantitative Finance.
- Mockus, J. (1975). On Bayesian Methods for Seeking the Extremum. IFIP Technical Conference.