@@ -45,7 +45,8 @@ We will use the following imports.
4545``` {code-cell} ipython3
4646import numpy as np
4747import matplotlib.pyplot as plt
48- from numpy.random import randn
48+
49+ rng = np.random.default_rng()
4950```
5051
5152
168169
169170S = 0.0
170171for i in range(n):
171- X_1 = np.exp(μ_1 + σ_1 * randn ())
172- X_2 = np.exp(μ_2 + σ_2 * randn ())
173- X_3 = np.exp(μ_3 + σ_3 * randn ())
172+ X_1 = np.exp(μ_1 + σ_1 * rng.standard_normal ())
173+ X_2 = np.exp(μ_2 + σ_2 * rng.standard_normal ())
174+ X_3 = np.exp(μ_3 + σ_3 * rng.standard_normal ())
174175 S += (X_1 + X_2 + X_3)**p
175176S / n
176177```
@@ -180,12 +181,12 @@ S / n
180181We can also construct a function that contains these operations:
181182
182183``` {code-cell} ipython3
183- def compute_mean(n=1_000_000):
184+ def compute_mean(n=1_000_000, rng=rng ):
184185 S = 0.0
185186 for i in range(n):
186- X_1 = np.exp(μ_1 + σ_1 * randn ())
187- X_2 = np.exp(μ_2 + σ_2 * randn ())
188- X_3 = np.exp(μ_3 + σ_3 * randn ())
187+ X_1 = np.exp(μ_1 + σ_1 * rng.standard_normal ())
188+ X_2 = np.exp(μ_2 + σ_2 * rng.standard_normal ())
189+ X_3 = np.exp(μ_3 + σ_3 * rng.standard_normal ())
189190 S += (X_1 + X_2 + X_3)**p
190191 return (S / n)
191192```
@@ -195,7 +196,7 @@ def compute_mean(n=1_000_000):
195196Now let's call it.
196197
197198``` {code-cell} ipython3
198- compute_mean()
199+ compute_mean(rng=rng )
199200```
200201
201202
@@ -209,18 +210,18 @@ But the code above runs quite slowly.
209210To make it faster, let's implement a vectorized routine using NumPy.
210211
211212``` {code-cell} ipython3
212- def compute_mean_vectorized(n=1_000_000):
213- X_1 = np.exp(μ_1 + σ_1 * randn (n))
214- X_2 = np.exp(μ_2 + σ_2 * randn (n))
215- X_3 = np.exp(μ_3 + σ_3 * randn (n))
213+ def compute_mean_vectorized(n=1_000_000, rng=rng ):
214+ X_1 = np.exp(μ_1 + σ_1 * rng.standard_normal (n))
215+ X_2 = np.exp(μ_2 + σ_2 * rng.standard_normal (n))
216+ X_3 = np.exp(μ_3 + σ_3 * rng.standard_normal (n))
216217 S = (X_1 + X_2 + X_3)**p
217218 return S.mean()
218219```
219220
220221``` {code-cell} ipython3
221222%%time
222223
223- compute_mean_vectorized()
224+ compute_mean_vectorized(rng=rng )
224225```
225226
226227
@@ -232,7 +233,7 @@ We can increase $n$ to get more accuracy and still have reasonable speed:
232233``` {code-cell} ipython3
233234%%time
234235
235- compute_mean_vectorized(n=10_000_000)
236+ compute_mean_vectorized(n=10_000_000, rng=rng )
236237```
237238
238239
@@ -399,7 +400,7 @@ M = 10_000_000
399400Here is our code
400401
401402``` {code-cell} ipython3
402- S = np.exp(μ + σ * np.random.randn (M))
403+ S = np.exp(μ + σ * rng.standard_normal (M))
403404return_draws = np.maximum(S - K, 0)
404405P = β**n * np.mean(return_draws)
405406print(f"The Monte Carlo option price is approximately {P:3f}")
@@ -514,14 +515,20 @@ $$ s_{t+1} = s_t + \mu + \exp(h_t) \xi_{t+1} $$
514515Here is a function to simulate a path using this equation:
515516
516517``` {code-cell} ipython3
517- def simulate_asset_price_path(μ=default_μ, S0=default_S0, h0=default_h0, n=default_n, ρ=default_ρ, ν=default_ν):
518+ def simulate_asset_price_path(μ=default_μ,
519+ S0=default_S0,
520+ h0=default_h0,
521+ n=default_n,
522+ ρ=default_ρ,
523+ ν=default_ν,
524+ rng=rng):
518525 s = np.empty(n+1)
519526 s[0] = np.log(S0)
520527
521528 h = h0
522529 for t in range(n):
523- s[t+1] = s[t] + μ + np.exp(h) * randn ()
524- h = ρ * h + ν * randn ()
530+ s[t+1] = s[t] + μ + np.exp(h) * rng.standard_normal ()
531+ h = ρ * h + ν * rng.standard_normal ()
525532
526533 return np.exp(s)
527534```
@@ -537,7 +544,7 @@ titles = 'log paths', 'paths'
537544transforms = np.log, lambda x: x
538545for ax, transform, title in zip(axes, transforms, titles):
539546 for i in range(50):
540- path = simulate_asset_price_path()
547+ path = simulate_asset_price_path(rng=rng )
541548 ax.plot(transform(path))
542549 ax.set_title(title)
543550
@@ -575,16 +582,17 @@ def compute_call_price(β=default_β,
575582 n=default_n,
576583 ρ=default_ρ,
577584 ν=default_ν,
578- M=10_000):
585+ M=10_000,
586+ rng=rng):
579587 current_sum = 0.0
580588 # For each sample path
581589 for m in range(M):
582590 s = np.log(S0)
583591 h = h0
584592 # Simulate forward in time
585593 for t in range(n):
586- s = s + μ + np.exp(h) * randn ()
587- h = ρ * h + ν * randn ()
594+ s = s + μ + np.exp(h) * rng.standard_normal ()
595+ h = ρ * h + ν * rng.standard_normal ()
588596 # And add the value max{S_n - K, 0} to current_sum
589597 current_sum += np.maximum(np.exp(s) - K, 0)
590598
@@ -593,7 +601,7 @@ def compute_call_price(β=default_β,
593601
594602``` {code-cell} ipython3
595603%%time
596- compute_call_price()
604+ compute_call_price(rng=rng )
597605```
598606
599607
@@ -624,12 +632,12 @@ def compute_call_price_vector(β=default_β,
624632 n=default_n,
625633 ρ=default_ρ,
626634 ν=default_ν,
627- M=10_000):
628-
635+ M=10_000,
636+ rng=rng):
629637 s = np.full(M, np.log(S0))
630638 h = np.full(M, h0)
631639 for t in range(n):
632- Z = np.random.randn( 2, M)
640+ Z = rng.standard_normal(( 2, M) )
633641 s = s + μ + np.exp(h) * Z[0, :]
634642 h = ρ * h + ν * Z[1, :]
635643 expectation = np.mean(np.maximum(np.exp(s) - K, 0))
@@ -639,7 +647,7 @@ def compute_call_price_vector(β=default_β,
639647
640648``` {code-cell} ipython3
641649%%time
642- compute_call_price_vector()
650+ compute_call_price_vector(rng=rng )
643651```
644652
645653
@@ -650,7 +658,7 @@ Now let's try with larger $M$ to get a more accurate calculation.
650658
651659``` {code-cell} ipython3
652660%%time
653- compute_call_price(M=10_000_000)
661+ compute_call_price(M=10_000_000, rng=rng )
654662```
655663
656664
@@ -696,7 +704,8 @@ def compute_call_price_with_barrier(β=default_β,
696704 ρ=default_ρ,
697705 ν=default_ν,
698706 bp=default_bp,
699- M=50_000):
707+ M=50_000,
708+ rng=rng):
700709 current_sum = 0.0
701710 # For each sample path
702711 for m in range(M):
@@ -706,8 +715,8 @@ def compute_call_price_with_barrier(β=default_β,
706715 option_is_null = False
707716 # Simulate forward in time
708717 for t in range(n):
709- s = s + μ + np.exp(h) * randn ()
710- h = ρ * h + ν * randn ()
718+ s = s + μ + np.exp(h) * rng.standard_normal ()
719+ h = ρ * h + ν * rng.standard_normal ()
711720 if np.exp(s) > bp:
712721 payoff = 0
713722 option_is_null = True
@@ -722,7 +731,7 @@ def compute_call_price_with_barrier(β=default_β,
722731```
723732
724733``` {code-cell} ipython3
725- %time compute_call_price_with_barrier()
734+ %time compute_call_price_with_barrier(rng=rng )
726735```
727736
728737
@@ -739,12 +748,13 @@ def compute_call_price_with_barrier_vector(β=default_β,
739748 ρ=default_ρ,
740749 ν=default_ν,
741750 bp=default_bp,
742- M=50_000):
751+ M=50_000,
752+ rng=rng):
743753 s = np.full(M, np.log(S0))
744754 h = np.full(M, h0)
745755 option_is_null = np.full(M, False)
746756 for t in range(n):
747- Z = np.random.randn( 2, M)
757+ Z = rng.standard_normal(( 2, M) )
748758 s = s + μ + np.exp(h) * Z[0, :]
749759 h = ρ * h + ν * Z[1, :]
750760 # Mark all the options null where S_n > barrier price
@@ -757,7 +767,7 @@ def compute_call_price_with_barrier_vector(β=default_β,
757767```
758768
759769``` {code-cell} ipython3
760- %time compute_call_price_with_barrier_vector()
770+ %time compute_call_price_with_barrier_vector(rng=rng )
761771```
762772
763773``` {solution-end}
0 commit comments