Lab 2 · Tail Risk & Square-Root Rule

尾部風險與平方根法則

Lab 1 我們已經知道:報酬率不是常態。那該怎麼量化「最壞會多慘」? 本 Lab 用 10 年真實資料跑歷史模擬法:計算 $\text{VaR}_{5\%}$、$\text{VaR}_{1\%}$、$\text{ES}_{5\%}$、$\text{ES}_{1\%}$、最大回撤 $\text{MDD}$; 再用平方根法則把日頻 VaR 換算成月頻,看看這個便宜的換算在真實市場裡到底差多少。

對應 CH4 問題集 P1–P3 對應 CH5 問題集 P1–P3 資料期間 2016-01-01 ~ 2025-12-31

開始之前

🕹️ 你要做什麼

  1. 挑 2~6 檔資產(預選 QQQ/SPY/AAPL/KO,也可加入台股 2330 / 0050 / 00645)。
  2. 看「日頻 / 月頻」兩張尾部風險表:$\text{VaR}_{5\%}$、$\text{ES}_{5\%}$、$\text{VaR}_{1\%}$、$\text{ES}_{1\%}$、$\text{MDD}$。
  3. 看「平方根法則誤差」表:將日 VaR/ES 乘上 $\sqrt{22}$,對照實際月值,找出誤差 > 20% 的項目。
  4. 寫三題反思 → 匯出 Markdown 報告(含完整 Python 實作碼)。

🧠 要建立的觀念

  • VaR(Value-at-Risk):在某信賴水準下的最壞單期損失分位數。$\text{VaR}_{5\%}$ = 100 天裡最慘的 5 天會超過的跌幅。
  • ES(Expected Shortfall):「破線那天平均跌多慘」。ES 永遠比 VaR 更負,能反映尾端厚度。
  • MDD(Maximum Drawdown):歷史上從波峰到波谷的最大累積跌幅 — 反映實際持有經驗。
  • 平方根法則($\sigma_T = \sigma_d \sqrt{T}$)在 IID 假設下成立。厚尾 + 波動叢聚 → 月 VaR 比 $\sqrt{T}$ 估出來的更極端。

🛠️ 會學到的方法

  • 歷史模擬 VaR:ret.quantile(0.05)(pandas 線性內插分位數)。
  • ES:ret[ret <= var].mean()(尾端條件平均)。
  • MDD:(px / px.cummax() - 1).min()
  • 平方根法則換算與相對誤差評估:|d·√22 − m| / |m|

📐 本 Lab 使用的關鍵公式

$$\text{VaR}_\alpha(R) = -\inf\{x \in \mathbb{R} : \Pr(R \le x) \ge \alpha\} \quad\text{(歷史分位數)}$$ $$\text{ES}_\alpha(R) = \mathbb{E}[\,R \mid R \le \text{VaR}_\alpha\,]$$ $$\text{MDD} = \min_{t}\left(\frac{P_t}{\max_{s \le t} P_s} - 1\right)$$ $$\text{VaR}_T \approx \text{VaR}_d \cdot \sqrt{T},\ T = 22 \quad\text{(平方根法則,IID 假設)}$$

本 Lab 的 VaR / ES 以負數呈現(損失方向)。pandas 的 quantile 使用線性內插;我們的 JS 實作刻意與之一致,方便你用 Colab 驗證。

1選擇資產

資料期間固定為 2016-01-01 ~ 2025-12-31(配合 CH4/CH5 問題集作業規格)
可多選 2~6 檔 — 建議同時包含股票(如 AAPL)與相對穩定資產(如 KO)以對比尾部厚度。

2歷史模擬尾部風險

對應 CH4 問題集 P1(日頻)、CH5 問題集 P1(月頻)
🐍 Python 程式碼(計算 VaR / ES / MDD,點擊展開)
import yfinance as yf, pandas as pd, numpy as np

tickers = ["QQQ", "SPY", "AAPL", "KO"]          # 可加 "2330.TW" / "0050.TW" / "00645.TW"
data = yf.download(tickers, start="2016-01-01", end="2025-12-31",
                   auto_adjust=False)["Adj Close"]

# === 日 / 月報酬 ===
daily   = data.pct_change().dropna()
monthly = data.resample("M").last().pct_change().dropna()

# === 歷史模擬 VaR + ES ===
def var_es(ret, q):
    var = ret.quantile(q)            # 線性內插分位數
    es  = ret[ret <= var].mean()     # 尾端條件平均
    return var, es

for sym in data.columns:
    d5, d5e = var_es(daily[sym],   0.05)
    d1, d1e = var_es(daily[sym],   0.01)
    m5, m5e = var_es(monthly[sym], 0.05)
    m1, m1e = var_es(monthly[sym], 0.01)
    print(f"{sym} 日: VaR5={d5:.4f}  ES5={d5e:.4f}  VaR1={d1:.4f}  ES1={d1e:.4f}")
    print(f"{sym} 月: VaR5={m5:.4f}  ES5={m5e:.4f}  VaR1={m1:.4f}  ES1={m1e:.4f}")

# === MDD ===
for sym in data.columns:
    px = data[sym].dropna()
    mdd = (px / px.cummax() - 1).min()
    print(f"{sym} MDD = {mdd:.4f}")

3平方根法則誤差分析

對應 CH4 問題集 P3 / CH5 問題集 P3:用日頻 VaR×√22 推估月頻,對照真實月值的相對誤差
🐍 Python 程式碼(√T 誤差檢驗,點擊展開)
import numpy as np

T = 22
mult = np.sqrt(T)
for sym in data.columns:
    d5, _ = var_es(daily[sym],   0.05)
    m5, _ = var_es(monthly[sym], 0.05)
    theory = d5 * mult
    err = abs(theory - m5) / abs(m5)
    print(f"{sym}: 真實月 VaR5={m5:.4f}  理論(√T)={theory:.4f}  相對誤差={err:.2%}")

4反思與匯出

寫完後按下匯出按鈕,會產生一份含所有表格與 Python 程式的 Markdown 報告。
下一個:Lab 3 · 投資組合優化 →