Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Conditional Normalising Flows for Risk-Averse Portfolio Optimisation

CSE 8803 IUQ, Spring 2026, Georgia Tech. Authors: Agam Saraf, Shrey Patel, Alexander Coles.

This repository implements the OUU pipeline described in deliverables/Final_Report.pdf: a Mean-CVaR portfolio optimiser whose scenario generator is a conditional Neural Spline Flow (NSF) trained on 10 years of S&P 500 daily returns. The frontier model adds a 12-component PCA projection and a rolling-correlation context channel, fixing the calibration failure modes diagnosed in the baseline.

What lives where

.
|-- src/
|   |-- data_module.py     # yfinance + FRED loader, train/test/COVID split
|   |-- models.py          # build_realnvp_flow, build_nsf_flow (conditional)
|   |-- baselines.py       # Gaussian + Ledoit-Wolf Mean-CVaR baseline
|   `-- evaluator.py       # cvar5, sharpe, VIX bins, portfolio_metrics
|-- scripts/
|   |-- train_baseline_nsf.py    # 1) Train + evaluate the baseline NSF
|   |-- calibrate_baseline.py    # 2) Realised vs predicted CVaR / vol / coverage
|   |-- bootstrap_ci_baseline.py # 3) Paired-bootstrap CIs for baseline NSF
|   |-- train_pca_frontier.py    # 4) Train PCA + corr NSF, run all final analyses
|   |-- evaluate_cv_windows.py   # 5) 4-window expanding-CV stress test
|   `-- make_report_figures.py   # 6) Build the head-to-head figures used in the report
|-- results/
|   |-- *.csv                    # All numeric outputs used in the report
|   `-- figures/*.png            # All figures rendered by the scripts
|-- deliverables/               # Final Report and Presentation PDFs
|-- checkpoints/                 # Trained NSF / PCA-NSF weights and stats
|-- requirements.txt
`-- README.md

Reproducing the main results

The full pipeline runs on CPU in well under an hour. With Python 3.10+:

pip install -r requirements.txt

# (1) Baseline NSF: trains, optimises Mean-CVaR portfolio, saves weights
python scripts/train_baseline_nsf.py

# (2) Per-bin reliability of CVaR, volatility and 95% PI coverage
python scripts/calibrate_baseline.py

# (3) Paired-bootstrap 95% CIs for Sharpe / CVaR (Gaussian, NSF static, NSF adaptive)
python scripts/bootstrap_ci_baseline.py

# (4) Frontier model: PCA + corr NSF, full reliability + bootstrap including PCA
python scripts/train_pca_frontier.py

# (5) Head-to-head report figures (correlation, vol ratio, coverage, CVaR)
python scripts/make_report_figures.py

Step (4) is the canonical command for the main results: it trains the frontier flow, generates regime-adaptive weights, computes per-day predictive intervals, builds the Gaussian counterfactual table, and runs the 3-model paired bootstrap. Step (5) regenerates the cross-model comparison figures used in the report from the saved CSVs.

The 4-window expanding cross-validation (used as ablation A2 in the report) is regenerated by:

python scripts/evaluate_cv_windows.py

All scripts use seed=42 and write to results/. Existing checkpoints under checkpoints/ are overwritten on retraining.

Headline numbers (503-day held-out test set)

Model Sharpe (95% CI) CVaR_5 (%) Coverage at VIX>=30 Crisis corr (pred / real)
Gaussian 0.68 [-0.69, 2.13] -1.67 [-2.07, -1.32] n/a 0.05 / 0.66
NSF (baseline) 0.95 [-0.42, 2.37] -1.57 [-2.03, -1.22] 20.0% 0.05 / 0.66
NSF (PCA + corr) 0.52 [-0.88, 1.90] -2.28 [-2.79, -1.82] 80.0% 0.65 / 0.66

Sharpe differences are not statistically significant under N=10,000 paired bootstrap (p > 0.25 in every comparison). The PCA + corr flow's gains are concentrated in calibration: the predicted-vs-realised correlation gap drops from 0.61 to 0.01 at VIX >= 30, and overall 95% predictive interval coverage rises from 80.6% to 97.6%.

See deliverables/Final_Report.pdf for the full discussion, including the calibration-Sharpe trade-off and the Gaussian counterfactual table.

Data provenance

  • Equity prices: 30 S&P 500 stocks, 11 GICS sectors (Appendix A of the report). Downloaded at runtime via yfinance.download(period="10y") (adjusted close).
  • VIX: CBOE VIX close, downloaded at runtime from the FRED CSV endpoint (VIXCLS).

The COVID-19 crash window 2020-02-15 to 2020-05-01 (52 trading days) is removed from training and held aside for the OOD backtest.

Repository access for course staff

A read-only copy lives at https://github.gatech.edu/Acoles6/market-shocks-class. The instructor pchen402 and TA psi6 have been added as collaborators.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages