The Black-Scholes formula prices European options by constructing a riskless hedge. The key insight: if you can replicate an option's payoff with stock and bonds, the option must cost the same as the replicating portfolio. No arbitrage forces the price.
The replicating portfolio argument
Hold Δ = ∂C/∂S shares of stock and borrow the rest. This portfolio tracks the option's value instant by instant. By Ito's lemma, the portfolio's randomness (the dB term) cancels the option's randomness. What remains is deterministic, so it must earn the risk-free rate. This no-arbitrage condition gives the Black-Scholes PDE.
Scheme
; Replicating portfolio: Delta shares of stock + bonds; Portfolio value V = Delta * S - B (borrowing B);; At expiry, call payoff = max(S - K, 0); Delta-hedging means adjusting Delta continuously
(define (call-payoff S K) (max (- S K) 0))
; Example: K = 100
(define K 100)
(display "Payoffs at expiry:") (newline)
(for-each (lambda (S)
(display " S = ") (display S)
(display " payoff = ") (display (call-payoff S K))
(newline))
(list 8090100110120))
; At expiry, Delta = 1 if S > K, else 0
(define (delta-at-expiry S K)
(if (> S K) 10))
(display "Delta at expiry:") (newline)
(display " S=110: ") (display (delta-at-expiry 110 K)) (newline)
(display " S=90: ") (display (delta-at-expiry 90 K))
Python
# Replicating portfolio payoffs
K = 100def call_payoff(S, K):
returnmax(S - K, 0)
print("Payoffs at expiry:")
for S in [80, 90, 100, 110, 120]:
print(f" S = {S} payoff = {call_payoff(S, K)}")
# At expiry, delta is 0 or 1print("\nDelta at expiry:")
for S in [90, 100, 110]:
delta = 1if S > K else0print(f" S = {S} delta = {delta}")
Risk-neutral valuation
Under the risk-neutral measure, all assets earn the risk-free rate r on average. The option price is the discounted expected payoff: C = e⁻ʳᵀ Eᵠ[max(S(T)-K, 0)]. We don't need to know the real drift μ; we replace it with r. This works because the hedge eliminates all risk, so risk preferences don't matter.
Scheme
; Risk-neutral pricing: C = e^(-rT) * E_Q[max(S_T - K, 0)]; Under Q: S_T = S0 * exp((r - 0.5*sigma^2)*T + sigma*sqrt(T)*Z)
(define S0 100.0)
(define K 100.0)
(define r 0.05)
(define sigma 0.2)
(define T 1.0)
; Price by averaging over scenarios
(define (risk-neutral-price z)
; z is a standard normal draw
(define S-T (* S0 (exp (+ (* (- r (* 0.5 sigma sigma)) T)
(* sigma (sqrt T) z)))))
(max (- S-T K) 0))
; Use z-values from standard normal: approximate E[payoff]
(define z-values (list -2.0-1.5-1.0-0.50.00.51.01.52.0))
(define payoffs (map risk-neutral-price z-values))
(define avg-payoff (/ (apply + payoffs) (length payoffs)))
(define price (* (exp (* -1 r T)) avg-payoff))
(display "Sample payoffs: ") (display payoffs) (newline)
(display "Avg payoff: ") (display avg-payoff) (newline)
(display "Discounted (approx C): ") (display price)
Python
importmathimport random
S0, K, r, sigma, T = 100, 100, 0.05, 0.2, 1.0def monte_carlo_call(n_sims=100000):
random.seed(42)
total = 0for _ inrange(n_sims):
z = random.gauss(0, 1)
S_T = S0 * math.exp((r - 0.5*sigma**2)*T + sigma*math.sqrt(T)*z)
total += max(S_T - K, 0)
returnmath.exp(-r*T) * total / n_sims
mc_price = monte_carlo_call()
print(f"Monte Carlo call price: {mc_price:.4f}")
print(f"(Black-Scholes exact: 10.4506)")
The Black-Scholes PDE
The no-arbitrage hedge argument yields a PDE: ∂C/∂t + ½σ²S² ∂²C/∂S² + rS ∂C/∂S - rC = 0. This is a backward heat equation. With the boundary condition C(S,T) = max(S-K, 0), its solution is the Black-Scholes formula involving two cumulative normal terms N(d1) and N(d2).
Scheme
; Black-Scholes formula; C = S0*N(d1) - K*e^(-rT)*N(d2); d1 = [ln(S/K) + (r + sigma^2/2)*T] / (sigma*sqrt(T)); d2 = d1 - sigma*sqrt(T); Approximate cumulative normal using Abramowitz & Stegun; Phi(x) = 0.5 * (1 + erf(x / sqrt(2)))
(define (phi x)
; CDF of standard normal, accurate to ~0.0003
(define (erf-approx t)
; Abramowitz & Stegun 7.1.26: erf approximation for t >= 0
(let* ((a1 0.254829592) (a2 -0.284496736) (a3 1.421413741)
(a4 -1.453152027) (a5 1.061405429) (p 0.3275911)
(u (/ 1.0 (+ 1.0 (* p (abs t)))))
(val (- 1.0 (* (+ (* (+ (* (+ (* (+ (* a5 u) a4) u) a3) u) a2) u) a1)
u (exp (* -1 t t))))))
(if (>= t 0) val (- val))))
(* 0.5 (+ 1.0 (erf-approx (/ x (sqrt 2))))))
(define (black-scholes S0 K r sigma T)
(define d1 (/ (+ (log (/ S0 K)) (* (+ r (* 0.5 sigma sigma)) T))
(* sigma (sqrt T))))
(define d2 (- d1 (* sigma (sqrt T))))
(- (* S0 (phi d1))
(* K (exp (* -1 r T)) (phi d2))))
(display "BS Call Price: ")
(display (black-scholes 1001000.050.21.0)) (newline)
; Vary strike
(for-each (lambda (K)
(display " K=") (display K)
(display " C=") (display (black-scholes 100 K 0.050.21.0))
(newline))
(list 9095100105110))
The Greeks measure how the option price changes with respect to each input. Delta (Δ) = ∂C/∂S, Gamma (Γ) = ∂²C/∂S², Theta (Θ) = ∂C/∂t, Vega (ν) = ∂C/∂σ, Rho (ρ) = ∂C/∂r. Delta tells you how many shares to hold for the hedge. Gamma tells you how fast to rebalance. Together they quantify the risk landscape.
Scheme
; Greeks via finite differences; Delta โ [C(S+h) - C(S-h)] / (2h); Gamma โ [C(S+h) - 2C(S) + C(S-h)] / h^2
(define (phi x)
; Phi(x) = 0.5 * (1 + erf(x / sqrt(2)))
(define (erf-approx t)
(let* ((a1 0.254829592) (a2 -0.284496736) (a3 1.421413741)
(a4 -1.453152027) (a5 1.061405429) (p 0.3275911)
(u (/ 1.0 (+ 1.0 (* p (abs t)))))
(val (- 1.0 (* (+ (* (+ (* (+ (* (+ (* a5 u) a4) u) a3) u) a2) u) a1)
u (exp (* -1 t t))))))
(if (>= t 0) val (- val))))
(* 0.5 (+ 1.0 (erf-approx (/ x (sqrt 2))))))
(define (bs S K r sigma T)
(define d1 (/ (+ (log (/ S K)) (* (+ r (* 0.5 sigma sigma)) T))
(* sigma (sqrt T))))
(define d2 (- d1 (* sigma (sqrt T))))
(- (* S (phi d1)) (* K (exp (* -1 r T)) (phi d2))))
(define S 100) (define K 100) (define r 0.05)
(define sigma 0.2) (define T 1.0)
(define h 0.01)
; Delta
(define delta (/ (- (bs (+ S h) K r sigma T)
(bs (- S h) K r sigma T))
(* 2 h)))
(display "Delta: ") (display delta) (newline)
; Gamma
(define gamma (/ (+ (bs (+ S h) K r sigma T)
(- (* 2 (bs S K r sigma T)))
(bs (- S h) K r sigma T))
(* h h)))
(display "Gamma: ") (display gamma) (newline)
; Vega (per 1% vol change)
(define vega (/ (- (bs S K r (+ sigma 0.005) T)
(bs S K r (- sigma 0.005) T))
0.01))
(display "Vega: ") (display vega)