Everyone can quote the Black-Scholes formula. Far fewer can say what it is — where it comes from, why those two normal CDFs are there, and what would have to be true for it to be wrong.

The formula is not a model of what stocks do. It’s the answer to a narrower and much stranger question: if I sold this option and hedged it perfectly, what would that hedge have cost me? This post derives it from that question, then does the thing most derivations skip — actually runs the hedge, path by path, and checks that it replicates the payoff.

The setup, and what we’re assuming

We assume the stock follows geometric Brownian motion:

    \[dS_t = \mu S_t dt + \sigma S_t dW_t\]

and that we can trade continuously, borrow and lend at a constant rate r, and face no transaction costs. Every one of those is false in the real world. We’ll come back to which ones matter.

Write V(t, S) for the value of the option. It’s a function of time and the stock price — that’s the only structural assumption we need about it.

Step one: what happens to the option’s value

The stock moves randomly, so V(t, S_t) moves randomly too. To describe how, we need Ito’s lemma — the chain rule for stochastic processes:

    \[dV = \left( \frac{\partial V}{\partial t} + \mu S \frac{\partial V}{\partial S} + \frac{1}{2}\sigma^2 S^2 \frac{\partial^2 V}{\partial S^2} \right) dt + \sigma S \frac{\partial V}{\partial S} dW_t\]

If you’ve only seen ordinary calculus, that third term in the bracket is the surprise. In ordinary calculus a second-order term would be negligible. Here it isn’t, because (dW)^2 = dt rather than something vanishingly small — a Brownian path wiggles so violently that its squared increments accumulate at a finite rate. That single fact is where the \frac{1}{2}\sigma^2 S^2 \Gamma term comes from, and it’s ultimately why volatility has a price at all.

Step two: build a portfolio with no risk

Here’s the trick the whole thing rests on. Notice that dV and dS are driven by the same dW_t. So we can cancel it.

Form a portfolio: short one option, long \Delta shares.

    \[\Pi = -V + \Delta S\]

Its change over an instant:

    \[d\Pi = -dV + \Delta dS\]

Substitute both expressions and collect the random parts. The dW_t terms are:

    \[-\sigma S \frac{\partial V}{\partial S} dW_t + \Delta \sigma S dW_t\]

Choose

    \[\Delta = \frac{\partial V}{\partial S}\]

and they cancel exactly. The portfolio has no random component left. Over the next instant, we know precisely what it will be worth.

That \Delta is the option’s delta, and this is where it comes from — not a sensitivity someone decided to define, but the number of shares that makes the risk disappear.

Step three: no risk means no excess return

We now hold something riskless. If it earned more than the risk-free rate, you’d borrow at r, buy it, and pocket the difference with no risk — an arbitrage. If it earned less, you’d do the reverse. So it must earn exactly r:

    \[d\Pi = r \Pi dt\]

Substitute, cancel the dt, and rearrange. The \mu terms cancel along the way — which is the fact worth pausing on, and which we’ve looked at properly elsewhere. What survives is the Black-Scholes PDE:

    \[\frac{\partial V}{\partial t} + \frac{1}{2}\sigma^2 S^2 \frac{\partial^2 V}{\partial S^2} + rS\frac{\partial V}{\partial S} - rV = 0\]

Read it as a statement about a hedged book rather than as an equation. Each term is a P&L stream:

  • \frac{\partial V}{\partial t}theta: what you lose to time passing.
  • \frac{1}{2}\sigma^2 S^2 \frac{\partial^2 V}{\partial S^2}gamma: what you earn from rebalancing as the stock moves around.
  • rS\frac{\partial V}{\partial S} - rVcarry: the financing on the shares you hold and the money you tied up.

The equation says these three sum to zero. A delta-hedged option book is a bet that gamma income covers theta bleed. That’s not an interpretation of the PDE — it is the PDE.

Step four: the boundary condition does the rest

The PDE describes any derivative on this stock. What makes it a call is the terminal condition:

    \[V(T, S) = \max(S - K, 0)\]

Solve the PDE with that boundary and you get the formula:

    \[C = S_0 N(d_1) - K e^{-rT} N(d_2)\]

    \[d_1 = \frac{\ln(S_0/K) + (r + \frac{1}{2}\sigma^2)T}{\sigma\sqrt{T}}, \quad d_2 = d_1 - \sigma\sqrt{T}\]

What N(d_1) and N(d_2) actually mean

These get memorised and rarely understood, which is a shame because they’re the most quotable part of the formula.

N(d_2) is the probability the option finishes in the money — under Q, not under your own view. So Ke^{-rT}N(d_2) is the discounted strike you expect to pay, weighted by the chance you’ll pay it.

N(d_1) is the delta: the shares you hold today. It’s also a probability, but under a different measure — one where the stock itself is the numeraire. So S_0 N(d_1) is the value of the stock you expect to receive, weighted appropriately.

Price equals what you expect to receive minus what you expect to pay. The formula is a two-line accounting statement wearing a lot of notation.

Does the hedge actually work? Run it.

This is the part worth doing yourself, because it turns the derivation from an argument into a fact.

The claim: sell the call for the Black-Scholes price, delta-hedge it, and at expiry your portfolio should be worth exactly the payoff you owe. No profit, no loss.

import numpy as np
from scipy.stats import norm

S0, K, r, sigma, T = 100.0, 100.0, 0.05, 0.20, 1.0
mu = 0.10                      # real-world drift; should not matter
n_paths, n_steps = 50_000, 252  # daily rebalancing
dt = T / n_steps

def bs_call(S, K, r, sigma, tau):
    d1 = (np.log(S/K) + (r + 0.5*sigma**2)*tau) / (sigma*np.sqrt(tau))
    d2 = d1 - sigma*np.sqrt(tau)
    return S*norm.cdf(d1) - K*np.exp(-r*tau)*norm.cdf(d2)

def bs_delta(S, K, r, sigma, tau):
    d1 = (np.log(S/K) + (r + 0.5*sigma**2)*tau) / (sigma*np.sqrt(tau))
    return norm.cdf(d1)

np.random.seed(1)
S  = np.full(n_paths, S0)
V0 = bs_call(S0, K, r, sigma, T)          # we sell at this price

delta  = bs_delta(np.array([S0]), K, r, sigma, T)[0]
cash   = V0 - delta*S0                     # premium in, shares bought
shares = np.full(n_paths, delta)

for i in range(1, n_steps + 1):
    Z    = np.random.standard_normal(n_paths)
    S    = S * np.exp((mu - 0.5*sigma**2)*dt + sigma*np.sqrt(dt)*Z)
    cash = cash * np.exp(r*dt)             # cash earns the risk-free rate
    tau  = T - i*dt

    new_delta = (bs_delta(S, K, r, sigma, tau) if tau > 1e-12
                 else (S > K).astype(float))
    cash  -= (new_delta - shares) * S      # buy/sell to the new delta
    shares = new_delta

portfolio = shares*S + cash
payoff    = np.maximum(S - K, 0.0)
err       = portfolio - payoff

print(f"Black-Scholes price at t=0: {V0:.4f}")
print(f"Mean hedge error:           {err.mean():+.4f}")
print(f"Std dev of hedge error:     {err.std():.4f}")

Which gives:

Black-Scholes price at t=0: 10.4506
Mean hedge error:           -0.0010
Std dev of hedge error:     0.4290

On average, the hedge lands on the payoff to within a tenth of a cent. We sold at 10.45, ran the hedge, and ended up owing exactly what we had. The premium was the cost of the hedge — which is what the derivation claimed.

Two things worth noticing. First, we simulated the stock with \mu = 10\%, nowhere near the risk-free rate, and the hedge still worked. The drift genuinely doesn’t matter. Change it to 30% or to -5\% and the mean error stays at zero.

Second, that standard deviation of 0.43 is not model error. It’s the price of rebalancing 252 times instead of continuously.

The gap between theory and a real desk

Watch what happens when we vary how often we rebalance:

    4 rebalances   mean error -0.0184   std 3.1490
   21 rebalances   mean error -0.0138   std 1.4574
   63 rebalances   mean error -0.0110   std 0.8526
  252 rebalances   mean error -0.0062   std 0.4232
 1008 rebalances   mean error +0.0006   std 0.2138

The mean stays at zero throughout — the hedge is unbiased however lazily you run it. But the spread shrinks roughly like 1/\sqrt{n}: quadruple the rebalancing, halve the error. Hedge four times a year and any individual trade can be off by several points. Hedge daily and you’re within half a point.

This is the real content of “continuous trading”. Black-Scholes doesn’t need the world to be continuous; it needs you to rebalance often enough that the leftover noise is small relative to your risk appetite. And in practice you can’t just rebalance a thousand times, because every trade costs money — which is why real desks hedge on bands and thresholds rather than on a schedule.

Which assumptions actually break

The famous list of Black-Scholes assumptions is not equally famous for being wrong. Ranked by how much they hurt:

  • Constant volatility — badly wrong, and it shows. Real option prices imply different volatilities at different strikes. That’s the smile, and it exists because returns aren’t lognormal: crashes are fatter and more sudden than the model allows. Everything from local vol to stochastic vol exists to patch this.
  • Continuous paths — wrong, and it matters for the wings. Prices gap. A hedge that assumes you can always trade at the last price fails precisely when you need it.
  • No transaction costs — wrong, and it changes your behaviour. It’s why nobody hedges continuously even when they could.
  • Single constant rate — wrong, but manageable. Real desks discount on a curve and worry about funding spreads. Structurally fine, just more bookkeeping.
  • Lognormal terminal distribution — wrong, but it’s the same objection as constant vol.

Given all that, why does anyone still use it? Because it survives as a language rather than as a forecast. Nobody believes the volatility surface is flat; everyone quotes options in Black-Scholes implied vol because it’s a clean way to express a price. The formula became the unit of measurement even after it stopped being the model.

What to take away

Black-Scholes is a hedging argument, not a prediction. Build a portfolio whose risk cancels, insist that riskless things earn the riskless rate, and the price falls out. Delta is what makes the cancellation work, gamma is what you’re paid for, theta is what you pay, and the drift never enters because a hedged book doesn’t care about direction.

If you want to see this built rather than argued — replication and no-arbitrage from first principles, then the measure change, then a Monte Carlo pricer benchmarked against exactly this formula with a full validation suite — that’s the arc of Risk-Neutral Valuation.

Until then: take the hedging code above, set volatility in the simulation to something different from the volatility in the delta, and watch the mean error stop being zero. That gap is vega, and it’s where the next set of models begins.


Get the Quant Reference Card — four pages covering stochastic calculus, measure change, the models you’ll actually meet, discretisation, rates, credit and XVA, with the practitioner notes that don’t make it into textbooks. Free.