Lab 3 · Mean-Variance Efficient Frontier
平均變異數效率前緣
馬可維茲告訴我們:理性投資人應該只待在「效率前緣」上。這條曲線是什麼形狀?由哪些量決定? 本 Lab 帶你從 $\Sigma$、$\Sigma^{-1}$ 開始,用 CH6 的三大係數 $A$、$B$、$C$ 推出 $\sigma^2 = (C\mu^2 - 2B\mu + A)/D$ 這條雙曲線,並用解析解算出 GMV 權重。
對應 CH6 問題集 P1–P5
資料期間 2016-01-01 ~ 2025-12-31
開始之前
🕹️ 你要做什麼
- 挑 2~6 檔資產(預選 SPY/QQQ/KO/AAPL)。
- 看 Step 2:$E$、$\Sigma$、$\Sigma^{-1}$ 三張表(年化後的數字)。
- 看 Step 3:$A$、$B$、$C$、$D$ 四個係數與 GMV 權重 — 這些就是問題集會叫你算的東西。
- 看 Step 4:把個別資產、GMV、效率前緣畫在 $(\sigma, \mu)$ 平面上。
- 寫三題反思 → 匯出 Markdown 報告(含 numpy 實作碼可貼到 Colab)。
🧠 要建立的觀念
- 共變異數矩陣 $\Sigma$:對角線是變異數,非對角線是共變異數 — 兩兩資產「一起動」的程度。
- 效率前緣是一條雙曲線。GMV 在頂點左側,其上方的弧段才是「效率的」(相同風險下最高報酬)。
- GMV 權重 $w_{GMV} \propto \Sigma^{-1}\mathbf{1}$,完全不用到 $E$ — 因此在實務上對平均報酬的估計誤差最不敏感。
- 當兩檔資產高度共線時,$\Sigma$ 接近奇異,GMV 不穩定 — 這就是為什麼實務上會用收縮法(shrinkage)。
🛠️ 會學到的方法
- 從
pct_change→rets.cov()得到 $\Sigma$。 - 用
np.linalg.inv算 $\Sigma^{-1}$。 - 以向量/矩陣運算算 $A = E^\top \Sigma^{-1} E$、$B$、$C$、$D$。
- 用 $w_{GMV} = \Sigma^{-1}\mathbf{1}/C$ 與 $\sigma^2 = (C\mu^2-2B\mu+A)/D$ 直接畫出前緣。
📐 本 Lab 的核心公式(CH6)
$$A = E^\top \Sigma^{-1} E,\quad B = E^\top \Sigma^{-1}\mathbf{1},\quad C = \mathbf{1}^\top \Sigma^{-1}\mathbf{1},\quad D = AC - B^2$$
$$w_{GMV} = \dfrac{\Sigma^{-1}\mathbf{1}}{C}, \quad \mu_{GMV} = \dfrac{B}{C}, \quad \sigma^2_{GMV} = \dfrac{1}{C}$$
$$\boxed{\ \sigma^2 \;=\; \dfrac{1}{D}\bigl(C\mu^2 - 2B\mu + A\bigr)\ }$$
最後一式是 $(\sigma, \mu)$ 平面上的雙曲線;切一刀 $\mu \ge \mu_{GMV}$ 的部分就是效率前緣。
1選擇資產(2–6 檔)
建議混合「不同資產類別」(大盤 + 個股 + 防禦股)才能看到真正的分散效果。資料期間固定為 2016-01-01 ~ 2025-12-31;本 Lab 允許做空(權重可為負),純做多需要 QP 數值解,屬於進階延伸。
2平均向量 $E$ 與共變異數矩陣 $\Sigma$
對應 CH6 問題集 P1:估計 $E$、$\Sigma$(本 Lab 使用年化日報酬)。🐍 Python 程式碼($E$、$\Sigma$、$\Sigma^{-1}$,點擊展開)
import yfinance as yf, pandas as pd, numpy as np
tickers = ["SPY", "QQQ", "KO", "AAPL"] # 可加 "2330.TW" / "0050.TW"
data = yf.download(tickers, start="2016-01-01", end="2025-12-31",
auto_adjust=False)["Adj Close"].dropna()
rets = data.pct_change().dropna()
E = rets.mean().values * 252
Sigma = rets.cov().values * 252
inv = np.linalg.inv(Sigma)
print("E =", dict(zip(data.columns, E.round(4))))
print("Sigma =\n", pd.DataFrame(Sigma, index=data.columns, columns=data.columns).round(4))
print("Sigma^-1 =\n", pd.DataFrame(inv, index=data.columns, columns=data.columns).round(4))
3GMV 解析解與效率前緣係數
對應 CH6 問題集 P2/P3:代公式,驗證 GMV 與雙曲線方程。🐍 Python 程式碼($A, B, C, D$ 與 GMV,點擊展開)
ones = np.ones_like(E)
A = E @ inv @ E
B = E @ inv @ ones
C = ones @ inv @ ones
D = A * C - B ** 2
print(f"A={A:.4f} B={B:.4f} C={C:.4f} D={D:.4f}")
w_gmv = inv @ ones / C
mu_gmv = B / C
var_gmv = 1 / C
print("w_GMV =", dict(zip(data.columns, w_gmv.round(4))))
print(f"μ_GMV={mu_gmv:.4f} σ_GMV={np.sqrt(var_gmv):.4f}")
4效率前緣 $(\sigma, \mu)$ 散佈圖
對應 CH6 問題集 P4/P5:畫出雙曲線,觀察 GMV 位置與個別資產的相對位置。效率前緣
綠實線 = 效率前緣($\mu \ge \mu_{GMV}$);綠虛線 = 最小變異前緣的下半;三角 = GMV;圓點 = 個別資產。效率前緣「吞掉」所有個別資產 — 這就是分散化帶來的免費午餐。
🐍 Python 程式碼(畫出效率前緣,點擊展開)
import matplotlib.pyplot as plt
mus = np.linspace(mu_gmv, E.max() * 1.2, 50)
sig = np.sqrt((C * mus ** 2 - 2 * B * mus + A) / D)
plt.plot(sig, mus, label="Efficient frontier")
plt.scatter(np.sqrt(np.diag(Sigma)), E, label="Assets")
plt.scatter([np.sqrt(var_gmv)], [mu_gmv], s=120, marker="^", label="GMV")
plt.xlabel("σ"); plt.ylabel("μ"); plt.legend(); plt.grid(alpha=0.3)
plt.show()