In [ 23 ]: 2 * v
Out[23]: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
Naive scalar multiplication does not return the scalar product. It rather returns, in this case,
two times the object (vector). With NumPy the result is, however, as desired:
In [ 24 ]: import numpy as np
v = np.arange( 1 , 6 )
v
Out[24]: array([1, 2, 3, 4, 5])
In [ 25 ]: 2 * v
Out[25]: array([ 2, 4, 6, 8, 10])
This approach can be beneficially applied to the Monte Carlo algorithm. Example 3-3
provides the respective code, this time making use of NumPy’s vectorization capabilities.
Example 3-3. Monte Carlo valuation of European call option with NumPy (first version)
Monte Carlo valuation of European call options with NumPy
mcs_vector_numpy.py
import math
import numpy as np
from time import time
np.random.seed( 20000 )
t0 = time()
Parameters
S0 = 100.; K = 105.; T = 1.0; r = 0.05; sigma = 0.2
M = 50 ; dt = T / M; I = 250000
Simulating I paths with M time steps
S = np.zeros((M + 1 , I))
S[ 0 ] = S0
for t in range( 1 , M + 1 ):
z = np.random.standard_normal(I) # pseudorandom numbers
S[t] = S[t - 1 ] np.exp((r - 0.5 sigma * 2 ) dt
- sigma math.sqrt(dt) z)
vectorized operation per time step over all paths
Calculating the Monte Carlo estimator
C0 = math.exp(-r T) np.sum(np.maximum(S[- 1 ] - K, 0 )) / I
Results output
tnp1 = time() - t0
print “European Option Value %7.3f” % C0
print “Duration in Seconds %7.3f” % tnp1
Let us run this script:
In [ 26 ]: %run mcs_vector_numpy.py
Out[26]: European Option Value 8.037
Duration in Seconds 1.215
In [ 27 ]: round(tpy / tnp1, 2 )
Out[27]: 28.2
Vectorization brings a speedup of more than 30 times in comparison to pure Python. The
estimated Monte Carlo value is again quite close to the benchmark value.
The vectorization becomes obvious when the pseudorandom numbers are generated. In the
line in question, 250,000 numbers are generated in a single step, i.e., a single line of code:
z = np.random.standard_normal(I)