This IPython Notebook illustrates how to implement a parallel valuation of American options by Monte Carlo simulation. The algorithm used is the Least-Squares Monte Carlo algorithm as proposed in Longstaff-Schwartz (2001): "Valuing American Options by Simulation: A Simple Least-Squares Approach." Review of Financial Studies, Vol. 14, 113-147. We observe speed-ups that are almost linear in the number of cores.
The following is an implementation of the Least-Squares Monte Carlo algorithm (LSM) of Longstaff-Schwartz (2001).
def optionValue(S0, vol, T, K=40, M=50, I=4096, r=0.06):
import numpy as np
np.random.seed(150000) # fix the seed for every valuation
dt = T / M # time interval
df = np.exp(-r * dt) # discount factor per time time interval
# Simulation of Index Levels
S = np.zeros((M + 1, I), 'd') # stock price matrix
S[0, :] = S0 # intial values for stock price
for t in range(1, M + 1):
ran = np.random.standard_normal(I / 2)
ran = np.concatenate((ran, -ran)) # antithetic variates
ran = ran - np.mean(ran) # correct first moment
ran = ran / np.std(ran) # correct second moment
S[t, :] = S[t - 1, :] * np.exp((r - vol ** 2 / 2) * dt
+ vol * ran * np.sqrt(dt))
h = np.maximum(K - S, 0) # inner values for put option
V = np.zeros_like(h) # value matrix
V[-1] = h[-1]
# Valuation by LSM
for t in range(M - 1, 0, -1):
rg = np.polyfit(S[t, :], V[t + 1, :] * df, 5) # regression
C = np.polyval(rg, S[t, :]) # evaluation of regression
V[t, :] = np.where(h[t, :] > C, h[t, :],
V[t + 1, :] * df) # exercise decision/optimization
V0 = np.sum(V[1, :] * df) / I # LSM estimator
print "S0 %4.1f|vol %4.2f|T %2.1f| Option Value %8.3f" % (S0, vol, T, V0)
return V0
We want to replicate the whole table 1 of the seminal paper.
def seqValue():
optionValues = []
for S0 in (36., 38., 40., 42., 44.): # initial stock price values
for vol in (0.2, 0.4): # volatility values
for T in (1.0, 2.0): # times-to-maturity
optionValues.append(optionValue(S0, vol, T))
return optionValues
Now, we measure the time for the 20 different American put options of that table 1 with sequential execution.
from time import time
t0 = time()
optionValues = seqValue() # calculate all values
t1 = time(); d1 = t1 - t0
print "Duration in Seconds %6.3f" % d1
S0 36.0|vol 0.20|T 1.0| Option Value 4.500 S0 36.0|vol 0.20|T 2.0| Option Value 4.854 S0 36.0|vol 0.40|T 1.0| Option Value 7.112 S0 36.0|vol 0.40|T 2.0| Option Value 8.472 S0 38.0|vol 0.20|T 1.0| Option Value 3.262 S0 38.0|vol 0.20|T 2.0| Option Value 3.723 S0 38.0|vol 0.40|T 1.0| Option Value 6.112 S0 38.0|vol 0.40|T 2.0| Option Value 7.682 S0 40.0|vol 0.20|T 1.0| Option Value 2.305 S0 40.0|vol 0.20|T 2.0| Option Value 2.861 S0 40.0|vol 0.40|T 1.0| Option Value 5.258 S0 40.0|vol 0.40|T 2.0| Option Value 6.899 S0 42.0|vol 0.20|T 1.0| Option Value 1.569 S0 42.0|vol 0.20|T 2.0| Option Value 2.164 S0 42.0|vol 0.40|T 1.0| Option Value 4.544 S0 42.0|vol 0.40|T 2.0| Option Value 6.171 S0 44.0|vol 0.20|T 1.0| Option Value 1.020 S0 44.0|vol 0.20|T 2.0| Option Value 1.602 S0 44.0|vol 0.40|T 1.0| Option Value 3.912 S0 44.0|vol 0.40|T 2.0| Option Value 5.591 Duration in Seconds 2.599
First, start a local cluster, if you have multiple cores in your machine.
# in the shell ...
####
# ipcluster start -n 4
####
# or maybe more cores
Enable parallel computing capabilities.
from IPython.parallel import Client
cluster_profile = "default"
# the profile is set to the default one
# see IPython docs for further details
c = Client(profile=cluster_profile)
view = c.load_balanced_view()
Again, a loop for the 20 options of table 1. This time asynchronously distributed to the 4 cores.
def parValue():
optionValues = []
for S in (36., 38., 40., 42., 44.):
for vol in (0.2, 0.4):
for T in (1.0, 2.0):
value = view.apply_async(optionValue, S, vol, T)
# asynchronously calculate the option values
optionValues.append(value)
return optionValues
Now, we measure the time needed with parallel execution.
def execution():
optionValues = parValue() # calculate all values
print "Submitted tasks %d" % len(optionValues)
c.wait(optionValues)
# wait for all tasks to be finished
return optionValues
t0 = time()
optionValues = execution()
t1 = time(); d2 = t1 - t0
print "Duration in Seconds %6.3f" % d2
Submitted tasks 20 Duration in Seconds 0.756
d1 / d2 # speed-up of parallel execution
3.436817046801849
Single values for the American put options can be directly accessed.
optionValues[0].result
4.4995294086976036
You can also inspect the metadata and access single data fields in JSON-typical manner.
optionValues[0].metadata
{'after': [], 'completed': datetime.datetime(2013, 4, 27, 8, 33, 35, 269030), 'data': {}, 'engine_id': 0, 'engine_uuid': '714c3d3a-704c-423d-a310-31a2d7ad2382', 'follow': [], 'msg_id': 'f4be8026-d0e6-416d-a1fc-0488f4fbe206', 'outputs': [], 'outputs_ready': True, 'pyerr': None, 'pyin': None, 'pyout': None, 'received': datetime.datetime(2013, 4, 27, 8, 33, 35, 271120), 'started': datetime.datetime(2013, 4, 27, 8, 33, 35, 130691), 'status': 'ok', 'stderr': '', 'stdout': 'S0 36.0|vol 0.20|T 1.0| Option Value 4.500\n', 'submitted': datetime.datetime(2013, 4, 27, 8, 33, 35, 123543)}
The whole output of the 20 valuations.
for result in optionValues:
print result.metadata['stdout'],
S0 36.0|vol 0.20|T 1.0| Option Value 4.500 S0 36.0|vol 0.20|T 2.0| Option Value 4.854 S0 36.0|vol 0.40|T 1.0| Option Value 7.112 S0 36.0|vol 0.40|T 2.0| Option Value 8.472 S0 38.0|vol 0.20|T 1.0| Option Value 3.262 S0 38.0|vol 0.20|T 2.0| Option Value 3.723 S0 38.0|vol 0.40|T 1.0| Option Value 6.112 S0 38.0|vol 0.40|T 2.0| Option Value 7.682 S0 40.0|vol 0.20|T 1.0| Option Value 2.305 S0 40.0|vol 0.20|T 2.0| Option Value 2.861 S0 40.0|vol 0.40|T 1.0| Option Value 5.258 S0 40.0|vol 0.40|T 2.0| Option Value 6.899 S0 42.0|vol 0.20|T 1.0| Option Value 1.569 S0 42.0|vol 0.20|T 2.0| Option Value 2.164 S0 42.0|vol 0.40|T 1.0| Option Value 4.544 S0 42.0|vol 0.40|T 2.0| Option Value 6.171 S0 44.0|vol 0.20|T 1.0| Option Value 1.020 S0 44.0|vol 0.20|T 2.0| Option Value 1.602 S0 44.0|vol 0.40|T 1.0| Option Value 3.912 S0 44.0|vol 0.40|T 2.0| Option Value 5.591
The Python Quants – the company Web site
Derivatives Analytics On Demand – our analytics suite
Derivatives Analytics with Python – our new book
Read an Excerpt and Order the Book
Contact Us