A dual-head Transformer that forecasts 21-day realised volatility and classifies the current volatility regime (Low / Normal / High Stress), then injects that forecast into a Monte Carlo simulation engine to generate realistic price paths.
The core innovation is replacing the fixed σ in standard Geometric Brownian Motion with a time-varying σ(t) produced by the model:
# Standard GBM — σ is a single historical constant
S(t+1) = S(t) × exp( (μ − σ²/2)dt + σ√dt × Z )
# Volatility Waves — σ(t) comes from the Transformer forecast
S(t+1) = S(t) × exp( (μ − σ(t)²/2)dt + σ(t)√dt × Z )
Pre-trained checkpoints are included for: SPY · AAPL · NVDA · MSFT · AMZN · GOOGL · GLD · QQQ · TLT · XOM · BTC-USD
streamlit run app.pyPick a ticker, simulation horizon (5–63 days), number of paths (1k / 5k / 10k), and simulation mode. The app generates a percentile fan chart (5th / 25th / 50th / 75th / 95th) plus VaR, CVaR, and a regime probability table in real time.
Input: (batch, 63 days, 9 features)
└─ Linear projection + LayerNorm → d = 64
└─ Learnable positional embedding
└─ 3× TransformerEncoderLayer (4 heads, GELU, dropout = 0.1)
└─ Last token (current market state)
├─ Vol head: LayerNorm → Linear(64,32) → GELU → Linear(32,1) → scalar vol forecast
└─ Regime head: LayerNorm → Linear(64,32) → GELU → Linear(32,3) → softmax regime probs
~250 k parameters. Trains in ≈5 min on CPU, ≈90 s on MPS/CUDA.
Loss = 0.7 × MSE(vol) + 0.3 × CrossEntropy(regime)
| Mode | σ source |
|---|---|
| AI vol | Time-varying σ(t) interpolated from current realised vol to Transformer forecast |
| Regime switch | Per-path regime drawn from the model's softmax probabilities, each regime has its own σ and drift |
| GBM baseline | Constant σ from trailing 21-day realised vol (standard GBM) |
| Feature | Description |
|---|---|
log_ret |
Daily log return |
rvol_5 |
5-day realised vol (annualised) |
rvol_10 |
10-day realised vol (annualised) |
rvol_21 |
21-day realised vol — also the regression target |
rvol_63 |
63-day realised vol (annualised) |
park_vol |
Parkinson high-low estimator |
vvol |
Vol-of-vol: rolling std of rvol_21 — regime transition signal |
vix |
VIX level (implied vol) |
vix_ratio |
rvol_21 / VIX — > 1 means realised exceeded implied |
volatility-waves/
├── app.py # Streamlit dashboard
├── requirements.txt
│
├── src/
│ ├── data/
│ │ └── fetch.py # Download OHLCV via yfinance → data/raw/
│ ├── features/
│ │ └── build.py # Leakage-safe feature engineering → data/processed/
│ ├── models/
│ │ ├── dataset.py # VolDataset — sliding window over features
│ │ ├── vol_transformer.py # Dual-head Transformer definition
│ │ └── inference.py # load_bundle, infer_latest, infer_rolling
│ ├── simulation/
│ │ └── paths.py # gbm_paths, ai_vol_paths, regime_paths, risk_metrics
│ ├── benchmarking.py # GARCH(1,1), EWMA, Kupiec LR test
│ └── train.py # Walk-forward CV, final model, ablation study
│
├── scripts/
│ └── backtest.py # Rolling 1-day VaR backtest over holdout period
│
├── notebooks/
│ └── 01_eda.ipynb # Feature inspection, regime plots, lookahead sanity check
│
├── data/
│ ├── raw/ # Yahoo Finance parquet files (git-ignored)
│ └── processed/ # Engineered features (git-ignored)
│
├── checkpoints/ # Model weights + metrics JSON (git-ignored by default)
│ ├── SPY_best.pt
│ ├── SPY_metrics.json
│ └── ...
│
└── tests/
├── test_simulation.py
├── test_features.py
├── test_dataset.py
├── test_vol_transformer.py
├── test_benchmarking.py
└── test_train_config.py
git clone https://github.com/YOUR_USERNAME/volatility-waves.git
cd volatility-waves
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtrequirements.txt installs the default PyTorch build. For a specific backend:
# CUDA (Linux / Windows)
pip install torch --index-url https://download.pytorch.org/whl/cu121
# MPS is bundled in the standard macOS wheel — no extra step
pip install torch
# CPU only (e.g. Streamlit Cloud free tier)
pip install torch --index-url https://download.pytorch.org/whl/cpuTraining auto-detects the best device in order: CUDA → MPS → CPU. Override with --device cuda|mps|cpu.
python -m src.data.fetchDownloads SPY, AAPL, NVDA, GLD, and ^VIX from Yahoo Finance (2010–2024) into data/raw/.
Optional — custom tickers or date range:
python -m src.data.fetch --tickers SPY AAPL VIX --start 2015-01-01 --end 2024-12-31python -m src.features.buildComputes all vol features per ticker with rolling quantile regime labels. All quantiles use only past data — no lookahead leakage.
pytest tests/ -vThe test suite covers feature leakage, dataset shapes, simulation properties (VaR coverage rate, CVaR ≥ VaR), and Kupiec test calibration.
python -m src.train --ticker SPYRuns 5-fold walk-forward cross-validation, trains the final model on the selection set, evaluates on a 252-day holdout, and saves to checkpoints/SPY_best.pt.
Key flags:
| Flag | Default | Description |
|---|---|---|
--ticker |
SPY |
Ticker to train on |
--epochs |
100 |
Training epochs |
--lookback |
63 |
Input window (trading days) |
--horizon |
21 |
Forecast horizon (trading days) |
--alpha |
0.3 |
Regime classification loss weight |
--device |
auto | Force cuda, mps, or cpu |
--run-ablation |
off | 4-way ablation study |
--n-splits |
5 |
Walk-forward folds |
--test-size |
252 |
Holdout days |
--seed |
42 |
Global random seed |
Ablation study:
python -m src.train --ticker SPY --run-ablationTrains 4 variants (full / no regime head / no VIX / 1-layer encoder) and reports mean ± std vol MAE for each.
python scripts/backtest.py --ticker SPYRolls a 1-day VaR backtest over the 252-day holdout, comparing the AI vol model against GARCH(1,1) and EWMA via the Kupiec likelihood-ratio test.
Example output:
==============================================================
VaR Backtest — SPY (95% confidence)
==============================================================
Model Breach Expected Kupiec LR p-val Result
--------------------------------------------------------------
AI vol 4.76% 5.00% 0.412 0.5211 PASS
GARCH 5.16% 5.00% 0.181 0.6706 PASS
EWMA 6.35% 5.00% 9.241 0.0024 FAIL
==============================================================
Optional flags: --conf 0.99, --test-size 504, --save.
streamlit run app.pyjupyter notebook notebooks/01_eda.ipynb- Push the repo to GitHub.
- Go to share.streamlit.io → New app → point to
app.py. - Confirm
requirements.txtis detected in Advanced settings → Packages. - The app falls back to GBM baseline automatically if no checkpoint is found — safe to deploy without weights.
For CPU-only deployment, replace the torch line in requirements.txt:
--index-url https://download.pytorch.org/whl/cpu
torch==2.5.1+cpu
Inference on the ~250k-parameter model runs in under 50ms on CPU.
MIT — see LICENSE.
This project is for educational and research purposes only. It is not financial advice and should not be used to make investment decisions.