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')