Chapter 6 Laboratory — Module 6: Paths, Quadratic Variation, and Jumps¶

Mathematical Foundations of Modern Finance · Part II · Week 6

Three panels: the scaled-walk limit (Donsker, reflection principle), quadratic variation / realized variance across sampling frequencies, and a Poisson-jump overlay with a tail odometer. Reproduces the collar limit −2.76 and the reflection probability 0.556.

Seeds: 20260601–20260604.

In [1]:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams.update({"figure.figsize": (7, 4.2), "axes.grid": True,
                     "grid.alpha": 0.3, "font.size": 11})
BRASS, INK = "#B4884A", "#0F1E3D"
import mfmf_engine_ch06 as eng

E1 — Donsker and the collar limit (supports LOS 6.1–6.2)¶

Refine the binomial tree ($n = 4, 12, 52, 252$) and watch the collar price converge to the continuous-time value $-2.76$ — the exact limit Chapter 8 confirms in closed form.

In [2]:
r1 = eng.E1_donsker_collar()
for n, v in r1['collar_by_n'].items():
    print(f"n = {n:3d} steps : collar = {v:+.4f}")
print(f"\nLimit (n=252): {r1['limit']:.4f}   target: {r1['target']}")

ns = list(r1['collar_by_n'].keys()); vs = list(r1['collar_by_n'].values())
fig, ax = plt.subplots()
ax.plot(ns, vs, "o-", color=INK, lw=2)
ax.axhline(r1['target'], color=BRASS, ls="--", lw=1.5, label="BS limit -2.76")
ax.set_xscale("log"); ax.set_xlabel("tree steps n"); ax.set_ylabel("collar price")
ax.set_title("E1: binomial collar converges to the continuous limit")
ax.legend(); plt.tight_layout(); plt.show()
n =   4 steps : collar = -2.8224
n =  12 steps : collar = -2.6734
n =  52 steps : collar = -2.7171
n = 252 steps : collar = -2.7644

Limit (n=252): -2.7644   target: -2.76
No description has been provided for this image

E2 — Realized variance across frequencies (supports LOS 6.3–6.4)¶

The quadratic variation $[W,W]_T = T$ is estimated by realized variance. Denser sampling sharpens the estimate — until microstructure noise corrupts the finest scales.

In [3]:
r2 = eng.E2_realized_variance()
for freq, d in r2['estimates'].items():
    print(f"{freq:8s}: vol = {d['vol']:.4f} ± {d['se']:.4f}  (n = {d['n_obs']})")
print(f"True sigma: {r2['true_sigma']}")

freqs = list(r2['estimates'].keys())
vols = [r2['estimates'][f]['vol'] for f in freqs]
ses = [r2['estimates'][f]['se'] for f in freqs]
fig, ax = plt.subplots()
ax.errorbar(freqs, vols, yerr=[2*s for s in ses], fmt="o", color=INK, capsize=5)
ax.axhline(r2['true_sigma'], color=BRASS, ls="--", lw=1.5, label="true sigma")
ax.set_ylabel("annualized vol estimate")
ax.set_title("E2: realized variance sharpens with sampling frequency")
ax.legend(); plt.tight_layout(); plt.show()
monthly : vol = 0.2249 ± 0.0459  (n = 12)
daily   : vol = 0.1759 ± 0.0078  (n = 252)
5-min   : vol = 0.1697 ± 0.0009  (n = 19656)
True sigma: 0.17
No description has been provided for this image

E3 — The reflection principle (supports LOS 6.5)¶

The fraction of driftless $\sigma=0.17$ paths touching $+10\%$ within the year is $2\Phi(-0.10/0.17) = 0.556$ — larger than a coin flip, for an asset going nowhere on average. The Monte Carlo (with a Brownian-bridge crossing correction) matches.

In [4]:
r3 = eng.E3_reflection_mc()
print(f"Monte Carlo fraction : {r3['mc_fraction']:.4f}")
print(f"Reflection formula   : {r3['formula']:.4f}")
print(f"Book target          : {r3['target']}")
Monte Carlo fraction : 0.5561
Reflection formula   : 0.5564
Book target          : 0.556

E4 — Jumps and the tail odometer (supports LOS 6.6)¶

Superpose a Poisson jump stream on GBM. The tail odometer reports the model-implied waiting time for a $-6\%$ day — jumps make such days far more frequent than GBM allows.

In [5]:
r4 = eng.E4_jump_odometer()
print(f"P(daily return <= -6%)          : {r4['p_minus6pct']:.5f}")
print(f"Implied waiting time (years)    : {r4['waiting_years_for_-6pct_day']:.2f}")
P(daily return <= -6%)          : 0.00043
Implied waiting time (years)    : 9.23

Validation checks¶

Every laboratory report must reproduce these; a report whose checks do not pass is returned ungraded.

In [6]:
checks = eng.validation_checks()
for k, v in checks.items():
    if k.startswith("_"): continue
    print(f"[{'PASS' if v else 'FAIL'}] {k}")
assert checks["ALL_PASS"], "Validation failed — do not submit."
print("\nAll Module validation checks pass.")
[PASS] V1_reflection_formula_0.556
[PASS] V2_collar_converges_to_-2.76
[PASS] V3_reflection_mc_matches
[PASS] V4_QV_equals_T
[PASS] ALL_PASS

All Module validation checks pass.