The Binomial Model

Two-State option Valuation

import numpy as np
import pandas as pd
from scipy.special import comb
import math

Parameters. share price is $50 and will be either $40 or $60 one year from now. Call option on the share has an exercise price of $50. Exercise a year from now. Borrowing rate is 10%.

What is the value of the call?

We need to equal alternatives, to compare and contrast

  1. Buy the option

  2. Borrow \$18.18, which will lead us to pay \$20 a year from now.

Parameters:

PU = 60
PD = 40
P = 50
U = 0.5
D = 0.5
r = 1.1
B = 20
Payoff at $60 Payoff at $40
Buy the call 60-50=10 0
Replicate:
Buy half the stock 30 20
Borrow $18.18 at 10% -20 -20
Net 10 0

What are the cost of the replication strategy?

cost = 0.5*P - B/r
print(f'Cost: ${cost:5.2f}\n')
Cost: $ 6.82

Determining Delta

Why buy half of the share?

Answer: the price of call price if eiter $0 or $10 while the share prices will be either $40 of $60:

Swing_call = 0.5 * PU - B - 0
Swing_stock = PU - PD
Delta = Swing_call/Swing_stock

print(f'Swing_call: ${Swing_call:5.2f}\n')
print(f'Swing_stock: ${Swing_stock:5.2f}\n')
print(f'Delta: ${Delta:5.2f}\n')
Swing_call: $10.00

Swing_stock: $20.00

Delta: $ 0.50

The replication strategy should give us the same risk for the both strategies. This is achieved by buying half of the share.


Determining the Amount of Borrowing

Busying half of the share gives us either \$30 or \$20 at expiration. Which is exactly \$20 more than the payoffs of \$10 and \$0.

Determining the value of the call option.

Call value = Stock Price x Delta - Borrowed

Call = P * Delta - B/r
print(f'Call value: ${Call:5.2f}\n')
Call value: $ 6.82

State prices

We thank Sagi Haim for developing this script

State prices - Up

# Input:
S0 = 50
X = 50
U = 1.1
D = 0.97
r = 1.06
m = 4
qU = (r - D) / (r * (U - D))
print("qU",qU)
qU 0.6531204644412192

State prices - Down

qD = (U - r) / (r * (U - D))
print("qD",qD)
qD 0.29027576197387517

Risk neutral probabilities - Up

pi_U = qU * r
print("pi_U",pi_U)
pi_U 0.6923076923076924

Risk neutral probabilities - Down

pi_D = qD * r
print("pi_D",pi_D)
pi_D 0.3076923076923077

Calculate payoff at maturity

ex_payoff = np.empty(m + 1)
for i in np.arange(0, m + 1):
    ex_payoff[i] = max(S0 * U ** i * D ** (m - i) - X, 0)
    pass
print(pd.Series(ex_payoff))
0     0.000000
1     0.197015
2     6.924450
3    14.553500
4    23.205000
dtype: float64

Calculate tree probabilities

ex_prob = np.empty(m + 1)
for i in np.arange(0, m + 1):
    ex_prob[i] = pi_U ** i * pi_D ** (m - i) * comb(m, i, exact=False)
    pass
print(pd.Series(ex_prob))
0    0.008963
1    0.080669
2    0.272259
3    0.408389
4    0.229719
dtype: float64
call_price = np.dot(ex_prob, ex_payoff) / r ** m
print("Call Price",call_price)
Call Price 10.436036460331527

American Put

# Input:
S0 = 50
X = 50
U = 1.1
D = 0.97
r = 1.06
m = 2

State prices

qU = (r - D) / (r * (U - D))
qD = (U - r) / (r * (U - D))

Risk neutral probabilities

pi_U = qU * r
pi_D = qD * r

Create a payoff matrix

ex_payoff = np.empty((m + 1, m + 1))
ex_payoff[:] = np.NaN

Calculate Payoff at Exercise

for col in np.arange(0, m + 1):
    for row in np.arange(0, col + 1):
        St = S0 * U ** (row) * D ** (col - row)
        ex_payoff[row, col] = max(X - St, 0)
        pass
    pass

Create a Put value matrix

put_value = np.empty((m + 1, m + 1))
put_value[:] = np.NaN
put_value[:, m] = ex_payoff[:, m]  # At maturity put value = exercise

Calculate Put tree

for col in np.flip(np.arange(0, m)):
    for row in np.arange(0, col + 1):
        ex_value = ex_payoff[row, col]
        pv_down = put_value[row, col + 1] * qD
        pv_up = put_value[row + 1, col + 1] * qU
        pres_value = pv_up + pv_down
        put_value[row, col] = max(ex_value, pres_value)
        pass
    pass
print("Put Value",put_value[0, 0])
Put Value 0.43541364296081275

ESO

S0 = 50  # Current stock price
X = 50  # Option exercise price
t = 10  # Time to option exercise (in years)
vesting = 3  # Vesting period (years)
interest = 0.05  # Annual interest rate
sigma = 0.35  # Riskiness of stock
div_rate = 0.025  # Annual dividend rate on stock
exit_rate = 0.1  # Exit rate
ex_multiple = 3  # Option exercise multiple
n = 50  # Number of subdivisions of one year

Delta t

delta_t = 1/n  # Delta t

Risk neutral probabilities

U = math.exp((interest - 0.5 * sigma ** 2) * delta_t + math.sqrt(delta_t) * sigma)
D = math.exp((interest - 0.5 * sigma ** 2) * delta_t - math.sqrt(delta_t) * sigma)
R = math.exp(interest * delta_t)
div = math.exp(-div_rate / n)
pi_U = (R * div - D) / (U - D)
pi_D = (U - R * div) / (U - D)
# Create a payoff matrix
ex_payoff = np.empty((n * t + 1, n * t + 1))
ex_payoff[:] = np.NaN

Create a Stock Price matrix

St = np.empty((n * t + 1, n * t + 1))
St[:] = np.NaN

Calculate Payoff at Exercise

for col in np.arange(0, n * t + 1):
    for row in np.arange(0, col + 1):
        St[row, col] = S0 * U ** (row) * D ** (col - row)
        ex_payoff[row, col] = max(St[row, col] - X, 0)
        pass
    pass

Create an ESO value matrix

ESO_value = np.empty((n * t + 1, n * t + 1))
ESO_value[:] = np.NaN

At maturity ESO value = exercise

ESO_value[:, n * t] = ex_payoff[:, n * t]

Calculate Payoff at Exercise

for col in np.flip(np.arange(0, n * t)):
    for row in np.arange(0, col + 1):

        pv_down = ESO_value[row, col + 1] * pi_D
        pv_up = ESO_value[row + 1, col + 1] * pi_U
        pres_value = pv_up + pv_down

        if col > vesting * n:  # when passed the vesting period
            if St[row, col] >= ex_multiple * X:  # Case where we cross the multiple execise
                ESO_value[row, col] = ex_payoff[row, col]
                pass
            elif St[row, col] < ex_multiple * X:  # Case where we didn't cross the multiple execise
                ESO_value[row, col] = (1-exit_rate) ** (1/n) * pres_value / \
                    R + (1 - (1 - exit_rate) ** (1 / n)) * ex_payoff[row, col]
                pass
            pass
        elif col <= vesting * n:  # before we passed the vesting period
            ESO_value[row, col] = (1-exit_rate) ** (1/n) * pres_value / R
            pass
        pass
    pass
pass
print("ESO Value", ESO_value[0, 0])
ESO Value 13.29213862828601