Skip to content

Repository files navigation

Options Pricing Laboratory & Greeks Surface Explorer

Four pricing engines, eleven Greeks, and the hedging-frequency tradeoff that decides whether the P&L survives costs.

Interface demo

Python NumPy SciPy Plotly Tests License

Live demo: https://codeebytee.github.io/01-options-pricing-lab/ (enable Pages: Settings → Pages → main /docs)

Run the interface locally: clone the repo and double-click docs/index.html. No install, no server, no internet — it works with the wifi off. requirements.txt is only needed to re-run the research.


Headline result

At 25 bps one-way transaction cost, the risk-adjusted optimal delta-hedging frequency is every 8 trading days — not daily.

Hedging error shrinks as √(rebalance interval) while costs grow as 1/√(rebalance interval), so the trader's objective is a U-shape with an interior minimum. Daily rebalancing at 25 bps spends 1.57 in fees — more than three times the entire zero-cost P&L standard deviation — to buy a risk reduction the square-root law caps hard. Moving to an 8-day schedule improves the objective by 31% (1.91 → 1.33).

Measured, not asserted: the fitted log-log slope of P&L standard deviation against rebalance interval is 0.4747 against a theoretical 0.5 (20,000 paths, results/hedging_sweep.csv).

What this does

  • Prices European and American options four independent ways — closed-form Black–Scholes–Merton, Cox–Ross–Rubinstein binomial, Crank–Nicolson PDE in log-space with Rannacher startup and PSOR for the American constraint, and Monte Carlo with antithetic and control variates. All four agree to the third decimal, with log-log convergence plots proving the error orders.
  • Computes eleven Greeks through third order — delta, gamma, vega, theta, rho, vanna, volga, charm, veta, speed, zomma — analytically and by independent central finite difference, agreeing to 1e-8 on first-order and better than 3e-5 on the cross-derivatives.
  • Quantifies the gamma-versus-transaction-cost tradeoff in a 20,000-path hedging simulation across a 9-point rebalance grid at four cost levels, showing where "hedge more often" stops being good advice.

Install and run

pip install -r requirements.txt      # 1. only needed to re-run the research
pytest -q                            # 2. 47 tests, ~40s
python scripts/build_frontend.py     # 3. regenerates docs/data.js (~3 min)

To just use the interface, skip all three and open docs/index.html.

The interface

Five tabs, every control wired to real output:

Tab What you can do
Greeks Lab Six sliders driving price and all eleven Greeks, recomputed live in JS on every move. A Greek-profile chart across spot and a rotatable 3D surface over (strike, maturity) — 1,575 closed-form evaluations per redraw.
Hedging Simulator Pick a rebalance frequency, a realised vol, and a cost level. Watch the P&L distribution and the risk-vs-cost U-curve move, with the optimum marked. Drag the cost slider to 25 bps and watch the optimum walk left.
Engines & Convergence Price and runtime for all four engines side by side; log-log convergence plots for the tree, the PDE (with and without Rannacher), and Monte Carlo.
American Exercise Early-exercise premium surface, and three independent American methods (tree, PSOR, LSM) agreeing to the third decimal.
Validation & Stress The analytic-vs-finite-difference Greek table, a live put–call parity check, and a stress panel with buttons that push the pricer to zero vol, zero time, deep OTM and 500% vol — showing the analytic limit instead of NaN.

Panels are labelled LIVE (recomputed in your browser from a JS port of src/models/black_scholes.py) or PRECOMPUTED (from docs/data.js, generated by Python). Nothing is a pre-rendered image.

Design decisions

  • Closed-form math runs live in the browser; everything else is precomputed. Black–Scholes and all eleven Greeks are ~15 lines of erf plus algebra, so every slider genuinely recomputes rather than looking up a grid. The American premium surface (41 × 25 grid of 600-step trees) and the 20,000-path hedging sweep cost CPU-minutes and ship as data. The split is labelled on every panel — a page where nothing is computed live is a slideshow, and a page that pretends a browser ran a 20,000-path Monte Carlo per keystroke is lying.
  • Degenerate inputs take the analytic limit rather than letting the arithmetic decide. At zero total variance $d_1$ divides by zero; the code returns discounted forward intrinsic, a step-function delta, and exactly zero for every second-order Greek. This is both the correct mathematical limit and what stops the charts going blank when a user drags a slider to its endpoint. The stress panel exists to demonstrate it.
  • Negative results are reported, not tuned away. Antithetic and control variates do not stack here — the control variate alone gives 5.76× variance reduction, both together only 2.94×, because the control removes exactly the payoff component antithetic pairing exploits. And the Crank–Nicolson convergence slope comes out at −1.36 against a theoretical −2.0; the experiment design, not the solver, is the likely culprit, and that is what the DEEP_DIVE says rather than quietly refitting over a friendlier range.

Repo map

docs/index.html        the entire interface, single file, works from file://
docs/data.js           generated — precomputed grids and sweeps
scripts/build_frontend.py   regenerates docs/data.js from src/ in one command
scripts/make_results.py     regenerates results/ tables and figures
scripts/make_gif.py         re-records the demo GIF from the real page
src/models/            black_scholes, binomial, pde, monte_carlo, lsm, hedging
src/utils/             config loading, finite-difference Greek auditing
src/data/              market snapshot with a synthetic fallback
tests/                 47 pytest tests, including a Python↔JS parity check
results/               validation tables (CSV), figures, LinkedIn draft
notebooks/             one research notebook telling the story end to end
config.yaml            every number the research scripts use

Validation summary

Check Result
Four engines on one ATM call agree within 2e-3 (results/engine_comparison.csv)
Three American methods agree within 2.6e-3; LSM 0.18 s.e. from the tree
11 Greeks × 4 contracts vs finite difference worst relative error 2.4e-5, on the third derivative
CRR / MC convergence order −0.998 / −0.501 vs theory −1.0 / −0.5
Boyle–Emanuel √m hedging law fitted 0.4747 vs theory 0.5
Put–call parity checked live in the browser on every input change
Rebuild reproducibility build_frontend.py regenerates data.js byte-identical

Data and honesty notes

Market snapshot is SPY daily closes via yfinance, as-of date displayed in the page footer. If the download fails the build falls back to a fixed-seed synthetic snapshot and a banner says so on the page. The default contract is the round-number ATM case ($S=K=100$) so results are reproducible and comparable to textbook values; the SPY snapshot is a one-click preset.

The hedging study is a simulation on GBM paths, not a historical backtest. Costs are proportional to traded notional, one-way, at the stated bps; there is no market-impact or borrow model. There is no look-ahead — the hedge at each rebalance uses only information available then, and is set with the implied vol, not the realised vol the path was generated with. GBM has no jumps, so the reported tails are a floor on the true tails, not an estimate of them.

This is a pricing and risk laboratory. It does not forecast markets and nothing here is investment advice.


New to options? Start with PREREQUISITES.md — no finance background assumed. Want to judge the work? Go to DEEP_DIVE.md — equations, validation numbers, and the limitations.

MIT licensed.

About

Four option pricing engines, eleven Greeks validated against finite differences, and the hedging-frequency tradeoff quantified: at 25bp costs the optimum is every 8 days, not daily.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages