風險管理:停損、投資組合與資金管理
在量化交易中,風險管理遠比交易策略重要。
一個策略的報酬率再高,如果風險管理沒做好,一次黑天鵝事件就可能讓你賠光所有獲利。
為什麼風險管理這麼重要?
想像你有兩個交易策略:
| 策略 | 平均年報酬 | 最大回撤 | 最終結果 | |------|-----------|---------|---------| | A | +30% | -15% | 10 年後:穩定成長 ✅ | | B | +50% | -60% | 遇到一次崩盤就幾乎歸零 ❌ |
在市場中活得久,比賺得快更重要。
停損策略 (Stop Loss)
停損是最基本也最重要的風險管理工具。它在你買入股票的同時,就設定好「如果跌到多少錢,就自動賣出」。
固定百分比停損
# 在 Backtrader 中加入停損
class GoldenCrossWithStopLoss(bt.Strategy):
params = (
('short_window', 10),
('long_window', 30),
('stop_loss', 0.05), # 5% 停損
)
def __init__(self):
self.short_ma = bt.indicators.SimpleMovingAverage(
self.data.close, period=self.params.short_window
)
self.long_ma = bt.indicators.SimpleMovingAverage(
self.data.close, period=self.params.long_window
)
self.crossover = bt.indicators.CrossOver(
self.short_ma, self.long_ma
)
self.entry_price = None
def next(self):
# 有持倉時檢查停損
if self.position and self.entry_price:
loss_pct = (self.data.close[0] - self.entry_price) / self.entry_price
if loss_pct < -self.params.stop_loss:
self.close()
self.log(f'停損賣出 - 虧損: {loss_pct*100:.2f}%')
self.entry_price = None
return
# 買進邏輯
if not self.position and self.crossover > 0:
self.buy(size=10)
self.entry_price = self.data.close[0]
self.log(f'買進 - 價格: {self.entry_price:.2f}')
# 賣出邏輯
elif self.position and self.crossover < 0:
self.close()
self.log(f'趨勢賣出 - 價格: {self.data.close[0]:.2f}')
self.entry_price = None
比較有無停損的績效
# 比較兩種策略
cerebro1 = bt.Cerebro()
cerebro1.adddata(data)
cerebro1.addstrategy(GoldenCrossStrategy) # 無停損
cerebro1.broker.setcash(1000000.0)
cerebro1.broker.setcommission(commission=0.001425)
cerebro2 = bt.Cerebro()
cerebro2.adddata(data)
cerebro2.addstrategy(GoldenCrossWithStopLoss, stop_loss=0.08) # 8% 停損
cerebro2.broker.setcash(1000000.0)
cerebro2.broker.setcommission(commission=0.001425)
result1 = cerebro1.run()
result2 = cerebro2.run()
print(f"無停損最終資金: {cerebro1.broker.getvalue():,.0f}")
print(f"有停損最終資金: {cerebro2.broker.getvalue():,.0f}")
資金管理:凱利公式 (Kelly Criterion)
凱利公式告訴你:「每次交易應該投入多少比例的資金,才能在長期獲得最大的複利成長。」
$$f^* = \frac{bp - q}{b} = \frac{p(b + 1) - 1}{b}$$
- $f^*$:建議投入的資金比例
- $p$:勝率(獲利交易的比例)
- $q$:敗率 = 1 - p
- $b$:賠率(平均獲利 / 平均虧損)
def kelly_criterion(win_rate, avg_win, avg_loss):
"""
計算凱利公式建議的資金比例
Parameters:
win_rate: 勝率 (0.0 ~ 1.0)
avg_win: 平均獲利金額
avg_loss: 平均虧損金額
"""
b = avg_win / avg_loss # 賠率
p = win_rate
q = 1 - p
f_star = (b * p - q) / b
return max(0, min(f_star, 0.25)) # 限制在 0% ~ 25% 之間
# 範例
win_rate = 0.55 # 55% 勝率
avg_win = 2000 # 平均獲利 2000
avg_loss = 1500 # 平均虧損 1500
f = kelly_criterion(win_rate, avg_win, avg_loss)
print(f"建議投入比例: {f*100:.1f}%")
# 實務建議:使用半凱利 (Half Kelly) 更安全
half_kelly = f / 2
print(f"半凱利建議比例: {half_kelly*100:.1f}%")
⚠️ 凱利公式的實務限制: 凱利公式假設你知道精確的勝率與賠率,但實務上這些都是估計值。因此業界通常使用「半凱利 (Half Kelly)」或「四分之一凱利」來降低風險。
投資組合最佳化
不要把所有資金放在單一股票上!馬科維茨的現代投資組合理論告訴我們:透過分散投資,可以在不降低預期報酬的情況下降低風險。
計算投資組合的預期報酬與風險
import yfinance as yf
import numpy as np
import pandas as pd
# 選取五檔股票
portfolio = ["2330.TW", "2317.TW", "2454.TW", "2308.TW", "2881.TW"]
# 下載資料
data = yf.download(portfolio, start="2022-01-01", end="2024-12-31")
close = data['Close']
# 計算每日報酬率
returns = close.pct_change().dropna()
# 計算年化報酬率與共變異數矩陣
annual_returns = returns.mean() * 252
cov_matrix = returns.cov() * 252
print("=== 各股票年化報酬率 ===")
print(annual_returns)
print("\n=== 共變異數矩陣 ===")
print(cov_matrix)
蒙地卡羅模擬找最佳權重
import matplotlib.pyplot as plt
n_portfolios = 10000 # 模擬 10000 種組合
results = []
weights_record = []
for _ in range(n_portfolios):
# 隨機生成權重
weights = np.random.random(len(portfolio))
weights = weights / weights.sum()
weights_record.append(weights)
# 計算投資組合報酬率
port_return = np.sum(annual_returns * weights)
# 計算投資組合風險(標準差)
port_std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
# 夏普比率(假設無風險利率 2%)
sharpe = (port_return - 0.02) / port_std
results.append({
'return': port_return,
'risk': port_std,
'sharpe': sharpe
})
results_df = pd.DataFrame(results)
# 找出夏普比率最高的組合
best_idx = results_df['sharpe'].idxmax()
best_weights = weights_record[best_idx]
print("\n=== 最佳投資組合 ===")
for ticker, weight in zip(portfolio, best_weights):
print(f"{ticker}: {weight*100:.1f}%")
print(f"預期年化報酬: {results_df.loc[best_idx, 'return']*100:.2f}%")
print(f"預期年化風險: {results_df.loc[best_idx, 'risk']*100:.2f}%")
print(f"夏普比率: {results_df.loc[best_idx, 'sharpe']:.2f}")
VaR (Value at Risk)
VaR 告訴你:「在 95% 的信心水準下,明天最多會虧多少錢。」
# 計算投資組合的 VaR
confidence_level = 0.95
# 投資組合每日報酬率
port_daily_returns = returns @ best_weights
# 歷史模擬法 VaR
var_95 = np.percentile(port_daily_returns, (1 - confidence_level) * 100)
# 投資組合目前市值
portfolio_value = 1000000 # 假設 100 萬
print(f"\n=== VaR 風險評估 ===")
print(f"95% VaR(日): {var_95*100:.2f}%")
print(f"95% VaR(金額): {abs(var_95) * portfolio_value:,.0f} 元")
print(f"這代表有 95% 的把握,明天虧損不超過 {abs(var_95) * portfolio_value:,.0f} 元")
使用 Vibe Coding 建立風控系統
🔥 【風險管理詠唱範例】
「請幫我建立一個風險管理儀表板:1. 輸入五檔股票的代號,下載過去 3 年資料。2. 使用蒙地卡羅模擬找出夏普比率最高的投資組合權重。3. 計算該組合的 95% VaR(日、週、月)。4. 畫出效率前緣 (Efficient Frontier) 圖。5. 畫出每個權重的風險貢獻佔比。6. 輸出完整報告。」
本日總結
在本章中,你學到了:
- ✅ 停損策略:固定百分比停損,限制單筆最大虧損
- ✅ 凱利公式:計算最佳資金投入比例
- ✅ 投資組合最佳化:蒙地卡羅模擬找最佳權重
- ✅ 效率前緣:了解報酬與風險的取捨關係
- ✅ VaR (Value at Risk):量化最大可能虧損
下一章,我們將建立一個完整的量化交易儀表板!