Chapter 2 Laboratory — Module 2: Probability and Distribution Lab¶
Mathematical Foundations of Modern Finance · Part I · Week 2
Two panels: distribution fitting (normal vs Student-$t$ by MLE, tail probabilities and quantiles) and a copula sandbox (Gaussian / $t$ / Clayton dependence, joint-tail counts, two-asset loss). Reproduces the two analysts of Examples 2.5 and 2.11.
Seeds: 20260201–20260203.
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_ch02 as eng
E1 — Two analysts and the crossing (supports LOS 2.2–2.5)¶
Analyst one models the return as Normal; analyst two as a Student-$t$ matched to the same mean and standard deviation. The $t$ is lighter near the mean and heavier far out, so the two tail probabilities cross exactly once.
r1 = eng.E1_two_analysts()
print(f"Normal P(R < -0.15) : {r1['normal_at_-0.15']:.4f}")
print(f"t P(R < -0.15) : {r1['t_at_-0.15']:.4f} (book: 7.5%)")
print(f"Normal P(R < -0.25) : {r1['normal_at_-0.25']:.4f}")
print(f"t P(R < -0.25) : {r1['t_at_-0.25']:.4f}")
print(f"Crossing threshold : {r1['crossing_threshold']:.4f}")
xs = np.linspace(-0.5, 0.6, 400)
fig, ax = plt.subplots()
ax.plot(xs, [eng.normal_tail(x) for x in xs], color=INK, lw=2, label="Normal")
ax.plot(xs, [eng.t_tail(x) for x in xs], color=BRASS, lw=2, label="Student-t (nu=4)")
ax.axvline(r1['crossing_threshold'], color="grey", ls="--", lw=1, label="crossing")
ax.set_xlabel("return threshold"); ax.set_ylabel("P(R < threshold)")
ax.set_title("E1: normal vs t left-tail probabilities cross once")
ax.legend(); plt.tight_layout(); plt.show()
Normal P(R < -0.15) : 0.1044 t P(R < -0.15) : 0.0750 (book: 7.5%) Normal P(R < -0.25) : 0.0337 t P(R < -0.25) : 0.0305 Crossing threshold : -0.2721
E2 — Estimation risk: the unstable degrees of freedom (supports LOS 2.3)¶
Refit the $t$'s tail parameter $\nu$ on rolling five-year windows. It is strikingly unstable — the estimation-risk theme of Chapter 12, in miniature.
r2 = eng.E2_rolling_df()
print("Rolling df estimates:", r2['df_estimates'])
print(f"Range: [{r2['df_min']}, {r2['df_max']}] around a true df of {r2['true_df']}")
fig, ax = plt.subplots()
ax.plot(range(1, len(r2['df_estimates'])+1), r2['df_estimates'],
"o-", color=INK, lw=2)
ax.axhline(r2['true_df'], color=BRASS, ls="--", lw=1.5, label="true nu = 4")
ax.set_xlabel("rolling window"); ax.set_ylabel("estimated degrees of freedom")
ax.set_title("E2: the tail parameter is unstable across windows")
ax.legend(); plt.tight_layout(); plt.show()
Rolling df estimates: [2.957, 3.039, 3.405, 3.215, 3.436, 3.69, 3.933, 3.762] Range: [2.957, 3.933] around a true df of 4
E3 — Copula switch: tail dependence appears (supports LOS 2.5)¶
Fix both marginals and the correlation at 0.6; switch the copula from Gaussian to $t_4$. The linear correlation is unchanged, but the joint-tail count jumps.
r3 = eng.E3_copula_switch()
print(f"Joint-tail freq, Gaussian copula : {r3['joint_tail_gaussian']:.5f}")
print(f"Joint-tail freq, t4 copula : {r3['joint_tail_t4']:.5f}")
print(f"Independent benchmark : {r3['independent_benchmark']:.5f}")
print(f"Tail amplification (t4/Gaussian) : {r3['tail_amplification']:.2f}x")
fig, ax = plt.subplots()
ax.bar(["independent", "Gaussian", "t4"],
[r3['independent_benchmark'], r3['joint_tail_gaussian'], r3['joint_tail_t4']],
color=["grey", INK, BRASS])
ax.set_ylabel("P(both assets in lower 5% tail)")
ax.set_title("E3: same correlation, far more joint-tail risk under t4")
plt.tight_layout(); plt.show()
Joint-tail freq, Gaussian copula : 0.01563 Joint-tail freq, t4 copula : 0.02016 Independent benchmark : 0.00250 Tail amplification (t4/Gaussian) : 1.29x
Validation checks¶
Every laboratory report must reproduce these; a report whose checks do not pass is returned ungraded.
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_t_scale_matches_sd [PASS] V2_normal_tail_reasonable [PASS] V3_t_lighter_near_mean [PASS] V4_crossing_exists [PASS] V5_t_heavier_far_out [PASS] ALL_PASS All Module validation checks pass.