Skip to content

About

Black-Scholes pricing and Greeks, variance-reduced Monte Carlo for exotics, and implied-volatility surface construction. A tested, pip-installable Python options-analytics library.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Options Monte Carlo Pricer

Tested Python package for options pricing — Black-Scholes & Greeks, variance-reduced Monte Carlo (benchmarked ~12x), and implied-volatility surface construction. Installable, CI-tested (13 tests), reproducible figures.

Author: Hatef Tabbakhian (Leo) · GitHub · LinkedIn

A derivatives pricing and volatility-analytics engine in Python. It implements closed-form Black-Scholes valuation, a variance-reduced Monte Carlo engine for path-dependent exotics, and implied-volatility surface construction from an option chain.

The goal of the repo is to demonstrate quant-trading fundamentals — option pricing, Greeks, simulation, variance reduction, and implied-vol inversion — with code that is tested, packaged, and reproducible. It is an analytics project: it does not model order books, latency, or exchange connectivity (see Scope & limitations).

CI


What it does

  • Prices European, Asian, Barrier (knock-in/out) and Digital options.
  • Computes analytical Greeks (delta, gamma, vega, theta, rho) in desk conventions, and validates Monte Carlo against the closed form.
  • Reduces Monte Carlo variance with antithetic and control variates, with measured speed-ups (see the benchmark table below).
  • Inverts a market option chain into an implied-vol smile/skew and 3-D surface.

Cross-validation: Monte Carlo vs Black-Scholes

For the ATM 1-year call (S=100, K=100, r=3%, σ=20%), the analytical price is 9.4134. The variance-reduced Monte Carlo estimate lands at 9.412 with a 95% confidence interval that contains the analytical value — the two methods agree, which is the main correctness check for the simulation engine.

Benchmark: variance reduction (measured)

Pricing the same ATM call with and without variance reduction. The variance-reduction ratio is Var(plain) / Var(reduced) at equal path counts — i.e. how many times more plain-MC paths you would need to match the reduced estimator's precision. Runtimes are from a single laptop-class core and are indicative, not a latency claim.

Paths Plain SE Reduced SE Variance-reduction ratio Reduced price Abs. error vs BS Plain runtime Reduced runtime
1,000 0.4272 0.1296 10.9x 9.2725 0.1409 ~0 ms 1 ms
10,000 0.1399 0.0409 11.7x 9.4115 0.0019 1 ms 2 ms
100,000 0.0445 0.0129 11.9x 9.3835 0.0299 16 ms 24 ms
500,000 0.0199 0.0058 11.8x 9.3948 0.0186 83 ms 121 ms

Tradeoff, stated honestly: the reduced estimator costs ~1.5x the wall-clock time per nominal path (antithetic doubles the path count and the control variate adds an O(n) regression), but it buys ~12x lower variance. Net, to hit a target standard error it is far cheaper — you reach it with roughly an order of magnitude fewer effective paths. Reproduce with python scripts/benchmark.py.

Implied volatility smile / skew

The synthetic chain (see Data) is built to show a realistic equity skew — downside puts are bid up — that flattens with maturity and curves up at the wings for longer tenors.

Volatility smile

Implied volatility surface

Volatility surface

Monte Carlo convergence

The antithetic + control-variate estimator (blue) tracks the Black-Scholes value with far fewer paths than plain Monte Carlo (red).

MC convergence

Greek profiles

Greeks


Design notes & decisions

A few choices worth calling out, since the why matters more than the what:

  • Exact log-Euler GBM, not naive Euler. GBM has a closed-form solution, so I simulate S_T = S_0 exp((r − q − ½σ²)t + σ√t Z) rather than stepping the SDE. This is unbiased at any step size; the multi-step grid is kept only because Asian and Barrier payoffs need the whole path.
  • Control variate = discounted terminal price. It is a martingale with a known mean (S₀e^{−qT}), perfectly correlated with the European payoff's main risk factor, and free to compute. The optimal coefficient β* is estimated by regression (Cov(Y,X)/Var(X)) rather than hard-coded.
  • Brent for implied vol, with arbitrage guards. Price is monotone in vol, so a bracketed root-finder is robust and never diverges. If a quote sits outside no-arbitrage bounds the solver returns NaN instead of a misleading number — this is exercised by tests (test_implied_vol_returns_nan_*).
  • Greeks in desk conventions. Vega per 1 vol point, theta per calendar day, rho per 1% rate move — so the magnitudes match how a trader reasons, not the raw partial derivatives.
  • Seed-per-engine reproducibility. Each MonteCarloEngine re-seeds its RNG, so pricing knock-in, knock-out and vanilla on three fresh engines reuses identical paths. That is what makes the knock-in + knock-out = vanilla parity hold to machine precision (test_barrier_in_out_parity), not just within MC noise. (Caught a real bug while writing that test — calling .price() twice on one engine advances the RNG and breaks the path-by-path identity.)

Project structure

Options-Monte-Carlo-Pricer/
├── README.md
├── pyproject.toml               # installable package (pip install -e .)
├── requirements.txt
├── LICENSE
├── .gitignore
├── .github/workflows/ci.yml     # GitHub Actions: pytest on 3.9 / 3.11 / 3.12
├── src/
│   └── options_pricer/
│       ├── __init__.py          # public API
│       ├── black_scholes.py     # closed-form pricing + analytical Greeks
│       ├── monte_carlo.py       # GBM engine, antithetic + control variates
│       ├── payoffs.py           # European / Asian / Barrier / Digital payoffs
│       ├── implied_vol.py       # IV root-finder + VolSurface
│       ├── visualization.py     # matplotlib helpers (house style)
│       └── utils.py             # logging + numerical helpers
├── notebooks/
│   ├── 01_black_scholes_and_greeks.ipynb
│   ├── 02_monte_carlo_pricing.ipynb
│   └── 03_implied_vol_surface.ipynb
├── scripts/
│   ├── generate_outputs.py      # reproduces every figure + dataset
│   └── benchmark.py             # variance-reduction benchmark table
├── tests/
│   └── test_pricing.py          # 13 pytest checks
├── data/
│   ├── sample_option_chain.csv  # synthetic market snapshot (with skew)
│   ├── exotic_book_prices.csv   # MC-priced exotic book
│   └── benchmark.csv            # benchmark results
└── images/                      # generated plots used in this README

Quickstart

git clone https://github.com/Leotaby/Options-Monte-Carlo-Pricer.git
cd Options-Monte-Carlo-Pricer

python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"        # editable install + dev tools (pytest, jupyter)

pytest -q                      # 13 tests
python scripts/generate_outputs.py   # regenerate figures + datasets
python scripts/benchmark.py          # regenerate the benchmark table
jupyter lab notebooks/

Minimal API example

from options_pricer import BlackScholesInputs, bs_price, bs_greeks, OptionType
from options_pricer import MonteCarloEngine, MonteCarloConfig
from options_pricer.payoffs import AsianPayoff

market = BlackScholesInputs(spot=100, strike=100, maturity=1.0, rate=0.03, volatility=0.20)

print(bs_price(market, OptionType.CALL))           # 9.4134
print(bs_greeks(market, OptionType.CALL)["delta"]) # 0.5987

engine = MonteCarloEngine(market, MonteCarloConfig(n_paths=100_000, n_steps=126, antithetic=True))
result = engine.price(AsianPayoff(strike=100, option_type=OptionType.CALL))
print(result.price, result.confidence_interval_95)

Data

The option chain in data/sample_option_chain.csv is synthetic, and deliberately so. It is generated from a known implied-vol function (a parametric equity skew that flattens with maturity), then converted to prices via Black-Scholes. This has two advantages over a one-off vendor snapshot:

  1. Reproducibility — anyone can regenerate it offline with a fixed seed; no API keys or paid market-data feed required for CI or for a reviewer.
  2. A ground-truth check — because the input vol is known, notebook 03 verifies that the implied-vol inversion recovers it (max abs error is reported in the notebook). That would be impossible with real quotes.

The code is structured so a real chain (same columns: strike, maturity, price, option_type) can be dropped in unchanged.


Scope & limitations

Being explicit about what this is and is not:

  • It is options analytics, not an HFT system. There is no order-book simulation, market-data replay, latency budget, or matching engine. Runtimes in the benchmark are indicative of algorithmic cost, not a low-latency claim.
  • Black-Scholes is the pricing model. Constant vol, no jumps, no stochastic vol. The implied-vol surface captures the smile empirically, but the simulator itself does not yet price under that surface (no local/stochastic vol).
  • Barriers are discretely monitored. With finite steps the knock probability is biased low versus continuous monitoring; a Brownian-bridge correction would reduce this.
  • Greeks are analytical (Black-Scholes) only. Monte Carlo Greeks (pathwise / likelihood-ratio) are not yet implemented.
  • Single-threaded NumPy. Fine for the path counts here; large books would want batching or a GPU/Numba path.

Next steps

  • Heston / local-vol simulation so the engine prices consistently with the smile.
  • SVI or SABR parametric, arbitrage-free surface fits.
  • Quasi-Monte Carlo (Sobol) and pathwise Greeks.
  • American exercise via Longstaff-Schwartz least-squares Monte Carlo.

How this maps to quant roles

Relevant to quant trading, derivatives, options market-making, and quant dev interviews:

  • Comfort with both analytical and simulation pricing, cross-validated.
  • Variance reduction framed as what it is — a regression/statistics problem — with measured results rather than assertions.
  • Implied-vol inversion under no-arbitrage constraints (root-finding).
  • Engineering basics: a pip-installable package, a green CI matrix, type hints, logging, tests including edge cases, and reproducible figures.

It is not positioned as an HFT/low-latency project; that would need order-book and microstructure work, which I'm building separately.

License

MIT — see LICENSE.

About

Black-Scholes pricing and Greeks, variance-reduced Monte Carlo for exotics, and implied-volatility surface construction. A tested, pip-installable Python options-analytics library.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages