A Python library for exploring how different numerical methods price options under different stochastic models — and a place to see the connecting mathematics (SDEs, PDEs, Monte Carlo, Malliavin calculus) implemented side by side rather than in isolation.
The organizing idea: a price is really a function of four independent choices —
- Payoff — what the contract pays (call, put, digital, up-and-out, Asian, ...)
- Exercise style — when it can be exercised (European, American)
- Dynamics — what the underlying is assumed to do (GBM, Heston, Merton jump-diffusion, ...)
- Method — how the resulting price is computed (closed form, tree, PDE, Monte Carlo, ...)
optpricing keeps these four as separate, composable objects (Payoff, Option,
StochasticProcess, PricingEngine) rather than one pricing function per model, so the same
option can be priced multiple ways and the ways cross-validate each other. That cross-validation
is used throughout the test suite: independent methods (a lattice vs. a PDE vs. simulation) are
checked against each other, not just against a single "known-good" reference.
Live demo (Streamlit): https://options-pricing-aluaz721.streamlit.app/
from optpricing.engines import BlackScholesEngine, MonteCarloEngine
from optpricing.instruments import Option
from optpricing.market import MarketData
from optpricing.payoffs import Call
from optpricing.processes import GBM
market = MarketData(spot=100.0, rate=0.04, dividend_yield=0.01)
process = GBM(vol=0.2)
option = Option(payoff=Call(strike=100.0), expiry=1.0)
BlackScholesEngine().price(option, process, market).price # closed form
MonteCarloEngine(n_paths=100_000).price(option, process, market).price # simulationEvery process implements simulate() (draw paths under the risk-neutral measure) and, where one
exists in closed form, characteristic_function().
Geometric Brownian motion — GBM. The Black-Scholes-Merton dynamics [1],
[2]:
Simulated exactly (not discretized): GBM.simulate draws
Heston stochastic volatility — Heston. Adds a mean-reverting variance process correlated
with the spot [3]:
Heston.simulate instead uses Andersen's (2008) Quadratic-Exponential
(QE) scheme [5]: at each step,
Merton jump-diffusion — MertonJump. Adds a compound Poisson jump component [6]:
where
Closed form — BlackScholesEngine. The Black-Scholes-Merton formula [1],
[2] for European calls/puts, plus the analogous formula for cash-or-nothing
digitals (both share the same
Monte Carlo — MonteCarloEngine. Simulate, discount, average:
process.simulate() and option.payoff(paths), so it prices any
path-dependent payoff (Asian, barrier) under any process that can simulate itself, with no
per-combination code.
Finite differences — CrankNicolsonEngine. Solves the Black-Scholes PDE directly on a grid
[7], [8]:
using the second-order-accurate Crank-Nicolson scheme (the average of implicit and explicit Euler in time-to-maturity). American exercise is handled via the Brennan-Schwartz (1977) trick [9]: a single forward-elimination/backward-substitution sweep of the tridiagonal system, clipping each value to the intrinsic payoff during back-substitution — no iterative projected solve (PSOR) required. Barrier options are handled by capping the grid domain at the barrier with a zero Dirichlet boundary there — the knock-out condition is the boundary condition, not a separate approximation.
Binomial trees — BinomialTreeEngine. The Cox-Ross-Rubinstein lattice [10]:
Least-Squares Monte Carlo — LongstaffSchwartzEngine. Longstaff & Schwartz (2001)
[11]: simulated paths have no shared grid to run backward induction on directly,
so LSM instead works backward through time steps, and at each step estimates the continuation
value via a cross-sectional least-squares regression of realized (discounted) future cash flows
against a polynomial basis in the current spot — standing in for the conditional expectation a
lattice/PDE method gets for free from its grid structure. Deliberately process-agnostic (only
calls process.simulate()), so it prices American exercise under Heston or Merton jump-diffusion
with no engine-side changes — only the process needs to know how to simulate itself.
Finite difference — FiniteDifferenceGreeks. Bump-and-reprice with central differences;
wraps any PricingEngine, since it only perturbs MarketData/Option and reprices.
Pathwise derivatives — PathwiseGreeks. Differentiates the discounted payoff along each
simulated path instead of bumping and repricing [12]. For GBM,
Needs
Malliavin / likelihood-ratio weights — MalliavinGreeks. Fournié, Lasry, Lebuchoux, Lions &
Touzi (1999) [13]; see also Broadie & Glasserman (1996) [12] and
Glasserman's textbook treatment [14]. Rather than differentiating the payoff, this
differentiates the Gaussian density
for
Antithetic variates. For a monotone payoff, antithetic=True on StochasticProcess.simulate (the process owns the random draws, so it
has to be the one to mirror them): full support for GBM (measured ~25% standard-error reduction),
partial support for Merton (only the Brownian increment is mirrored — a Poisson jump count has no
natural antithetic partner — still ~24% reduction empirically), and explicitly unsupported for
Heston, since the QE scheme's variance step is a nonlinear function of its driving randomness and
naive mirroring has no proven variance-reduction guarantee there. Computing the standard error
correctly under antithetic pairing needs its own care — the two halves of each pair are correlated
by construction, so treating all paths as i.i.d. overstates the error; _variance_reduction.py
instead computes it over the pair averages.
- Black, F. and Scholes, M. (1973). "The Pricing of Options and Corporate Liabilities." Journal of Political Economy, 81(3), 637–654.
- Merton, R. C. (1973). "Theory of Rational Option Pricing." Bell Journal of Economics and Management Science, 4(1), 141–183.
- Heston, S. L. (1993). "A Closed-Form Solution for Options with Stochastic Volatility with Applications to Bond and Currency Options." Review of Financial Studies, 6(2), 327–343.
- Cox, J. C., Ingersoll, J. E. and Ross, S. A. (1985). "A Theory of the Term Structure of Interest Rates." Econometrica, 53(2), 385–407.
- Andersen, L. (2008). "Simple and Efficient Simulation of the Heston Stochastic Volatility Model." Journal of Computational Finance, 11(3), 1–42.
- Merton, R. C. (1976). "Option Pricing When Underlying Stock Returns Are Discontinuous." Journal of Financial Economics, 3(1-2), 125–144.
- Crank, J. and Nicolson, P. (1947). "A Practical Method for Numerical Evaluation of Solutions of Partial Differential Equations of the Heat-Conduction Type." Mathematical Proceedings of the Cambridge Philosophical Society, 43(1), 50–67.
- Wilmott, P., Howison, S. and Dewynne, J. (1995). The Mathematics of Financial Derivatives: A Student Introduction. Cambridge University Press.
- Brennan, M. J. and Schwartz, E. S. (1977). "The Valuation of American Put Options." The Journal of Finance, 32(2), 449–462.
- Cox, J. C., Ross, S. A. and Rubinstein, M. (1979). "Option Pricing: A Simplified Approach." Journal of Financial Economics, 7(3), 229–263.
- Longstaff, F. A. and Schwartz, E. S. (2001). "Valuing American Options by Simulation: A Simple Least-Squares Approach." Review of Financial Studies, 14(1), 113–147.
- Broadie, M. and Glasserman, P. (1996). "Estimating Security Price Derivatives Using Simulation." Management Science, 42(2), 269–285.
- Fournié, E., Lasry, J.-M., Lebuchoux, J., Lions, P.-L. and Touzi, N. (1999). "Applications of Malliavin Calculus to Monte Carlo Methods in Finance." Finance and Stochastics, 3(4), 391–412.
- Glasserman, P. (2004). Monte Carlo Methods in Financial Engineering. Springer, Chapter 7 ("Estimating Sensitivities").
optpricing/ the library
payoffs/ what the contract pays (Call, Put, Asian, barrier, digital)
instruments/ exercise style + expiry (Option, European/American)
processes/ underlying dynamics + simulation (GBM, Heston, MertonJump)
engines/ pricing methods (BlackScholes, MonteCarlo, CrankNicolson, Binomial, LongstaffSchwartz)
greeks/ sensitivity methods (FiniteDifference, Pathwise, Malliavin)
dashboard/ a Streamlit frontend, kept separate from the library — only imports
optpricing's public API, the way an external user would
tests/ one file per engine/concern; conftest.py holds shared fixtures
examples/ minimal end-to-end usage script
Every PricingEngine/GreeksEngine declares what it supports via supports(option, process) and
raises UnsupportedCombination otherwise — not every method is meaningful for every combination
(there's no closed form for American exercise, no CRR tree for Heston), and the library is
explicit about that rather than silently producing a wrong number.
Payoffs: vanilla call/put, Asian (arithmetic/geometric), up-and-out barrier, cash-or-nothing digital. Exercise: European, American (Bermudan is modeled but not yet wired into any engine). Dynamics: GBM, Heston, Merton jump-diffusion. Methods: closed form, Monte Carlo, Crank-Nicolson finite differences, CRR binomial trees, Longstaff-Schwartz. Greeks: finite-difference, pathwise, Malliavin. Variance reduction: antithetic variates.
Not yet implemented: multi-asset/basket options, non-equity asset classes (FX, commodities, rates), local volatility / SABR / variance-gamma dynamics, characteristic-function (FFT/COS) pricing, ADI methods, and other variance-reduction techniques (control variates, QMC).
pip install -e ".[dev]"
pytestFor the dashboard, see dashboard/README.md.