Numerical methods
Most of quantitative finance has no closed form. Implied volatility is the root of an equation nobody can invert; American options have no Black-Scholes; most expectations are integrals nobody can do. What stands between the model and a number is a small set of numerical methods — root-finding, quadrature, trees, and Monte Carlo — plus the variance-reduction tricks that make Monte Carlo cheap enough to use. This chapter builds each one and races them against each other on the same option.
Every numerical method converts compute into accuracy at a different exchange rate. Bisection buys one bit of accuracy per function call; Newton’s method roughly doubles the number of correct digits per call; a binomial tree’s error shrinks like ; crude Monte Carlo crawls at — to get one more digit you need a hundred times the paths. The craft is matching the method to the problem: fast converters when you have smoothness and derivatives, robust plodders when you have nothing, and — when you are stuck with Monte Carlo, as you usually are in high dimensions — variance reduction, which changes not the exchange rate but the constant in front of it, often by a factor of ten or more. Free accuracy, if you know the derivations.
Root-finding: bisection and Newton#
The prototype problem: find with . Bisection starts with a bracket where changes sign, evaluates the midpoint, keeps the half that still brackets the root, and repeats. The bracket halves each step, so the error after steps is — linear convergence, one bit per evaluation, guaranteed for any continuous . Newton’s method is greedier: linearise at the current guess and jump to where the tangent line crosses zero,
Near a simple root the error squares each step (): quadratic convergence, doubling the correct digits per iteration — three or four steps to machine precision. The price is fragility: it needs the derivative, and a bad start can diverge. Production code hybridises — Newton steps inside a bisection-maintained bracket.
implied_vol converges in a handful of iterations — falling back to bisection where vega collapses (deep in/out-of-the-money, short expiry), precisely the regions where Newton stalls on a flat function. See Options & greeks for what the resulting surface means.Numerical integration#
Prices are expectations, and expectations are integrals: . In one dimension, deterministic quadrature is unbeatable. The trapezoid rule joins sample points with straight lines (error for step ); Simpson’s rule fits parabolas through triples of points,
so doubling the points buys sixteen times the accuracy on smooth integrands. This is how you price a European option under any density you can evaluate — integrate payoff times density on a grid. The catch is the curse of dimensionality: a grid in dimensions needs points, hopeless for a basket of 20 assets or a path-dependent payoff over 252 steps. High dimensions belong to Monte Carlo, whose rate is slow but — crucially — dimension-independent.
Binomial trees: CRR and its convergence#
Between closed forms and Monte Carlo sit trees. The Cox-Ross-Rubinstein tree chops into steps of and lets the price jump up by or down by each step, with a risk-neutral up-probability chosen to match the risk-free drift and the volatility:
Price by backward induction: at expiry the option is worth its payoff at each terminal node; step backwards, each node’s value is the discounted expectation — and for an American option, the maximum of that and immediate exercise. That one extra is why trees matter: early exercise breaks every closed form, and the tree handles it for free. As the binomial terminal distribution converges (CLT again) to the log-normal of GBM, and the European tree price converges to Black-Scholes with error — visibly oscillating around the true value as strikes fall between or on node levels.

import edgekit as ek
# American put on the house scenario: S=100, K=100, T=1y, r=5%, sigma=20%.
amer = ek.options.binomial_tree(100, 100, 1.0, 0.05, 0.20, n=500, kind="put", american=True)
# Same tree, European exercise — converges to Black-Scholes as n grows.
euro = ek.options.binomial_tree(100, 100, 1.0, 0.05, 0.20, n=500, kind="put", american=False)
amer - euro # > 0: the early-exercise premium, priced by one extra max()Monte Carlo, and why variance reduction pays#
Crude Monte Carlo prices by simulating paths and averaging discounted payoffs . The standard error is — to halve it, quadruple the paths. Variance reduction attacks instead of .
Antithetic variates#
Simulate paths in mirrored pairs: for every draw of Gaussian shocks , also run , and average the pair’s payoffs .
For the average of two identically-distributed estimates with correlation :
Independent pairs () give — exactly what two plain paths give, no gain. But if the payoff is monotone in the shocks (a call is: bigger shocks, bigger payoff), then is anticorrelated with , , and the pair average beats two independent paths — at half the random-number cost, since each pair reuses its draws. When one path lands high, its mirror lands low; the errors cancel by construction. For payoffs that are close to linear in the shocks, and the variance nearly vanishes.
Control variates#
Pair each payoff with a correlated quantity whose true mean you know — for options, the terminal price itself: under the risk-neutral measure. Then estimate with the corrected average
Whatever luck pushed the simulated above its known mean also pushed up; subtracting times the observable error removes the shared luck. Minimising over gives the optimal coefficient and the payoff:
The same as an OLS slope, and the same as a regression’s unexplained variance — this is regression, applied to simulation error. With , variance drops ; at , .
Low-discrepancy sequences#
Random points clump and leave voids — that irregularity is the . Quasi-Monte Carlo abandons randomness for sequences engineered to fill space evenly. The Halton sequence builds coordinate by reflecting the integers written in base (the -th prime) about the decimal point: in base 2 the sequence runs — each new point landing in the largest remaining gap. Integration error for such sequences obeys the Koksma-Hlawka bound,
which is nearly in modest dimension — a square-root improvement in the exponent, worth orders of magnitude at large . The factor is the warning label: as grows the advantage fades (in practice QMC still wins well past the bound’s pessimism, because payoffs concentrate their variance in a few effective dimensions). Push Halton uniforms through the inverse normal CDF and you have evenly-spaced Gaussians ready for any path simulator from Simulating markets.

Price the European call with , , , . Black-Scholes says — the target. A CRR tree is within a cent by steps (error ). Crude Monte Carlo with paths: the discounted payoff has , so the standard error is — a penny and a half of noise per dollar of option. Antithetic pairing ( for this payoff) cuts the variance by , standard error to roughly . A control variate on ( at the money) multiplies variance by : standard error , a path saving. Halton quasi-normals at the same land within about a cent. Moral: before buying compute, spend ten lines of code — the tricks stack, and together they buy a effective speed-up.
import numpy as np
import edgekit as ek
S0, K, T, r, sigma = 100.0, 100.0, 1.0, 0.05, 0.20
n = 10_000
disc = np.exp(-r * T)
def terminal(z): # exact GBM terminal price from N(0,1) draws
return S0 * np.exp((r - 0.5 * sigma**2) * T + sigma * np.sqrt(T) * z)
# 1) Antithetic: second half of draws is the negation of the first.
z = ek.sim.antithetic(n)[:, 0]
pay = disc * np.maximum(terminal(z) - K, 0.0)
est_anti = pay.mean()
# 2) Control variate: S_T has known risk-neutral mean S0 * e^{rT}.
st = terminal(np.random.default_rng(11).standard_normal(n))
y = disc * np.maximum(st - K, 0.0)
cv = ek.sim.control_variate(y, st, S0 * np.exp(r * T))
cv["estimate"], cv["beta"], cv["var_reduction"]
# 3) Quasi-Monte Carlo: Halton points -> normals -> paths.
zq = ek.sim.quasi_normals(n)[:, 0] # norm_ppf(halton), evenly spaced draws
est_qmc = (disc * np.maximum(terminal(zq) - K, 0.0)).mean()Choosing a method#
| Method | Error rate | Best for | Breaks down when |
|---|---|---|---|
| Newton root-finding | quadratic (digits double) | implied vol, yields — smooth monotone f | flat vega / bad start; keep a bisection bracket |
| Simpson quadrature | O(h^4) | 1-D expectations with known density | dimension > ~3 (grid explodes) |
| CRR binomial tree | O(1/n) | American / early-exercise features | path-dependence, many state variables |
| MC + variance reduction | O(1/sqrt(n)), smaller constant | high dimensions, any payoff | control poorly correlated; payoff non-monotone (antithetic) |
| Quasi-MC (Halton) | ~O((log n)^d / n) | smooth payoffs, moderate d | large d; naive error bars (points are not random) |
Next: what happens when the object you estimate is an entire matrix — and most of its eigenvalues are noise. Random matrix theory.

