Chapter 9 Laboratory — Module 9: The Allocation Laboratory¶
Mathematical Foundations of Modern Finance · Part III · Week 9
Four panels: the frontier, the estimation-fragility panel, the Merton dynamic solution, and the Kelly growth panel. Reproduces the tangency weights 16.5 / 37.0 / 46.5 and the Merton 60% weight at γ = 1.74.
Seeds: 20260901–20260904.
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_ch09 as eng
E1 — The tangency portfolio and the Sharpe gap (supports LOS 9.2)¶
From the capital-market assumptions, the tangency weights are 16.5 / 37.0 / 46.5. A 60/40-style policy leaves a measurable Sharpe gap to efficiency.
r1 = eng.E1_tangency()
print(f"Tangency weights (%) : {r1['tangency_weights_pct']} (book: 16.5 / 37.0 / 46.5)")
print(f"Tangency Sharpe : {r1['tangency_sharpe']:.4f}")
print(f"Policy Sharpe : {r1['policy_sharpe']:.4f}")
print(f"Sharpe gap : {r1['sharpe_gap']:.4f}")
w = r1['tangency_weights_pct']
fig, ax = plt.subplots()
ax.bar(["Asset 1", "Asset 2", "Asset 3"], w, color=[INK, BRASS, "#5A6B82"])
ax.set_ylabel("tangency weight (%)"); ax.set_title("E1: the tangency portfolio")
plt.tight_layout(); plt.show()
Tangency weights (%) : [16.5, 37.0, 46.5] (book: 16.5 / 37.0 / 46.5) Tangency Sharpe : 0.5468 Policy Sharpe : 0.5426 Sharpe gap : 0.0042
E2 — Estimation fragility and its defenses (supports LOS 9.3)¶
Tangency weights are exquisitely sensitive to the mean inputs. Resampling the means produces a wide cloud of weights; shrinkage toward the cross-sectional average tames it.
r2 = eng.E2_fragility()
print(f"Base weights (%) : {r2['base_weights_pct']}")
print(f"Raw weight dispersion : {r2['raw_weight_dispersion']:.4f}")
print(f"Shrunk weight dispersion: {r2['shrunk_weight_dispersion']:.4f}")
print(f"Shrinkage tames the cloud: {r2['shrinkage_tames']}")
Base weights (%) : [16.5, 37.0, 46.5] Raw weight dispersion : 0.2047 Shrunk weight dispersion: 0.1589 Shrinkage tames the cloud: True
E3 — Merton's dynamic solution (supports LOS 9.4–9.5)¶
The dynamic problem has a constant-weight solution $w^\* = (\mu-r_f)/(\gamma\sigma^2)$. At $\gamma = 1.74$ this is the 60% risky weight — and the investment horizon is irrelevant.
r3 = eng.E3_merton()
print(f"Merton weight at gamma=1.74 : {r3['merton_weight']:.4f} (book: 60%)")
print(f"Implied gamma for a 60% weight: {r3['implied_gamma_for_60pct']:.4f}")
print(f"Horizon irrelevant : {r3['horizon_irrelevant']}")
Merton weight at gamma=1.74 : 0.6005 (book: 60%) Implied gamma for a 60% weight: 1.7415 Horizon irrelevant : True
E4 — The Kelly criterion and the growth ceiling (supports LOS 9.6)¶
The log-growth rate is a parabola in the risky weight. Kelly sits at the vertex; twice-Kelly gives back all the growth (zero), the growth ceiling made concrete.
r4 = eng.E4_kelly()
print(f"Kelly fraction : {r4['kelly_fraction']:.4f}")
print(f"g at 60% : {r4['g_at_60pct']:.5f}")
print(f"g at Kelly : {r4['g_at_kelly']:.5f}")
print(f"g at twice-Kelly : {r4['g_at_twice_kelly']:.5f} (zero growth)")
ws = np.linspace(0, 2*r4['kelly_fraction'], 100)
excess, sigma = 0.052-0.02, 0.175
g = ws*excess - 0.5*ws**2*sigma**2
fig, ax = plt.subplots()
ax.plot(ws, g, color=INK, lw=2)
ax.axvline(r4['kelly_fraction'], color=BRASS, ls="--", lw=1.5, label="Kelly")
ax.axhline(0, color="grey", lw=1)
ax.set_xlabel("risky weight w"); ax.set_ylabel("log-growth rate g(w)")
ax.set_title("E4: the growth parabola; Kelly at the vertex")
ax.legend(); plt.tight_layout(); plt.show()
Kelly fraction : 1.0449 g at 60% : 0.01369 g at Kelly : 0.01672 g at twice-Kelly : 0.00000 (zero growth)
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_tangency_16.5_37_46.5 [PASS] V2_sharpe_gap_positive [PASS] V3_merton_60pct_at_gamma_1.74 [PASS] V4_twice_kelly_zero_growth [PASS] ALL_PASS All Module validation checks pass.