績效指標與風險管理
一個策略「有賺錢」還不夠,還要知道賺得穩不穩、過程中最慘會虧多少。這一頁介紹如何計算報酬率、夏普比率、最大回撤這幾個回測後最常看的績效指標,以及停損與部位控制這兩個最基本的風險管理工具。
計算報酬率
個股報酬率是最基本的績效起點:
data["Return"] = data["Close"].pct_change() # 每日報酬率
cum_return = (1 + data["Return"]).cumprod() - 1 # 累積報酬率
投資組合(多檔股票)的報酬率,則是把各檔股票依權重加總:
# weights:各股票的投資權重(合計為 1);returns:各股票的日報酬率 DataFrame
portfolio_return = (returns * weights).sum(axis=1)
ETF(如 0050、SPY)的報酬率算法與個股相同,差別只在於 ETF 本身已經是一籃子股票,能用來代表大盤或特定市場的整體表現,也常被當作策略績效的比較基準。
夏普比率(Sharpe Ratio)
夏普比率衡量「每承擔一單位風險,換到多少超額報酬」,是最常見的風險調整後績效指標:
\[
\text{夏普比率} = \frac{\text{超額報酬平均值}}{\text{超額報酬標準差}}
\]
其中超額報酬 = 投資組合報酬 − 無風險利率(如定存利率)。
import numpy as np
daily_return = data["Close"].pct_change()
risk_free_rate = 0.04 / 252 # 假設年化無風險利率 4%,換算成日利率
excess_return = daily_return - risk_free_rate
sharpe_ratio = np.sqrt(252) * excess_return.mean() / excess_return.std()
一般經驗法則:夏普比率 > 1 代表策略有一定吸引力,> 2 則屬於表現優異;但仍要搭配最大回撤一起看,避免只看報酬忽略風險。
最大回撤(Maximum Drawdown)
最大回撤是「從最高點到之後最低點,資產最多跌掉的百分比」,直接反映策略最壞情況下的心理與資金壓力:
cum_return = (1 + data["Close"].pct_change()).cumprod()
running_max = cum_return.cummax()
drawdown = cum_return / running_max - 1
max_drawdown = drawdown.min()
最大回撤越小,代表策略在市場下跌時越抗震;即使兩個策略總報酬相近,最大回撤較小的通常更適合長期執行。
停損與停利
停損(Stop Loss)與停利(Take Profit)是最基本的單筆交易風險控制:
stop_loss_pct = 0.02 # 2% 停損
take_profit_pct = 0.05 # 5% 停利
entry_price = data["Close"].iloc[-1]
stop_loss_price = entry_price * (1 - stop_loss_pct)
take_profit_price = entry_price * (1 + take_profit_pct)
部位管理(Position Sizing)
部位管理決定「這筆交易要投入多少資金」,目的是讓單筆交易的最大虧損維持在可承受範圍內,而不是每次都全押:
capital = 100_000 # 總資金
risk_per_trade = 0.01 # 單筆交易最多承受 1% 總資金的虧損
max_loss = capital * risk_per_trade
shares = max_loss / (entry_price - stop_loss_price)
即使策略勝率不錯,若每次都重壓,一次意外的重大虧損也可能讓資金曲線難以恢復——這是部位管理存在的原因。
動手做
| Notebook | 內容 |
|---|---|
| 4-1 計算個股投資報酬率 | 計算單一股票的報酬率 |
| 4-2 計算 ETF 報酬率 | 以 ETF 為例計算報酬率 |
| 4-3 計算投資組合報酬率 | 多檔股票依權重加總的投資組合報酬率 |
| 4-4 計算過去 N 年的投資報酬率 | 依指定年數回推計算長期報酬率 |
| 5-1 計算 Sharpe Ratio | 以 SPY、0050 為例計算夏普比率 |
| 5-2 計算 Maximum DrawDown | 使用 backtest.py 輔助模組計算最大回撤與回撤天數 |