Skip to content

Commit bdc2831

Browse files
jstacHumphreyYangmmcky
authored
Added Option Pricing Exercises (#236)
* Added Option Pricing Exercises * fix default values * update hint admonitions * misc * Update lectures/parallelization.md * Update lectures/scipy.md * Update lectures/scipy.md * Update lectures/scipy.md * Update lectures/parallelization.md * Update lectures/parallelization.md * Update lectures/scipy.md Fixed Typo Co-authored-by: Humphrey Yang <u6474961@anu.edu.au> Co-authored-by: mmcky <mmcky@users.noreply.github.com>
1 parent a545bda commit bdc2831

File tree

2 files changed

+249
-1
lines changed

2 files changed

+249
-1
lines changed

lectures/parallelization.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,3 +486,119 @@ on the number of CPUs on your machine.)
486486

487487
```{solution-end}
488488
```
489+
490+
491+
```{exercise}
492+
:label: parallel_ex2
493+
494+
In {doc}`our lecture on SciPy<scipy>`, we discussed pricing a call option in a
495+
setting where the underlying stock price had a simple and well-known
496+
distribution.
497+
498+
Here we discuss a more realistic setting.
499+
500+
We recall that the price of the option obeys
501+
502+
$$
503+
P = \beta^n \mathbb E \max\{ S_n - K, 0 \}
504+
$$
505+
506+
where
507+
508+
1. $\beta$ is a discount factor,
509+
2. $n$ is the expiry date,
510+
2. $K$ is the strike price and
511+
3. $\{S_t\}$ is the price of the underlying asset at each time $t$.
512+
513+
Suppose that `n, β, K = 20, 0.99, 100`.
514+
515+
Assume that the stock price obeys
516+
517+
$$
518+
\ln \frac{S_{t+1}}{S_t} = \mu + \sigma_t \xi_{t+1}
519+
$$
520+
521+
where
522+
523+
$$
524+
\sigma_t = \exp(h_t),
525+
\quad
526+
h_{t+1} = \rho h_t + \nu \eta_{t+1}
527+
$$
528+
529+
Here $\{\xi_t\}$ and $\{\eta_t\}$ are IID and standard normal.
530+
531+
(This is a **stochastic volatility** model, where the volatility $\sigma_t$
532+
varies over time.)
533+
534+
Use the defaults `μ, ρ, ν, S0, h0 = 0.0001, 0.1, 0.001, 10, 0`.
535+
536+
(Here `S0` is $S_0$ and `h0` is $h_0$.)
537+
538+
By generating $M$ paths $s_0, \ldots, s_n$, compute the Monte Carlo estimate
539+
540+
$$
541+
\hat P_M
542+
:= \beta^n \mathbb E \max\{ S_n - K, 0 \}
543+
\approx
544+
\frac{1}{M} \sum_{m=1}^M \max \{S_n^m - K, 0 \}
545+
$$
546+
547+
548+
of the price, applying Numba and parallelization.
549+
550+
```
551+
552+
553+
```{solution-start} parallel_ex2
554+
:class: dropdown
555+
```
556+
557+
558+
With $s_t := \ln S_t$, the price dynamics become
559+
560+
$$
561+
s_{t+1} = s_t + \mu + \exp(h_t) \xi_{t+1}
562+
$$
563+
564+
Using this fact, the solution can be written as follows.
565+
566+
567+
```{code-cell} ipython3
568+
from numpy.random import randn
569+
M = 10_000_000
570+
571+
n, β, K = 20, 0.99, 100
572+
μ, ρ, ν, S0, h0 = 0.0001, 0.1, 0.001, 10, 0
573+
574+
@njit(parallel=True)
575+
def compute_call_price_parallel(β=β,
576+
μ=μ,
577+
S0=S0,
578+
h0=h0,
579+
K=K,
580+
n=n,
581+
ρ=ρ,
582+
ν=ν,
583+
M=M):
584+
current_sum = 0.0
585+
# For each sample path
586+
for m in prange(M):
587+
s = np.log(S0)
588+
h = h0
589+
# Simulate forward in time
590+
for t in range(n):
591+
s = s + μ + np.exp(h) * randn()
592+
h = ρ * h + ν * randn()
593+
# And add the value max{S_n - K, 0} to current_sum
594+
current_sum += np.maximum(np.exp(s) - K, 0)
595+
596+
return β**n * current_sum / M
597+
```
598+
599+
Try swapping between `parallel=True` and `parallel=False` and noting the run time.
600+
601+
If you are on a machine with many CPUs, the difference should be significant.
602+
603+
```{solution-end}
604+
```

lectures/scipy.md

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,12 +440,144 @@ We leave you to investigate the [set of available routines](http://docs.scipy.or
440440

441441
## Exercises
442442

443+
The first few exercises concern pricing a European call option under the
444+
assumption of risk neutrality. The price satisfies
445+
446+
$$
447+
P = \beta^n \mathbb E \max\{ S_n - K, 0 \}
448+
$$
449+
450+
where
451+
452+
1. $\beta$ is a discount factor,
453+
2. $n$ is the expiry date,
454+
2. $K$ is the strike price and
455+
3. $\{S_t\}$ is the price of the underlying asset at each time $t$.
456+
457+
For example, if the call option is to buy stock in Amazon at strike price $K$, the owner has the right (but not the obligation) to buy 1 share in Amazon at price $K$ after $n$ days.
458+
459+
The payoff is therefore $\max\{S_n - K, 0\}$
460+
461+
The price is the expectation of the payoff, discounted to current value.
462+
463+
464+
```{exercise-start}
465+
:label: sp_ex01
466+
```
467+
468+
Suppose that $S_n$ has the [log-normal](https://en.wikipedia.org/wiki/Log-normal_distribution) distribution with parameters $\mu$ and $\sigma$. Let $f$ denote the density of this distribution. Then
469+
470+
$$
471+
P = \beta^n \int_0^\infty \max\{x - K, 0\} f(x) dx
472+
$$
473+
474+
Plot the function
475+
476+
$$
477+
g(x) = \beta^n \max\{x - K, 0\} f(x)
478+
$$
479+
480+
over the interval $[0, 400]$ when `μ, σ, β, n, K = 4, 0.25, 0.99, 10, 40`.
481+
482+
```{hint}
483+
:class: dropdown
484+
485+
From `scipy.stats` you can import `lognorm` and then use `lognorm(x, σ, scale=np.exp(μ)` to get the density $f$.
486+
```
487+
488+
```{exercise-end}
489+
```
490+
491+
```{solution-start} sp_ex01
492+
:class: dropdown
493+
```
494+
495+
Here's one possible solution
496+
497+
```{code-cell} ipython3
498+
from scipy.integrate import quad
499+
from scipy.stats import lognorm
500+
501+
μ, σ, β, n, K = 4, 0.25, 0.99, 10, 40
502+
503+
def g(x):
504+
return β**n * np.maximum(x - K, 0) * lognorm.pdf(x, σ, scale=np.exp(μ))
505+
506+
x_grid = np.linspace(0, 400, 1000)
507+
y_grid = g(x_grid)
508+
509+
fig, ax = plt.subplots()
510+
ax.plot(x_grid, y_grid, label="$g$")
511+
ax.legend()
512+
plt.show()
513+
```
514+
515+
```{solution-end}
516+
```
517+
518+
```{exercise}
519+
:label: sp_ex02
520+
521+
In order to get the option price, compute the integral of this function numerically using `quad` from `scipy.optimize`.
522+
523+
```
524+
525+
```{solution-start} sp_ex02
526+
:class: dropdown
527+
```
528+
529+
```{code-cell} ipython3
530+
P, error = quad(g, 0, 1_000)
531+
print(f"The numerical integration based option price is {P:.3f}")
532+
```
533+
534+
```{solution-end}
535+
```
536+
537+
```{exercise}
538+
:label: sp_ex03
539+
540+
Try to get a similar result using Monte Carlo to compute the expectation term in the option price, rather than `quad`.
541+
542+
In particular, use the fact that if $S_n^1, \ldots, S_n^M$ are independent
543+
draws from the lognormal distribution specified above, then, by the law of
544+
large numbers,
545+
546+
$$ \mathbb E \max\{ S_n - K, 0 \}
547+
\approx
548+
\frac{1}{M} \sum_{m=1}^M \max \{S_n^m - K, 0 \}
549+
$$
550+
551+
Set `M = 10_000_000`
552+
553+
```
554+
555+
```{solution-start} sp_ex03
556+
:class: dropdown
557+
```
558+
559+
Here is one solution:
560+
561+
```{code-cell} ipython3
562+
M = 10_000_000
563+
S = np.exp(μ + σ * np.random.randn(M))
564+
return_draws = np.maximum(S - K, 0)
565+
P = β**n * np.mean(return_draws)
566+
print(f"The Monte Carlo option price is {P:3f}")
567+
```
568+
569+
570+
```{solution-end}
571+
```
572+
573+
574+
443575
```{exercise}
444576
:label: sp_ex1
445577
446578
In {ref}`this lecture <functions>`, we discussed the concept of {ref}`recursive function calls <recursive_functions>`.
447579
448-
Try to write a recursive implementation of homemade bisection function {ref}`described above <bisect_func>`.
580+
Try to write a recursive implementation of the homemade bisection function {ref}`described above <bisect_func>`.
449581
450582
Test it on the function {eq}`root_f`.
451583
```

0 commit comments

Comments
 (0)