The binomial option pricing model, step by step
The binomial option pricing model values an option by letting the stock move only up or down in each time step, building a tree of possible prices, and working backwards from expiry to today. In each step the option is worth its discounted expected value under the risk-neutral probability.
John Cox, Stephen Ross and Mark Rubinstein published it in 1979. It is the easiest way to see why options have the price they do, and unlike Black-Scholes it handles American options, which can be exercised early.
One step, by hand
A stock is at 100. In one year it will be at either 120 or 80. A call with strike 100 then pays 20 or 0. The risk-free rate is 5%. What is the call worth today?
Build a portfolio that pays the same in both cases. Buy Δ shares and borrow B: it pays 120Δ − Be0.05 if the stock goes up and 80Δ − Be0.05 if down. Matching 20 and 0 gives Δ = 20/40 = 0.5 shares and a loan worth 38.05 today. The portfolio costs 0.5 × 100 − 38.05 = 11.95, so the call must cost 11.95 too, or there is a free profit.
The same price comes from a probability. The risk-neutral probability of an up move is:
and the call is the discounted expected payoff: e−0.05 × (0.6282 × 20 + 0.3718 × 0) = 11.95.
from math import exp
S, K, r, T = 100, 100, 0.05, 1.0
u, d = 1.2, 0.8 # the stock ends at 120 or 80
p = (exp(r * T) - d) / (u - d) # risk-neutral probability of "up"
call_up, call_down = max(S * u - K, 0), max(S * d - K, 0)
price = exp(-r * T) * (p * call_up + (1 - p) * call_down)
print(f"p = {p:.4f}")
print(f"call = {price:.4f}")
# The same price from a replicating portfolio: delta shares plus borrowing
delta = (call_up - call_down) / (S * u - S * d)
borrow = exp(-r * T) * (delta * S * d - call_down)
print(f"delta = {delta:.2f} shares, borrow {borrow:.4f}")
print(f"cost of replicating portfolio = {delta * S - borrow:.4f}")p = 0.6282 call = 11.9508 delta = 0.50 shares, borrow 38.0492 cost of replicating portfolio = 11.9508
Notice what is missing: nobody’s view of whether the stock will rise. The real-world probability never enters. That is the central idea of derivatives pricing.
Growing the tree
With many small steps, Cox, Ross and Rubinstein set the up and down factors from the volatility so the tree matches geometric Brownian motion:
Start at the last column, where each node’s value is the payoff. Step back one column at a time: each node is e−rΔt × (p × up value + (1 − p) × down value). For an American option, also compare with exercising now and keep the larger.
from math import exp, sqrt
def crr_price(S, K, T, r, sigma, steps, kind="call", american=False):
"""Cox-Ross-Rubinstein binomial tree."""
dt = T / steps
u = exp(sigma * sqrt(dt))
d = 1 / u
p = (exp(r * dt) - d) / (u - d) # risk-neutral up probability
disc = exp(-r * dt)
sign = 1 if kind == "call" else -1
# option values at expiry, from the lowest node up
values = [max(sign * (S * u**j * d**(steps - j) - K), 0.0) for j in range(steps + 1)]
for i in range(steps - 1, -1, -1):
for j in range(i + 1):
cont = disc * (p * values[j + 1] + (1 - p) * values[j])
if american:
cont = max(cont, sign * (S * u**j * d**(i - j) - K))
values[j] = cont
return values[0]
for steps in (1, 2, 10, 100, 1000):
print(f"{steps:>5} steps: call = {crr_price(100, 100, 1, 0.05, 0.2, steps):.4f}")
print("Black-Scholes: call = 10.4506")
print(f"European put = {crr_price(100, 100, 1, 0.05, 0.2, 1000, 'put'):.4f}")
print(f"American put = {crr_price(100, 100, 1, 0.05, 0.2, 1000, 'put', american=True):.4f}")1 steps: call = 12.1623
2 steps: call = 9.5405
10 steps: call = 10.2534
100 steps: call = 10.4306
1000 steps: call = 10.4486
Black-Scholes: call = 10.4506
European put = 5.5715
American put = 6.0896American options and early exercise
With 1,000 steps the European put is worth 5.5715 and the American put 6.0896. The difference, about 0.52, is the value of being allowed to exercise early: when the stock falls far enough, taking the strike now and earning interest on it beats waiting. For a call on a stock with no dividends, early exercise is never optimal, so American and European calls are worth the same.
The tree also checks put-call parity: the European prices from the same tree satisfy it, and the American ones only satisfy its inequality version.
Learn this on your phone, one short lesson at a time
QF-Learn turns topics like this into five-minute lessons with quizzes, finance games and certificates. It is free, and it is coming to Android and iOS. Leave your email and you will be first to get it.