@@ -12,6 +12,7 @@ kernelspec:
12
12
---
13
13
14
14
15
+
15
16
# Monte Carlo and Option Pricing
16
17
17
18
## Overview
@@ -48,6 +49,7 @@ from numpy.random import randn
48
49
```
49
50
50
51
52
+
51
53
## An Introduction to Monte Carlo
52
54
53
55
In this section we describe how Monte Carlo can be used to compute
@@ -151,6 +153,7 @@ p = 0.5
151
153
```
152
154
153
155
156
+
154
157
#### A Routine using Loops in Python
155
158
156
159
@@ -174,6 +177,7 @@ S / n
174
177
```
175
178
176
179
180
+
177
181
We can also construct a function that contains these operations:
178
182
179
183
``` {code-cell} ipython3
@@ -188,13 +192,15 @@ def compute_mean(n=1_000_000):
188
192
```
189
193
190
194
195
+
191
196
Now let's call it.
192
197
193
198
``` {code-cell} ipython3
194
199
compute_mean()
195
200
```
196
201
197
202
203
+
198
204
### A Vectorized Routine
199
205
200
206
If we want a more accurate estimate we should increase $n$.
@@ -219,6 +225,7 @@ compute_mean_vectorized()
219
225
```
220
226
221
227
228
+
222
229
Notice that this routine is much faster.
223
230
224
231
We can increase $n$ to get more accuracy and still have reasonable speed:
@@ -230,6 +237,7 @@ compute_mean_vectorized(n=10_000_000)
230
237
```
231
238
232
239
240
+
233
241
## Pricing a European Call Option under Risk Neutrality
234
242
235
243
Next we are going to price a European call option under risk neutrality.
276
284
$$
277
285
278
286
287
+
279
288
### A Comment on Risk
280
289
281
290
As suggested by the name, the risk-neutral price ignores risk.
@@ -297,6 +306,7 @@ Nonetheless, the risk-neutral price is an important benchmark, which economists
297
306
and financial market participants try to calculate every day.
298
307
299
308
309
+
300
310
### Discounting
301
311
302
312
Another thing we ignored in the previous discussion was time.
326
336
$$
327
337
328
338
339
+
329
340
### European Call Options
330
341
331
342
Now let's price a European call option.
@@ -377,13 +388,15 @@ n = 10
377
388
```
378
389
379
390
391
+
380
392
We set the simulation size to
381
393
382
394
``` {code-cell} ipython3
383
395
M = 10_000_000
384
396
```
385
397
386
398
399
+
387
400
Here is our code
388
401
389
402
``` {code-cell} ipython3
@@ -394,6 +407,7 @@ print(f"The Monte Carlo option price is approximately {P:3f}")
394
407
```
395
408
396
409
410
+
397
411
## Pricing Via a Dynamic Model
398
412
399
413
In this exercise we investigate a more realistic model for the share price $S_n$.
465
479
Here $\{ \eta_t\} $ is also IID and standard normal.
466
480
467
481
482
+
468
483
### Default Parameters
469
484
470
485
For the dynamic model, we adopt the following parameter values.
@@ -478,6 +493,7 @@ h0 = 0
478
493
```
479
494
480
495
496
+
481
497
(Here ` S0 ` is $S_0$ and ` h0 ` is $h_0$.)
482
498
483
499
For the option we use the following defaults.
@@ -489,6 +505,7 @@ n = 10
489
505
```
490
506
491
507
508
+
492
509
### Visualizations
493
510
494
511
With $s_t := \ln S_t$, the price dynamics become
@@ -511,6 +528,7 @@ def simulate_asset_price_path(μ=μ, S0=S0, h0=h0, n=n, ρ=ρ, ν=ν):
511
528
```
512
529
513
530
531
+
514
532
Here we plot the paths and the log of the paths.
515
533
516
534
``` {code-cell} ipython3
@@ -529,6 +547,7 @@ plt.show()
529
547
```
530
548
531
549
550
+
532
551
### Computing the Price
533
552
534
553
Now that our model is more complicated, we cannot easily determine the
@@ -579,6 +598,7 @@ compute_call_price()
579
598
```
580
599
581
600
601
+
582
602
## Exercises
583
603
584
604
``` {exercise}
@@ -624,6 +644,7 @@ compute_call_price()
624
644
```
625
645
626
646
647
+
627
648
Notice that this version is faster than the one using a Python loop.
628
649
629
650
Now let's try with larger $M$ to get a more accurate calculation.
@@ -634,6 +655,7 @@ compute_call_price(M=10_000_000)
634
655
```
635
656
636
657
658
+
637
659
``` {solution-end}
638
660
```
639
661
@@ -675,30 +697,68 @@ def compute_call_price_with_barrier(β=β,
675
697
ρ=ρ,
676
698
ν=ν,
677
699
bp=bp,
678
- M=10_000 ):
700
+ M=50_000 ):
679
701
current_sum = 0.0
680
702
# For each sample path
681
703
for m in range(M):
682
704
s = np.log(S0)
683
705
h = h0
684
706
payoff = 0
707
+ option_is_null = False
685
708
# Simulate forward in time
686
709
for t in range(n):
687
710
s = s + μ + np.exp(h) * randn()
688
711
h = ρ * h + ν * randn()
689
712
if np.exp(s) > bp:
690
713
payoff = 0
714
+ option_is_null = True
691
715
break
692
- else:
693
- payoff = np.maximum(np.exp(s) - K, 0)
716
+
717
+ if not option_is_null:
718
+ payoff = np.maximum(np.exp(s) - K, 0)
694
719
# And add the payoff to current_sum
695
720
current_sum += payoff
696
721
697
722
return β**n * current_sum / M
698
723
```
699
724
700
725
``` {code-cell} ipython3
701
- compute_call_price_with_barrier()
726
+ %time compute_call_price_with_barrier()
727
+ ```
728
+
729
+
730
+
731
+ Let's look at the vectorized version which is faster than using Python loops.
732
+
733
+ ``` {code-cell} ipython3
734
+ def compute_call_price_with_barrier_vector(β=β,
735
+ μ=μ,
736
+ S0=S0,
737
+ h0=h0,
738
+ K=K,
739
+ n=n,
740
+ ρ=ρ,
741
+ ν=ν,
742
+ bp=bp,
743
+ M=50_000):
744
+ s = np.full(M, np.log(S0))
745
+ h = np.full(M, h0)
746
+ option_is_null = np.full(M, False)
747
+ for t in range(n):
748
+ Z = np.random.randn(2, M)
749
+ s = s + μ + np.exp(h) * Z[0, :]
750
+ h = ρ * h + ν * Z[1, :]
751
+ # Mark all the options null where S_n > barrier price
752
+ option_is_null = np.where(np.exp(s) > bp, True, option_is_null)
753
+
754
+ # mark payoff as 0 in the indices where options are null
755
+ payoff = np.where(option_is_null, 0, np.maximum(np.exp(s) - K, 0))
756
+ expectation = np.mean(payoff)
757
+ return β**n * expectation
758
+ ```
759
+
760
+ ``` {code-cell} ipython3
761
+ %time compute_call_price_with_barrier_vector()
702
762
```
703
763
704
764
``` {solution-end}
0 commit comments