A small, transparent example of a volatility-aware allocation strategy for SPY. The strategy combines:
- a VIX term-structure regime filter;
- trailing realized-volatility targeting;
- a leverage cap;
- explicit turnover costs.
This repository is intentionally self-contained. It presents the strategy, reproducible backtest outputs, and a few diagnostic charts without including the broader internal validation framework used to review strategies.
For trading day t:
- Compute the previous day's VIX term-structure ratio:
VIX3M[t-1] / VIX[t-1]. - Compute lagged trailing SPY realized volatility.
- Hold SPY only when the lagged ratio is above
1.0. - Size the position to a 10% annualized volatility target, capped at 2x.
- Apply the row-
texposure to the row-tclose-to-close SPY return.
Both features are shifted by one trading observation. Therefore, exposure on
row t uses information through t-1; no additional execution shift is
applied in this reference implementation.
term_structure_lag[t] = (VIX3M / VIX)[t-1]
realized_vol_lag[t] = std(SPY returns, 20 days)[t-1] * sqrt(252)
exposure[t] = 1(term_structure_lag[t] > 1.0)
* min(0.10 / realized_vol_lag[t], 2.0)
strategy_return[t] = exposure[t] * SPY_return[t]
- turnover[t] * 1 bp
Python 3.12 is recommended.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
python -m pip install -r requirements.txt
python run_analysis.py
python -m unittest discover -s tests -vrun_analysis.py downloads adjusted daily closes from Yahoo Finance. Its
default end date is exclusive and fixed so the committed snapshot can be
recreated against the same requested date range. Vendor history may still be
revised over time; the committed CSV and charts are the repository snapshot.
metrics.json records the SHA-256 of the derived CSV used for that snapshot.
The committed results use observations returned for the request from 2013-01-01 through the exclusive end date 2026-07-21. The exact aligned sample is shown below. Metrics are descriptive, not a claim of future performance.
Sample: 2013-02-01 to 2026-07-17, 3,384 aligned trading observations.
| Metric | Strategy | SPY buy-and-hold |
|---|---|---|
| CAGR | 8.13% | 14.62% |
| Annualized volatility | 10.60% | 16.86% |
| Sharpe (0% risk-free rate) | 0.791 | 0.894 |
| Maximum drawdown | -17.17% | -33.72% |
| Total return | 185.81% | 525.23% |
| Relative/trading diagnostic | Value |
|---|---|
| Annualized active return | -6.69% |
| Information ratio | -0.554 |
| Correlation with SPY | 0.701 |
| Beta to SPY | 0.441 |
| Average SPY exposure | 85.1% |
| Annualized turnover | 17.15x |
| Regime on | 92.6% of observations |
strategy.py Strategy, data preparation, and metric functions
run_analysis.py Reproducible CLI that writes metrics, CSV, and charts
results/metrics.json Machine-readable snapshot metrics and configuration
results/backtest.csv Aligned derived return/exposure series
results/*.png Publication-ready diagnostic charts
tests/test_strategy.py Synthetic-data timing and accounting tests
- Yahoo Finance is a convenient public data source, not a point-in-time market data archive. Historical observations may be revised.
- VIX indices are used as regime indicators; the strategy does not trade VIX products.
- The backtest does not model bid/ask spreads, market impact, taxes, financing, borrow constraints, or intraday execution.
- The cost model is a simple linear cost per unit of turnover.
- The parameters were chosen for demonstration and are not presented as universally optimal.
- This is a historical example, not investment advice.
Market data is fetched from Yahoo Finance and remains subject to the data provider's terms. No separate license is granted for third-party data.


