@@ -12,6 +12,7 @@ kernelspec:
1212---
1313
1414
15+
1516# Monte Carlo and Option Pricing
1617
1718## Overview
@@ -48,6 +49,7 @@ from numpy.random import randn
4849```
4950
5051
52+
5153## An Introduction to Monte Carlo
5254
5355In this section we describe how Monte Carlo can be used to compute
@@ -151,6 +153,7 @@ p = 0.5
151153```
152154
153155
156+
154157#### A Routine using Loops in Python
155158
156159
@@ -174,6 +177,7 @@ S / n
174177```
175178
176179
180+
177181We can also construct a function that contains these operations:
178182
179183``` {code-cell} ipython3
@@ -188,13 +192,15 @@ def compute_mean(n=1_000_000):
188192```
189193
190194
195+
191196Now let's call it.
192197
193198``` {code-cell} ipython3
194199compute_mean()
195200```
196201
197202
203+
198204### A Vectorized Routine
199205
200206If we want a more accurate estimate we should increase $n$.
@@ -219,6 +225,7 @@ compute_mean_vectorized()
219225```
220226
221227
228+
222229Notice that this routine is much faster.
223230
224231We can increase $n$ to get more accuracy and still have reasonable speed:
@@ -230,6 +237,7 @@ compute_mean_vectorized(n=10_000_000)
230237```
231238
232239
240+
233241## Pricing a European Call Option under Risk Neutrality
234242
235243Next we are going to price a European call option under risk neutrality.
276284$$
277285
278286
287+
279288### A Comment on Risk
280289
281290As 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
297306and financial market participants try to calculate every day.
298307
299308
309+
300310### Discounting
301311
302312Another thing we ignored in the previous discussion was time.
326336$$
327337
328338
339+
329340### European Call Options
330341
331342Now let's price a European call option.
@@ -377,13 +388,15 @@ n = 10
377388```
378389
379390
391+
380392We set the simulation size to
381393
382394``` {code-cell} ipython3
383395M = 10_000_000
384396```
385397
386398
399+
387400Here is our code
388401
389402``` {code-cell} ipython3
@@ -394,6 +407,7 @@ print(f"The Monte Carlo option price is approximately {P:3f}")
394407```
395408
396409
410+
397411## Pricing Via a Dynamic Model
398412
399413In this exercise we investigate a more realistic model for the share price $S_n$.
465479Here $\{ \eta_t\} $ is also IID and standard normal.
466480
467481
482+
468483### Default Parameters
469484
470485For the dynamic model, we adopt the following parameter values.
@@ -478,6 +493,7 @@ h0 = 0
478493```
479494
480495
496+
481497(Here ` S0 ` is $S_0$ and ` h0 ` is $h_0$.)
482498
483499For the option we use the following defaults.
@@ -489,6 +505,7 @@ n = 10
489505```
490506
491507
508+
492509### Visualizations
493510
494511With $s_t := \ln S_t$, the price dynamics become
@@ -511,6 +528,7 @@ def simulate_asset_price_path(μ=μ, S0=S0, h0=h0, n=n, ρ=ρ, ν=ν):
511528```
512529
513530
531+
514532Here we plot the paths and the log of the paths.
515533
516534``` {code-cell} ipython3
@@ -529,6 +547,7 @@ plt.show()
529547```
530548
531549
550+
532551### Computing the Price
533552
534553Now that our model is more complicated, we cannot easily determine the
@@ -579,6 +598,7 @@ compute_call_price()
579598```
580599
581600
601+
582602## Exercises
583603
584604``` {exercise}
@@ -624,6 +644,7 @@ compute_call_price()
624644```
625645
626646
647+
627648Notice that this version is faster than the one using a Python loop.
628649
629650Now let's try with larger $M$ to get a more accurate calculation.
@@ -634,6 +655,7 @@ compute_call_price(M=10_000_000)
634655```
635656
636657
658+
637659``` {solution-end}
638660```
639661
@@ -675,30 +697,68 @@ def compute_call_price_with_barrier(β=β,
675697 ρ=ρ,
676698 ν=ν,
677699 bp=bp,
678- M=10_000 ):
700+ M=50_000 ):
679701 current_sum = 0.0
680702 # For each sample path
681703 for m in range(M):
682704 s = np.log(S0)
683705 h = h0
684706 payoff = 0
707+ option_is_null = False
685708 # Simulate forward in time
686709 for t in range(n):
687710 s = s + μ + np.exp(h) * randn()
688711 h = ρ * h + ν * randn()
689712 if np.exp(s) > bp:
690713 payoff = 0
714+ option_is_null = True
691715 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)
694719 # And add the payoff to current_sum
695720 current_sum += payoff
696721
697722 return β**n * current_sum / M
698723```
699724
700725``` {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()
702762```
703763
704764``` {solution-end}
0 commit comments