Barrier Options with Binomial Trees

Barrier Options with the Binomial Asset Pricing Model 
Implementation of a simple slow and fast barrier tree pricing model in python. We will treat binomial tree as a network with nodes (i,j) with i representing the time steps and j representing the number of ordered price outcome, lowest – or bottom of tree – to highest. 
– barrier_tree_slow
– barrier_tree_fast
We will be using a generic timing wrapper function to judge the time performance of using numpy arrays over for loops to iterate over j nodes in each time step i. Please note if you do not want to use the timing wrapper, please still import numpy!

import numpy as np

from functools import wraps
from time import time

def timing(f):
    @wraps(f)
    def wrap(*args, **kw):
        ts = time()
        result = f(*args, **kw)
        te = time()
        print('func:%r args:[%r, %r] took: %2.4f sec' % \
          (f.__name__, args, kw, te-ts))
        return result
    return wrap

Binomial Tree Representation

Stock tree can be represented using nodes \((i,j)\) and intial stock price \(S0\)
\(S_{i,j} = S_0u^{j}d^{i-j}\)
\(C_{i,j}\) represents contract price at each node \((i,j)\). Where \(C_{N,j} \) represents final payoff function that we can define.

Barrier Option Characteristics

For an up-and-out barrier put option: if \(T = t_N\) then at the terminal nodes, \(C^{j}_N = (K-S^{j}_N)^{+}Ind{(S^{j}_N < H)}\)

For all other parts of the tree at nodes \((i,j)\):
– \(t_n \in T\) and \(S^{j}_i \geq H\) then \(C^{j}_i = 0 \)
– \(t_n \notin T\) or \(S^{j}_i < H\) then  \(C^{j}_i = \exp^{-r\Delta T}[q^j_i C^{j+1}_{i+1} + (1-q^{j}_i)C^{j-1}_{i+1}]\)

# Initialise parameters
S0 = 100      # initial stock price
K = 100       # strike price
T = 1         # time to maturity in years
H = 125       # up-and-out barrier price/value
r = 0.06      # annual risk-free rate
N = 3         # number of time steps
u = 1.1       # up-factor in binomial models
d = 1/u       # ensure recombining tree
opttype = 'C' # Option Type 'C' or 'P'

Barrier Tree Slow

Here we will use for loops to iterate through nodes j at each time step i.

@timing
def barrier_tree_slow(K,T,S0,H,r,N,u,d,opttype='C'):
    #precompute values
    dt = T/N
    q = (np.exp(r*dt) - d)/(u-d)
    disc = np.exp(-r*dt)
    
    # initialise asset prices at maturity
    S = np.zeros(N+1)
    for j in range(0,N+1):
        S[j] = S0 * u**j * d**(N-j)
        
    # option payoff
    C = np.zeros(N+1)
    for j in range(0,N+1):
        if opttype == 'C':
            C[j] = max(0, S[j] - K)
        else:
            C[j] = max(0, K - S[j])
            
    # check terminal condition payoff
    for j in range(0, N+1):
        S = S0 * u**j * d**(N-j)
        if S >= H:
            C[j] = 0
            
    # backward recursion through the tree
    for i in np.arange(N-1,-1,-1):
        for j in range(0,i+1):
            S = S0 * u**j * d**(i-j)
            if S >= H:
                C[j] = 0
            else:
                C[j] = disc * (q*C[j+1]+(1-q)*C[j])
    return C[0]

barrier_tree_slow(K,T,S0,H,r,N,u,d,opttype='C')

Barrier Tree Fast

Now we will vectorise out code using numpy arrays instead of for loops through j nodes.

@timing
def barrier_tree_fast(K,T,S0,H,r,N,u,d,opttype='C'):
    #precompute values
    dt = T/N
    q = (np.exp(r*dt) - d)/(u-d)
    disc = np.exp(-r*dt)
    
    # initialise asset prices at maturity
    S = S0 * d**(np.arange(N,-1,-1)) * u**(np.arange(0,N+1,1))
        
    # option payoff
    if opttype == 'C':
        C = np.maximum( S - K, 0 )
    else:
        C = np.maximum( K - S, 0 )
            
    # check terminal condition payoff
    C[S >= H] = 0
            
    # backward recursion through the tree
    for i in np.arange(N-1,-1,-1):
        S = S0 * d**(np.arange(i,-1,-1)) * u**(np.arange(0,i+1,1))
        C[:i+1] = disc * ( q * C[1:i+2] + (1-q) * C[0:i+1] )
        C = C[:-1]
        C[S >= H] = 0
    return C[0]

barrier_tree_fast(K,T,S0,H,r,N,u,d,opttype='C')

Barrier Options with Binomial Tree Slow vs Fast

Now we will compare runtimes for slow vs fast. Ignore option price changes as this is impacted with changing the time steps and keeping the u and d factors the same.

for N in [3,50, 100, 1000, 5000]:
    barrier_tree_slow(K,T,S0,H,r,N,u,d,opttype='C')
    barrier_tree_fast(K,T,S0,H,r,N,u,d,opttype='C')