Push n toward infinity (more steps, each one smaller) and the binomial tree's up/down lattice approaches a geometric Brownian motion (GBM): a continuous process whose log-returns are normally distributed, which is precisely why terminal stock prices end up lognormally distributed. Black, Scholes, and Merton solved the continuous-time replication argument directly (via a partial differential equation you are not required to derive here, ∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0, if you have seen stochastic calculus and are curious) and arrived at a closed form for a European call and put:
d1 = [ ln(S/K) + (r − q + ½σ²)·T ] / (σ·√T)
d2 = d1 − σ·√T
Call = S·e^(−qT)·N(d1) − K·e^(−rT)·N(d2)
Put = K·e^(−rT)·N(−d2) − S·e^(−qT)·N(−d1)
N(·) is the standard normal cumulative distribution function. Read N(d2) as (loosely) the risk-neutral probability the option finishes in the money, and N(d1) as the probability-weighted share of the stock's expected value that ends up in the payoff, the two are close but not identical, a distinction the Common Mistakes section returns to. BSM is not a different, competing model from the binomial tree, it is the same risk-neutral argument, taken to the limit. If your binomial price and your BSM price disagree once n is large, you have a bug, not a modeling choice; Worked Example 5 proves this numerically with a full convergence table. Here is the closed-form pricer, using only math.erf for the normal CDF (no scipy needed):
def n_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
def bsm_price(S, K, r, q, sigma, T, call=True):
d1 = (math.log(S / K) + (r - q + 0.5 * sigma**2) * T) / (sigma * math.sqrt(T))
d2 = d1 - sigma * math.sqrt(T)
if call:
return S * math.exp(-q * T) * n_cdf(d1) - K * math.exp(-r * T) * n_cdf(d2)
return K * math.exp(-r * T) * n_cdf(-d2) - S * math.exp(-q * T) * n_cdf(-d1)
Advertisement
Black-Scholes assumptions and where they break
Every BSM assumption, and where it actually breaks. State each one out loud, because DV1.03 and every real trading desk exists to manage the gap between this list and reality:
- The underlying follows GBM with constant volatility
σ. Breaks whenever the market prices different strikes or maturities at different implied vols, the volatility skew/smile (Worked Example 6) is a direct, daily, observable falsification of this assumption. - Prices are lognormally distributed, no jumps. Breaks in a crash: the 19 October 1987 "Black Monday" crash is the standard reference point after which index option markets have priced in a persistent downside skew (crash insurance demand), a feature BSM's smooth lognormal world cannot generate on its own.
- The risk-free rate
ris constant. Breaks constantly, rates move, which is exactly why rho exists as a Greek at all. - Frictionless, continuous trading (no transaction costs, no bid-ask spread, hedges rebalanced continuously). Breaks in every real market: discrete rebalancing leaves gamma risk between hedges (the very motivation for DV1.03's delta-gamma hedging), and spreads/financing costs mean the textbook parity arbitrage of Worked Examples 1–2 is far thinner, or absent, once real frictions are included.
- European exercise only. Breaks for the enormous universe of American-style equity and futures options, which is why the binomial tree, not the BSM formula, is the right tool the moment early exercise is possible.
- A known, constant dividend treatment (continuous yield
qor discrete cash dividends, fixed in advance). Breaks when a dividend is cut, raised, or the ex-date shifts, a real risk for single-stock options that the index case mostly launders away.
When to use Black-Scholes and when to use a tree
That last point about tool choice is worth stating as its own rule, because it is the practical takeaway of this whole section: use the closed-form BSM formula where its assumptions roughly hold (European exercise, a known dividend treatment), use the binomial tree, or another numerical method, the moment early exercise, discrete path-dependency, or a term structure of rates/volatility genuinely matters. Reaching for the fancier tool by default, or the closed form out of habit, is not sophistication; it is not checking which assumptions the contract in front of you actually satisfies.
{
"skill": "Building a binomial/BSM pricing engine fast, without inheriting a silent sign or convention error",
"use": "Have an AI coding assistant draft the CRR tree function and the BSM+Greeks functions in Python, then reconcile every output against the by-hand 2-step tree, the closed-form parity identity, and a finite-difference cross-check before trusting a single number it produces.",
"tools": ["AI coding assistants — Claude Code, GitHub Copilot, Cursor", "A plain Python/Jupyter environment (no paid dependency needed — math.erf covers the normal CDF)"],
"prompt": "Write a Python option pricer with (1) an n-step CRR binomial tree supporting European and American exercise and a continuous dividend yield, and (2) closed-form Black-Scholes-Merton price and all five Greeks. Use only the stdlib math module. Then write a second, independent finite-difference routine that bumps each input and reprices, so I can cross-check every Greek against your closed-form formula.",
"verify": "Reconcile three ways before trusting the engine with any real position: (1) the n-step binomial price must converge to the BSM price as n grows (Worked Example 5's pattern); (2) every closed-form Greek must match its finite-difference cross-check to several decimal places; (3) BSM call and put prices must satisfy put-call parity exactly. A mismatch on any of the three means a bug, not a modeling nuance — do not proceed until it is found (Guardrail #3, Recompute, applied to code instead of a filed number).",
"diy": "You must already be able to derive the risk-neutral probability, walk a tree backward by hand, and state the BSM formula and its assumptions from memory — this Core section builds exactly that. The AI writes the loop faster than you would; it does not know whether the loop is right.",
"market": "IN"
}