Skip to content

Commit 34a37be

Browse files
Chihiro2000GitHubclaudeHumphreyYangjstac
authored
[monte_carlo.md] Update np.random → Generator API (#741)
* Update rng usage in monte_carlo.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Pass rng as explicit argument in monte_carlo.md functions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [monte_carlo.md] Remove rng=None guard; use global rng as default Per reviewer feedback, remove repetitive `if rng is None` checks and change `rng=None` to `rng=rng` across all 7 functions, following the convention established in lecture-python.myst PR #874. Also reformat `simulate_asset_price_path` signature to one-arg-per-line style (consistent with other multi-argument functions in the file) to fix the 80-character line length violation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Humphrey Yang <39026988+HumphreyYang@users.noreply.github.com> Co-authored-by: John Stachurski <john.stachurski@gmail.com>
1 parent 21b4fdc commit 34a37be

1 file changed

Lines changed: 46 additions & 36 deletions

File tree

‎lectures/monte_carlo.md‎

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ We will use the following imports.
4545
```{code-cell} ipython3
4646
import numpy as np
4747
import matplotlib.pyplot as plt
48-
from numpy.random import randn
48+
49+
rng = np.random.default_rng()
4950
```
5051

5152

@@ -168,9 +169,9 @@ $$
168169
169170
S = 0.0
170171
for 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
175176
S / n
176177
```
@@ -180,12 +181,12 @@ S / n
180181
We 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):
195196
Now 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.
209210
To 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
399400
Here is our code
400401

401402
```{code-cell} ipython3
402-
S = np.exp(μ + σ * np.random.randn(M))
403+
S = np.exp(μ + σ * rng.standard_normal(M))
403404
return_draws = np.maximum(S - K, 0)
404405
P = β**n * np.mean(return_draws)
405406
print(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} $$
514515
Here 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'
537544
transforms = np.log, lambda x: x
538545
for 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

Comments
 (0)