# fast-vollib showcase

A self-contained, static showcase for [fast-vollib](https://github.com/raeidsaqur/fast-vollib):
real SPX volatility surfaces fitted four different ways, the arbitrage each fit leaves behind,
an interactive Greeks playground, Monte-Carlo simulation, and backend benchmarks.

Live: <https://www.cs.toronto.edu/~raeidsaqur/research/ml-quant-finance/>

## Portability

**This folder is the unit of deployment.** It has no dependency on anything outside itself:

* every path in the HTML/CSS/JS is **relative**, so it works from any base path
  (`/~user/research/ml-quant-finance/`, `/fast-vollib/`, or a bucket root);
* there is **no third-party JavaScript** at all — see `vendor/VERSIONS.md` for why the 3-D
  surface renderer and the chart helpers are hand-written rather than Three.js and D3;
* no build step is needed to *view* it: it is HTML, CSS and ES modules;
* the data pipeline lives in `scripts/` inside this folder, so the whole thing moves together.

To host it elsewhere:

```bash
TARGET=s3://my-bucket/fast-vollib ./deploy.sh     # or any rsync/scp destination
```

The only touch points outside this folder are the link from the site homepage and the
site's own deploy script.

## Layout

```
index.html            the page
showcase.css          all styling, no external fonts
config.js             generated by deploy.sh, gitignored (Clerk publishable key)
config.example.js     committed template
js/
  bs.js               Black-Scholes + Greeks in the browser (validated, see below)
  surface3d.js        canvas height-map surface renderer
  charts.js           SVG scales, axes, lines, scatter, heatmap, legend
  app.js              page orchestrator
data/                 generated JSON artefacts (all derived, none raw)
scripts/              the pipeline that produces data/
  extract_spx.py      OptionMetrics -> local parquet cache (runs once)
  common.py           shared helpers, provenance, JSON writer
  build_surfaces.py   multi-algorithm time-lapse + calm/stressed day fits
  build_greeks_mc.py  Greeks cross-check grid + Monte Carlo
  build_benchmarks.py backend benchmarks
  verify_js.mjs       diffs js/bs.js against fast-vollib
vendor/VERSIONS.md    why there is nothing vendored
dev-notes/            overnight ledger and report
```

## Rebuilding the data

On a Mathematical Institute GPU node (pyro/sauron — `/scratch` is node-local, so the
environment is per-node):

```bash
./setup_env.sh                 # builds the venv, pins torch==2.6.0+cu126
./build.sh                     # everything
./build.sh --skip-extract      # reuse the cached SPX extracts (the slow NFS read)
./build.sh --skip-extract --skip-bench
```

`build.sh` writes raw WRDS extracts to `$FV_SHOWCASE_CACHE` (default: `../../../cache`),
which is deliberately **outside** this folder — this folder is committed and deployed, and
those extracts must never be.

### Environment gotcha

The `cu126` PyTorch index also serves `cu130` wheels for the newest torch, and a cu130 wheel
will not initialise on this driver (560.35.05 / CUDA 12.6). `torch==2.6.0+cu126` is pinned in
`setup_env.sh` for that reason.

## Data provenance and licensing

Every real-data figure is derived from **OptionMetrics IvyDB US, accessed via WRDS**, for
**SPX (secid 108105)**, over **Q2 2025 (2025-04-01 → 2025-06-30)** — the latest complete
calendar quarter in the 2025 files, which end 2025-08-29.

OptionMetrics data is licensed and **is not redistributed**. Only derived artefacts are
published:

* implied volatilities on a coarse 25 × 8 log-moneyness × maturity grid;
* fitted surface diagnostics (RMSE, Durrleman g, density, violation counts);
* market points **binned** to a coarse moneyness grid, published only where a bin aggregates
  at least three quotes, so no published value corresponds to a single contract.

No raw quotes, no per-contract rows, no `secid`/`optionid`/bid/ask columns appear in `data/`.
`deploy.sh` greps for those column names and refuses to deploy if it finds them.

## The browser Black-Scholes

`js/bs.js` is a re-implementation, not fast-vollib. It exists so the hero animation and the
playground respond instantly. Its agreement with fast-vollib is **measured, not asserted**:
`build_greeks_mc.py` writes fast-vollib's values on a fixed 1,260-point grid to
`data/crosscheck.json`, and `scripts/verify_js.mjs` recomputes the same grid in
the browser implementation, diffs them, and writes the measured maxima back into that file.
The page displays them. It uses Hart's normal CDF (~1e-15), not Abramowitz–Stegun.

`node` is not installed on the GPU nodes, so `deploy.sh` runs this check over ssh on the web
host, which has Node 22, and copies the annotated JSON back.

## Newsletter sign-up

Optional and feature-flagged. `config.js` (generated at deploy time, gitignored) carries only
Clerk's **publishable** key; the secret key never leaves the host's `~/.env_secrets`. With
`clerkEnabled: false` — the committed default — no external request is made and the section
shows an email fallback instead.
