風險管理:停損、投資組合與資金管理

在量化交易中,風險管理遠比交易策略重要

一個策略的報酬率再高,如果風險管理沒做好,一次黑天鵝事件就可能讓你賠光所有獲利。

為什麼風險管理這麼重要?

想像你有兩個交易策略:

| 策略 | 平均年報酬 | 最大回撤 | 最終結果 | |------|-----------|---------|---------| | 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. 輸出完整報告。」

本日總結

在本章中,你學到了:

  1. 停損策略:固定百分比停損,限制單筆最大虧損
  2. 凱利公式:計算最佳資金投入比例
  3. 投資組合最佳化:蒙地卡羅模擬找最佳權重
  4. 效率前緣:了解報酬與風險的取捨關係
  5. VaR (Value at Risk):量化最大可能虧損

下一章,我們將建立一個完整的量化交易儀表板!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!