Python

Binomial Valuation of American Options with CRR

# Binomial Valuation
#
# Valuation of American Options
# with the Cox-Ross-Rubinstein Model
# Primal Algorithm
# Case 1: American Put Option (APO)
# Case 2: Short Condor Spread (SCS)
#
import math
import numpy as np

# General Parameters and Option Values
def set_parameters(otype, M):
    ''' Sets parameters depending on valuation case.
    Parameters
    ==========
    otype: int
    option type
    1 = American put option
    2 = Short Condor Spread
    '''
    if otype == 1:
        # Parameters -- American Put Option
        S0 = 36. # initial stock level
        T = 1.0 # time-to-maturity
        r = 0.06 # short rate
        sigma = 0.2 # volatility
    elif otype == 2:
        # Parameters -- Short Condor Spread
        S0 = 100. # initial stock level
        T = 1.0 # time-to-maturity
        r = 0.05 # short rate
        sigma = 0.5 # volatility
    else:
        raise ValueError('Option type not known.')
    # Numerical Parameters
    dt = T / M # time interval
    df = math.exp(-r * dt) # discount factor
    u = math.exp(sigma * math.sqrt(dt)) # up-movement
    d = 1 / u # down-movement
    q = (math.exp(r * dt) - d) / (u - d) # martingale probability
    return S0, T, r, sigma, M, dt, df, u, d, q

def inner_value(S, otype):
    ''' Inner value functions for American put option and short condor spread
    option with American exercise.
    Parameters
    ==========
    otype: int
    option type
    1 = American put option
    2 = Short Condor Spread
    '''
    if otype == 1:
        return np.maximum(40. - S, 0)
    elif otype == 2:
        return np.minimum(40., np.maximum(90. - S, 0)
                          + np.maximum(S - 110., 0))
    else:
        raise ValueError('Option type not known.')
    
def CRR_option_valuation(otype, M=500):
    S0, T, r, sigma, M, dt, df, u, d, q = set_parameters(otype, M)
    # Array Generation for Stock Prices
    mu = np.arange(M + 1)
    mu = np.resize(mu, (M + 1, M + 1))
    md = np.transpose(mu)
    mu = u ** (mu - md)
    md = d ** md
    S = S0 * mu * md
    # Valuation by Backwards Induction
    h = inner_value(S, otype) # innver value matrix
    V = inner_value(S, otype) # value matrix
    C = np.zeros((M + 1, M + 1), dtype=np.float) # continuation values
    ex = np.zeros((M + 1, M + 1), dtype=np.float) # exercise matrix
    z = 0
    for i in range(M - 1, -1, -1):
        C[0:M - z, i] = (q * V[0:M - z, i + 1]
        + (1 - q) * V[1:M - z + 1, i + 1]) * df
        V[0:M - z, i] = np.where(h[0:M - z, i] > C[0:M - z, i],
        h[0:M - z, i], C[0:M - z, i])
        ex[0:M - z, i] = np.where(h[0:M - z, i] > C[0:M - z, i], 1, 0)
        z += 1
    return V[0, 0]