波動度目標化怎麼運作

固定部位大小會忽略市場環境。波動度目標化根據當前市場波動程度,自動調整每次下注的規模。

最後更新 2026-09
重點整理
  • 波動度目標化讓部位大小跟已實現波動反向連動:市場比目標吵就投少一點,比目標靜就投多一點,但有上限。
  • 兩步:先算對數報酬的滾動標準差再年化,得到已實現波動;再把訊號乘上 target_vol ÷ realized_vol,並用 vol_cap 砍頂。
  • 預設 target_vol=0.30、vol_cap=2.0、lookback=720、periods_per_year=8760,這組是為 1h 策略校準的——換週期一定要重算。
  • 經驗法則:target_vol ≈ 可接受 MDD ÷ 2。預設的 30% 對應的典型 MDD 約 −60%。
  • 它調的是部位大小,不是訊號的方向;訊號錯的時候,它只決定你錯多大。

同一支策略,在平靜的那幾個月無聊到你想關掉它;在市場開始劇烈跳動的那個星期,回撤深得跟回測平均完全對不起來。訊號沒有變,變的是市場——而你每次下注的大小一直沒變。

波動度目標化是什麼

波動度目標化是讓部位大小跟市場的已實現波動反向連動:波動比目標高就投少一點,比目標低就投多一點,但有上限。

基本的 Type A 策略輸出 1.0(做多)或 0.0(空手)。Runner 在訊號為 1.0 時把全部配置資金都投入。這樣做有效,但有個隱性問題:不管市場是平靜還是劇烈波動,策略下的注都一樣大。

在低波動期,1% 的日線波動已經算大。在高波動期,1% 的波動只是雜訊。如果策略在兩種環境都用同樣的部位大小進場,實際的金額風險就會大幅波動——高波動期的回撤會遠大於回測平均所暗示的水準。

訊號說「買」,沒說「買多少」。
固定部位等於預設每一天的市場都一樣吵——同樣一單,在平靜期和危機期是兩種不同的風險。

公式

Blave Agent 的波動度目標化分兩步:

第一步——測量已實現波動度

log_return = log(Close / Close.shift(1))
realized_vol = log_return.rolling(lookback).std() * sqrt(periods_per_year)

計算對數報酬率的滾動標準差,再乘以 sqrt(periods_per_year) 換算成年化。結果就是年化已實現波動度——和 Sharpe ratio 使用的波動度單位相同。

預設值:lookback=720 根 K 棒、periods_per_year=8760。這是為1h 週期策略校準的:720 根 = 30 天 × 24 小時;8760 = 24 × 365。

第二步——縮放訊號

scale = (target_vol / realized_vol).clip(upper=vol_cap)
scaled_signal = signal * scale

訊號(1.0 = 全額做多)乘以波動度比值,上限為 vol_cap,得到小數部位大小。

預設值:target_vol=0.30(年化 30%)、vol_cap=2.0(最大 2 倍槓桿)。

具體範例

已實現波動(年化)目標波動原始倍數套用上限後部位大小
10%30%3.0×2.0×(上限)配置資金的 200%
20%30%1.5×1.5×配置資金的 150%
30%30%1.0×1.0×100%——基準
60%30%0.5×0.5×配置資金的 50%
90%30%0.33×0.33×配置資金的 33%

已實現波動在 30% 時,策略以全額運行。危機期間波動飆升到 90% 時,策略自動縮減至三分之一部位。平靜期波動降至 10% 時,放大到上限 2 倍。

不同週期的參數調整

預設值(lookback=720、periods_per_year=8760)是為1h 策略設定的。其他週期必須重新計算:

週期periods_per_yearlookback(30 天窗口)
5min105,120(12 × 24 × 365)8,640 根
1h(預設)8,760720 根
4h2,190180 根
8h1,09590 根
1d36530 根
日線策略用 30 根的 lookback 很短。考慮用 252 根(一年)來取得更穩定的波動估計,避免對短期飆升過度反應。

怎麼加進策略裡

VOL_TARGETING    = True
TARGET_VOL       = 0.30   # 年化目標波動 30%
VOL_CAP          = 2.0    # 最大 2 倍槓桿
VOL_LOOKBACK     = 720    # lookback 窗口長度(根 K 棒)
PERIODS_PER_YEAR = 8760   # 1h 週期用

def fetch_data(hdrs):
    from lib.data import fetch_kline
    from lib.strategy import add_realized_vol
    df = fetch_kline(SYMBOL, INTERVAL, START, END, hdrs)
    if VOL_TARGETING:
        add_realized_vol(df, VOL_LOOKBACK, PERIODS_PER_YEAR)
    return df

def compute_signals(df):
    from lib.strategy import apply_vol_scaling
    signal = pd.Series(np.nan, index=df.index)
    # ... 訊號邏輯 ...
    if VOL_TARGETING:
        return apply_vol_scaling(signal, df, TARGET_VOL, VOL_CAP)
    return signal
參數預設意思什麼時候要改
TARGET_VOL0.30年化目標波動 30%照你能接受的 MDD ÷ 2
VOL_CAP2.0最大 2 倍槓桿完全不想要槓桿就設 1.0
VOL_LOOKBACK720波動估計的窗口長度(根 K 棒)換週期就要重算,見上面那張表
PERIODS_PER_YEAR8760年化用的每年期數(1h 週期)換週期就要重算,見上面那張表

注意:add_realized_vol 必須在 fetch_data 裡呼叫(訊號計算之前),因為 apply_vol_scaling 會讀取 df['realized_vol'],而這個欄位是第一個函式就地寫入的。

vol_cap 為什麼預設是 2×

上限的存在是因為在持續低波動的環境下,波動度目標化可能產生極端槓桿。如果已實現波動降到 5%,沒有上限的倍數會是 6 倍。單日不利的走勢就可能造成毀滅性的回撤,即使訊號本身完全正確。

預設 2× 的上限讓策略最多只用到配置資金的兩倍。如果你完全不想有槓桿,把 vol_cap=1.0——波動度目標化就只會在高波動時縮減部位,不會在低波動時放大超過全額。

目標波動和隱含最大回撤

一個實用的經驗法則:target_vol ≈ 可接受 MDD ÷ 2。這來自趨勢追蹤策略中年化波動度和最大回撤的經驗關係。

target_vol隱含典型 MDD適合情境
10%約 −20%保守型,資金保全優先
20%約 −40%中等
30%(預設)約 −60%積極型——用滿風險預算

怎麼知道你的參數設對了

1

periods_per_year 跟你的週期對得上嗎?預設 8760 是 1h 用的。5min 是 105,120、4h 是 2,190、8h 是 1,095、1d 是 365。填錯,年化那一步就跟著錯,後面每一格都是錯的。

2

lookback 還是 30 天嗎?720 根=1h × 30 天。換週期要跟著換:5min 8,640 根、4h 180 根、8h 90 根、1d 30 根。日線只用 30 根偏短,可以考慮 252 根(一年)換一個更穩的估計。

3

target_vol 跟你能接受的 MDD 對得上嗎?經驗法則是 target_vol ≈ 可接受 MDD ÷ 2:10% 對約 −20%、20% 對約 −40%、預設的 30% 對約 −60%。(MDD 怎麼讀,見〈回測是什麼?回測報告怎麼看〉。)

4

vol_cap 給你的槓桿是你要的嗎?預設 2.0 表示最多用到配置資金的兩倍。設 1.0,它就只會在高波動時縮部位,不會在低波動時放大超過全額。

5

個別策略的 TARGET_VOL 跟組合 Manager 的 target_vol_pct 一致嗎?Manager 決定整體槓桿用的是同一套邏輯(槓桿 = target_vol ÷ 組合已實現波動)。兩邊不一致,多個策略組起來會重複計算風險。

波動度目標化不會幫你做的事

  • 它調的是部位大小,不是訊號的對錯。縮放乘在訊號後面——訊號錯的時候,它只決定你錯多大。
  • target_vol ≈ 可接受 MDD ÷ 2 是趨勢追蹤策略上的經驗關係,不是保證。表裡寫的是「典型」MDD,不是上限。
  • 已實現波動是用過去 lookback 根算出來的:窗口越長,估計越穩、反應也越慢;窗口越短,越容易對一次短期飆升過度反應。兩邊都不會讓你提前知道波動要跳。