Lab 1 · Performance Measurement
績效衡量實驗室
同一個投資組合,拿給 A 老師和 B 老師算 Sharpe,答案居然不一樣—— 原因不在數學,在「算法選擇」。在這個 Lab,你會親手切換幾種常見的計算設定, 看 Sharpe / Sortino / Calmar / 年化報酬 實際差多少。
1選投資組合
2設定兩組算法
3並排比較
開始之前
🕹️ 你要做什麼
- 從 4 個預設投組中挑一個(VTI 單壓、60/40、全球分散、科技+債券)。
- 左邊「基準卡」固定一組算法;右邊「自訂卡」改 4 個參數:年化方式、無風險利率、交易日、波動頻率。
- 按「計算並比較」,看 KPI 差值(年化報酬、Sharpe、Sortino、Calmar、MDD)。
- 寫下反思 → 匯出 Markdown 報告。
🧠 要建立的觀念
- 績效指標是「假設的產物」—— 換一組假設,數字就變,不是 bug。
- 幾何年化 < 算術年化,差距來自波動拖累(
variance drag)。 - 無風險利率 (RF) 是 Sharpe 分子的基準線,假設越高、Sharpe 越低。
- 波動度會隨資料頻率(日/週/月)不同而改變,需年化回 √252、√52、√12。
🛠️ 會學到的方法
- 計算 CAGR、Sharpe、Sortino、Calmar、MDD 的完整公式。
- 日頻 → 週/月頻資料重取樣(compound 複利法)。
- 用同一段報酬,在 Excel 或 Python 重現網頁上的兩組結果。
- 讀別人的績效報告時,學會先問「他用哪組假設?」
1選投資組合
選一個 preset,或切換時間區間。權重已固定,這個 Lab 的焦點是「算法」而不是「建構」。2設定兩組算法
左邊是「教科書基準」,右邊是你自己的選擇。兩組會同時算給你看。📘 基準設定(不可改)
- 年化方式
- 幾何年化
- 無風險利率
- 2% / 年
- 交易日數
- 252
- 波動度資料頻率
- 日報酬
🧪 你的設定(可調)
3並排比較
差距 ≥ 10% 會以紅字標出。數字會隨你在 Step 2 的調整即時更新。權益曲線(起始 = 1)
回撤(Drawdown)
Rolling 252 日 Sharpe — 同一條權益曲線,兩套算法
🎓 教學解讀:這些數字在告訴你什麼?
每個 KPI 差距背後都有一個「觀念陷阱」—— 把指標讀成判斷題之前,先讀懂它對假設有多敏感。算術年化 vs 幾何年化
算術年化 = 平均日報酬 × 252,它高估長期複利。幾何年化 = (1+r₁)(1+r₂)…ⁿ − 1 才反映真實滾出的資產。兩者差額 ≈ ½σ²,稱為 variance drag。
💡 試試看:把右邊設成「算術年化」,配高波動資產(科技重倉),差距會放大到 2–4%。
📐 variance drag
arith = r.mean() * 252
geo = (1 + r).prod() ** (252 / len(r)) - 1
drag = arith - geo # ≈ σ² / 2
無風險利率的槓桿效果
Sharpe = (μ − RF) / σ。RF 越高,分子越小、Sharpe 越低。極端案例:RF ≈ 平均報酬時,Sharpe 會接近 0 甚至為負。
💡 試試看:把 RF 從 0% 調到 5%,看 60/40 的 Sharpe 掉多少。Sortino 用「下行偏差」當分母,對 RF 的敏感度略低。
🎚️ Sharpe vs RF
vol = r.std() * np.sqrt(252)
sharpe = lambda rf: (geo - rf) / vol
sharpe(0.00), sharpe(0.02), sharpe(0.05)
波動度頻率:√252 vs √12 的陷阱
理論上日/週/月頻年化應該相近(IID 假設)。實際常差 10–30%,因為報酬有自相關(波動叢聚)。月頻會「平均掉」當月的劇烈震盪,通常看起來更低、Sharpe 更高 —— 這是業界常見的粉飾手法。
💡 試試看:切到 2020(COVID),用月頻 √12,看 Sharpe 會不會變得很漂亮。
📆 換頻率看 σ
sig_d = r.std() * np.sqrt(252) # 日
sig_w = r.resample("W").sum().std() * np.sqrt(52)
sig_m = r.resample("M").sum().std() * np.sqrt(12)
# 三個值應該接近;差很多 → 自相關
252 vs 365 交易日
波動度年化 σ × √n 的 n 必須跟資料頻率對齊。美股用 252(一年約 252 交易日);365 會把波動度放大 √(365/252) ≈ 1.20 倍,Sharpe 變小。讀別人報告時,看到不尋常的 Sharpe 先檢查這個。
💡 試試看:把交易日從 252 切到 365,觀察波動度欄位的變化幅度。
📅 252 vs 365 偏差
cagr_252 = eq.iloc[-1] ** (252 / n) - 1 # 正確
cagr_365 = eq.iloc[-1] ** (365 / n) - 1 # 錯誤!
# cagr_365 / cagr_252 ≈ 365/252 ≈ 1.45 倍膨脹
想要完整可執行的 notebook?
上方每個觀念都附了 3~5 行對應程式碼;要看「把它們串起來的完整 pipeline」, 可以在 Colab 打開我們準備好的
在 Colab 開啟 →
上方每個觀念都附了 3~5 行對應程式碼;要看「把它們串起來的完整 pipeline」, 可以在 Colab 打開我們準備好的
lab1_performance.ipynb,一鍵重現本頁所有數字。