Exercise · m08_mc.py
Monte Carlo under the risk-neutral measure, finite-difference Greeks, and numerical checks of Ito calculus
not run yet
Not saved: sign up to keep your code"""M08 exercise: Monte Carlo under the risk-neutral measure, finite-difference Greeks,
and numerical checks of Ito calculus (notes §8.5, §8.7, §8.9).
Run: cd exercises && .venv/bin/python -m pytest modules/M08-option-pricing/tests/test_m08_mc.py
Use numpy.random.default_rng(seed) for all randomness so results are reproducible.
bs_price below is the course's reference Black-Scholes (toolkit.options), so this exercise
does not depend on your m08_black_scholes being finished. Once your version passes its
tests you can swap the import for `from m08_black_scholes import bs_price`.
"""
from __future__ import annotations
import math
import numpy as np
from toolkit.options import bs_price # noqa: F401 (provided)
def gbm_terminal(S0: float, T: float, r: float, sigma: float, n_paths: int, seed: int, q: float = 0.0,
antithetic: bool = False) -> np.ndarray:
"""Exact draws of S_T under the risk-neutral measure (notes eq. (8.20)):
S_T = S0 exp((r - q - sigma^2/2) T + sigma sqrt(T) Z), Z ~ N(0,1).
If antithetic, draw n_paths//2 normals z and use the concatenation [z, -z].
"""
raise NotImplementedError
def mc_price(S: float, K: float, T: float, r: float, sigma: float, kind: str = "call", q: float = 0.0,
n_paths: int = 200_000, seed: int = 0) -> tuple[float, float]:
"""Risk-neutral Monte Carlo price e^{-rT} E[payoff(S_T)] using gbm_terminal (no antithetics).
Return (estimate, standard_error) where standard_error = sample sd (ddof=1) of the discounted
payoffs / sqrt(n_paths). The test requires |estimate - bs_price| < 3 * standard_error."""
raise NotImplementedError
def mc_parity_residual(S: float, K: float, T: float, r: float, sigma: float, q: float = 0.0,
n_paths: int = 100_000, seed: int = 0) -> float:
"""Using ONE set of draws S_T, compute MC call C, MC put P and the MC forward value
e^{-rT}(mean(S_T) - K). Return C - P - forward. Explain (notes §8.2) why this is zero to
rounding error path by path, even though each of C and P has Monte Carlo error."""
raise NotImplementedError
def fd_greeks(S: float, K: float, T: float, r: float, sigma: float, kind: str = "call", q: float = 0.0,
h_rel: float = 1e-4) -> dict:
"""Central finite-difference Greeks of bs_price, same keys and units as bs_greeks:
delta and gamma with step h = h_rel*S; vega and rho with step 1e-5; theta = -dV/dT with step 1e-6.
gamma = [V(S+h) - 2V(S) + V(S-h)] / h^2."""
raise NotImplementedError
def quadratic_variation(T: float, n_steps: int, seed: int) -> float:
"""Simulate n_steps Brownian increments dW ~ N(0, T/n_steps) and return sum(dW^2).
It converges to T as n_steps -> infinity (notes Prop. 8.5.1), with sd T sqrt(2/n_steps)."""
raise NotImplementedError
def gbm_log_return_moments(mu: float, sigma: float, T: float, n_steps: int, n_paths: int, seed: int) -> tuple[float, float]:
"""Euler-simulate dS = mu S dt + sigma S dW from S0 = 1 with n_steps steps
(S <- S (1 + mu dt + sigma sqrt(dt) Z)). Return (mean, sample variance with ddof=1) of ln S_T.
Ito's lemma (notes §8.5) predicts mean (mu - sigma^2/2) T and variance sigma^2 T."""
raise NotImplementedError
No runs yet
The stub's functions raise
NotImplementedError until you write them. Fill in one, press
Run tests, and watch its tests turn green.Reference solution
Available after your first run, with an account approved for code runs.