American Binomial Pricer (with Dividends)
Prices an American call on a dividend-paying stock with a Cox–Ross–Rubinstein binomial tree. Dividends are handled by subtracting their present value from the spot at \(t=0\) (the “escrow” model), then re-adding the present value of any future dividends at each node so the early-exercise check uses the with-dividend stock price. Uses decimal.Decimal with 2000 digits of precision so the up/down factors don’t drift when steps get large.
Run it from the command line:
python american.py S K sigma r T dt [d1_amt d1_t] [d2_amt d2_t] ...
import sys
import math
from decimal import Decimal, getcontext
getcontext().prec = 2000
ctx = getcontext()
initialStockPrice = Decimal(sys.argv[1])
strikePrice = Decimal(sys.argv[2])
volatility = Decimal(sys.argv[3])
interestRate = Decimal(sys.argv[4])
totalTime = Decimal(sys.argv[5])
timeStep = Decimal(sys.argv[6])
dividends = []
for i in range(7, len(sys.argv), 2):
dividends.append((Decimal(sys.argv[i]), Decimal(sys.argv[i+1])))
initialStockDiv = initialStockPrice
for dividend in dividends:
initialStockDiv -= dividend[0] * ctx.exp(-interestRate * (dividend[1]))
steps = int((totalTime / timeStep))
sqrt_dt = timeStep.sqrt()
up = ctx.exp(volatility * sqrt_dt)
down = ctx.exp(-volatility * sqrt_dt)
p = (ctx.exp(interestRate * timeStep) - down) / (up - down)
one = Decimal(1)
numFinalNodes = steps + 1
topFinalNode = initialStockDiv * (up ** steps)
currPrice = []
prevPrice = []
currNodes = []
prevNodes = []
for j in range(numFinalNodes, 0, -1):
if j == numFinalNodes:
for i in range(j):
if i == 0:
nodeBelow = topFinalNode
else:
nodeBelow = down * (currNode / up)
prevNodes.append(max(nodeBelow - strikePrice, Decimal(0)))
prevPrice.append(nodeBelow)
currNode = nodeBelow
continue
for i in range(j):
eurOptionValue = ctx.exp(-interestRate * timeStep) * (
p * prevNodes[i] + (one - p) * prevNodes[i + 1]
)
currStockPrice = prevPrice[i] / up
currPrice.append(currStockPrice)
currTime = (j - 1) * timeStep
for dividend in dividends:
if currTime < dividend[1]:
currStockPrice = currStockPrice + dividend[0] * ctx.exp(
-interestRate * (dividend[1] - currTime)
)
currOption = currStockPrice - strikePrice
currNodes.append(max(eurOptionValue, currOption, 0))
prevNodes = currNodes
prevPrice = currPrice
currNodes = []
currPrice = []
rounded = prevNodes[0].quantize(Decimal("0.000000000000001"))
print(rounded)