ARK · 金融機器學習CHAPTER 10 / 12

CHAPTER 10 / 12 · PART 3 · 時序決策:強化學習

強化學習的金融應用

Applications of Reinforcement Learning

把對沖與退休投資寫成序列決策:QLBS 從資料定價,G-learning 把硬決策軟化。

§01學習重點

§02課程內容

一、定價是預測問題,還是決策問題?

想像你在期交所賣出一口三個月後到期的台指買權,收進一筆權利金。從這一刻起你背了一個義務:到期時若大盤高於履約價,差額由你埋單。接下來三個月你每天要做的事,不是「預測大盤會漲到哪」,而是管理這個義務的風險——持有多少口台指期貨來對沖、今天要不要加減碼。市場每天給你新的資訊(指數變動、剩餘天數),你每天做一個動作(調整對沖部位),每個動作都產生當期損益,然後進入下一天。

把這段話翻成第 9 章的語言:這是一個馬可夫決策過程。狀態是指數水位、剩餘時間與你目前的部位;行動是調整後的對沖持倉;報酬是這一期的風險調整損益;你要學的政策,是在每個狀態下該持有多少對沖部位的規則。選擇權定價從頭到尾就是一個序列決策問題。

那為什麼你在教科書上學到的 Black-Scholes 公式看起來完全不像決策問題,而像一條純數學的定價公式?因為 BS 活在一個把決策「做到隱形」的理想世界:交易連續進行、沒有任何摩擦,於是最適對沖(delta 對沖)可以每一瞬間執行、把風險完全消掉。當最好的決策能把風險消到零,決策本身就從公式裡消失了——剩下的只有一個唯一的無套利價格。定價與對沖在 BS 世界是兩件事:公式給價格,delta 給操作。

現實世界不是這樣。你頂多每天(或每小時)重平衡一次,每次交易有成本,於是無論怎麼對沖都留有殘餘風險。殘餘風險不為零,賣方就必須為它收費,而收多少取決於「你打算怎麼對沖」——對沖做得越好,該收的風險加價越少。價格與對沖政策從此綁死在一起:定價問題變成「找出最適對沖政策,然後把這個政策下的期望成本加上風險加價」。這正是強化學習的形狀。本章的第一位主角 QLBS(Q-Learner in the Black-Scholes world),就是把這個觀察徹底執行的模型:用 Q-learning 的框架同時解出選擇權的價格與對沖,而 BS 公式退位成它在「連續重平衡、零風險趨避」極限下的一個角落特例。

本章的路線圖:第二節先把離散時間的對沖組合與殘餘風險算清楚;第三、四節搭出 QLBS 並給出兩條解法;第五節引入本章第二位主角 G-learning,把 Bellman 方程裡的硬決策軟化;第六、七節把工具帶進動態投資組合與退休財富管理;第八節誠實盤點限界。

二、離散時間的 BSM:對沖組合與殘餘風險

先把舞台搭好。時間切成 \(N\) 期,每期長 \(\Delta t\),時點 \(t = 0, 1, \dots, T\)。無風險利率 \(r\),一期折現因子 \(\gamma = e^{-r\Delta t}\)。你是選擇權賣方,手上維持一個對沖組合:持有 \(u_t\) 股標的(股價 \(S_t\))加上現金部位,組合總值記為 \(\Pi_t\)。組合是自融資的:調整持股的錢從現金部位進出,不再額外注資。整理自融資條件,可以得到一條漂亮的逆向遞迴式:

$$ \Pi_t = \gamma\left(\Pi_{t+1} - u_t\,\Delta S_t\right), \qquad \Delta S_t = S_{t+1} - \frac{S_t}{\gamma}, \qquad \Pi_T = H(S_T) $$

逐項拆解:\(\Pi_T = H(S_T)\) 是終端條件——到期那天,組合價值必須剛好等於履約義務 \(H\)(買權就是 \(\max(S_T-K,0)\));\(\Delta S_t\) 是「超額價格變動」——股價實際變動扣掉純粹放無風險利率也會有的增長,它衡量的是持有股票多賺(或多賠)的部分;整條式子說的是:想知道今天組合該值多少(\(\Pi_t\)),就拿下一期的組合價值,扣掉這一期持股 \(u_t\) 貢獻的超額損益,再折現回來。注意方向:從到期日往回走。這個「逆向」正是後面 Bellman 逆向歸納的雛形。

問題來了。這條遞迴對每一條股價路徑都成立,但 \(u_t\) 只能依當下資訊決定、無法預知下一期股價。於是不管 \(u_t\) 怎麼選,倒推回今天的 \(\Pi_0\) 在不同路徑上會得到不同的值——它是一個分布,不是一個數。這個分布的散開程度,就是殘餘風險:離散對沖無論如何消不掉的部分。用模擬看看它有多大、隨對沖頻率怎麼縮:

PYTHON
import math
import numpy as np

rng = np.random.default_rng(10)

# 自創設定:S0=100、K=100、波動 20%、無風險利率 2%、三個月到期
S0, K, sigma, r, T = 100.0, 100.0, 0.20, 0.02, 0.25
mu = 0.05          # 真實世界的股價漂移(跟 r 不同!)
n_paths = 20000

def bs_call(S, K, sigma, r, T):
    """Black-Scholes 歐式買權閉式解(用 math.erf 算常態 CDF)"""
    d1 = (math.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * math.sqrt(T))
    d2 = d1 - sigma * math.sqrt(T)
    N = lambda x: 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))
    return S * N(d1) - K * math.exp(-r * T) * N(d2), N(d1)

def hedge_error(n_steps):
    """離散對沖:每期用 BS delta 重新平衡,量測到期時的殘餘損益標準差"""
    dt = T / n_steps
    S = np.full(n_paths, S0)
    C0, delta = bs_call(S0, K, sigma, r, T)
    pos = np.full(n_paths, delta)
    cash = np.full(n_paths, C0 - delta * S0)    # 收權利金、買 delta 股,剩的放現金
    for i in range(1, n_steps + 1):
        z = rng.standard_normal(n_paths)
        S = S * np.exp((mu - 0.5 * sigma**2) * dt + sigma * math.sqrt(dt) * z)
        cash = cash * math.exp(r * dt)
        tau = T - i * dt
        if tau > 1e-12:
            new_delta = np.array([bs_call(s, K, sigma, r, tau)[1] for s in S])
        else:
            new_delta = (S > K).astype(float)
        cash -= (new_delta - pos) * S           # 自融資:調整部位的錢從現金進出
        pos = new_delta
    payoff = np.maximum(S - K, 0.0)
    pnl = pos * S + cash - payoff               # 對沖組合價值 − 應付的履約金
    return pnl.std()

for n in (3, 13, 63):
    print(f"存續期內重平衡 {n:>3d} 次|殘餘損益標準差 = {hedge_error(n):.4f}")
C0, _ = bs_call(S0, K, sigma, r, T)
print(f"(BS 理論價 = {C0:.4f}:連續對沖的極限下殘餘風險為零)")
# 輸出:
# 存續期內重平衡   3 次|殘餘損益標準差 = 1.8626
# 存續期內重平衡  13 次|殘餘損益標準差 = 0.9360
# 存續期內重平衡  63 次|殘餘損益標準差 = 0.4363
# (BS 理論價 = 4.2322:連續對沖的極限下殘餘風險為零)

三個月只調 3 次(月頻),殘餘損益的標準差高達 1.86——別忘了權利金總共才 4.23,殘餘風險接近權利金的一半。調 13 次(週頻)縮到 0.94,調 63 次(日頻)縮到 0.44:重平衡次數每翻四倍,殘餘風險約砍半,正是 \(1/\sqrt{N}\) 的收縮速度。往極限推,連續重平衡下殘餘風險趨於零——BS 的完美複製只在極限成立,離散世界裡對沖是門手藝,不是公式

比喻: 走鋼索的人靠不停微調平衡桿保持穩定。如果他能連續不斷地調,理論上可以走得紋絲不動——這是 BS 的世界。現在規定他每十秒才准調一次桿:兩次調整之間的晃動就吃定了,風(波動率)越大晃得越兇。晃動消不掉,只能靠調整頻率壓小;而「十秒內累積的晃動」就是離散對沖的殘餘風險。

殘餘風險消不掉,就得為它標價。QLBS 的做法直白:引入風險趨避係數 \(\lambda\),把賣方的要價寫成「期望對沖成本+\(\lambda\) 乘上整段存續期累積的殘餘變異」。\(\lambda\) 是一個把風險換算成錢的匯率——\(\lambda\) 越大,同樣的殘餘風險收越多錢;\(\lambda \to 0\) 時回到純期望成本。這一項不是裝飾:它讓「對沖做得好不好」直接反映在價格上,也讓整個問題有了強化學習需要的「風險調整報酬」。

三、QLBS:把定價寫成一個 Q 函數

現在把第二節的內容正式翻譯成 MDP 四件套。

狀態。 最自然的候選是股價 \(S_t\),但 QLBS 選了一個更聰明的變數:去漂移的對數股價 \(X_t = \log S_t - (\mu - \tfrac{1}{2}\sigma^2)\,t\)。白話:把對數股價裡「時間自然帶來的趨勢」扣掉,只留下純粹的隨機遊走部分。好處很實際——\(X_t\) 的分布不隨時間系統性外移,後面做橫斷面回歸時,每一期的樣本都落在差不多的範圍裡,數值上穩定得多。這是「Black-Scholes 世界的狀態變數」:一維、馬可夫、夠用。

行動。 \(u_t\):這一期持有的對沖股數。連續值,這點跟教科書裡動作離散的迷宮問題不同,但等一下會看到,連續反而是福音。

報酬。 一期的風險調整損益:

$$ R_t = \gamma\, u_t\, \Delta S_t \;-\; \lambda\, \mathrm{Var}_t\!\left[\Pi_t\right] $$

逐項拆解:第一項 \(\gamma u_t \Delta S_t\) 是這一期對沖部位貢獻的超額損益(折現後);第二項是懲罰——\(\mathrm{Var}_t[\Pi_t]\) 是站在 \(t\) 期、當期組合價值還消不掉的條件變異,乘上匯率 \(\lambda\) 變成錢。報酬 = 賺到的錢 − 承擔的風險,這是金融版強化學習最標準的報酬設計。

Bellman 方程。 定義 \(Q_t(x, u)\):在 \(t\) 期、狀態 \(x\)、這期採取行動 \(u\)、之後每期都走最適政策,能拿到的期望累積折現報酬。它滿足第 9 章的 Bellman 最適方程:

$$ Q_t(x, u) = \mathbb{E}_t\!\left[\,R_t \;+\; \gamma \max_{u'} Q_{t+1}(X_{t+1}, u')\,\right], \qquad Q_T(x, u) = -H\!\left(S_T(x)\right) $$

逐項拆解:右邊第一項是「這一期立刻拿到的報酬」;第二項是「下一期起、狀態變成 \(X_{t+1}\) 之後,照最好的方式走能拿到的一切」,折現一次;終端條件說到期時沒有決策可做,只剩付掉履約義務——把遞迴從 \(T\) 望遠鏡式展開,對沖損益逐期累加、履約義務在最後扣除,恰好還原第二節的 \(\Pi_0\) 與風險加價(實作上離散化的狀態格點還會補一項終端殘餘變異的罰款)。跟第 9 章唯一的差別是這裡的 Q 帶時間下標——問題有到期日,是有限期的 MDP,所以每一期有自己的 Q 函數,解法是從 \(T\) 逆向逐期回推。

關鍵的結構性好消息:把報酬的定義代進去展開,會發現 \(R_t\) 對 \(u_t\) 是二次函數,而且開口向下(風險罰款項是 \(u\) 的二次項、係數為負)。二次函數的 max 不用搜尋——頂點有閉式解。於是每一期的最適行動 \(u_t^*(x)\) 直接寫得出來:分子是「下一期組合價值與超額價格變動的條件共變異」再加一個和漂移與 \(\lambda\) 有關的修正項,分母是「超額價格變動的條件變異」。白話:最適對沖 = 用回歸的方式問「下期組合價值跟著股價動多少」,這正是 delta 的資料版定義。而頂點的高度 \(\max_u Q_t(x,u)\),就是「從此以後最好情況下的風險調整成本」——差一個負號就是選擇權在該狀態的要價

比喻: 保險公司幫你的機車報保費時,精算部門其實同時算了兩件事:這張保單該收多少錢,以及公司自己要怎麼分散、再保這筆風險。兩個答案出自同一張精算表——風險管理方式決定保費,保費內含風險管理成本。QLBS 的 Q 函數就是那張精算表:對行動掃一遍,頂點的位置告訴你怎麼對沖,頂點的高度告訴你該收多少。價格與對沖不是兩個問題,是同一張表的兩個座標。

最後補上與經典理論的和解。讓 \(\Delta t \to 0\)(重平衡無限頻繁)且 \(\lambda \to 0\)(殘餘風險反正歸零,不用收費),QLBS 的價格收斂到 BS 公式、最適行動收斂到 BS delta。也就是說 QLBS 不是推翻 BS,而是把 BS 嵌進一個更大的座標系:橫軸是重平衡頻率、縱軸是風險趨避,BS 佔據其中一個角落,真實世界住在座標系的內部。

四、兩條解法:已知模型走 DP,未知模型走 FQI

Q 函數有了,怎麼把它算出來?分岔點在於:你知不知道股價的動態模型?

路線一:知道模型,走動態規劃(DP)。 假設你相信股價是幾何布朗運動(或任何你能模擬的過程)。做法分兩步:先用模型正向模擬出一大批股價路徑;然後逆向逐期回推——在每個時點,把所有路徑當成一個橫斷面,用多項式基底對狀態 \(X_t\) 做回歸,估計出最適行動公式裡需要的各個條件期望,代入閉式解得到 \(u_t^*\),再用自融資遞迴把 \(\Pi\) 往回推一期。走完 \(T\) 期回到今天,\(\Pi_0\) 的平均是純對沖成本,加上 \(\lambda\) 乘累積殘餘變異就是要價。這套「模擬+橫斷面回歸+逆向歸納」的組合拳,實跑起來像這樣:

PYTHON
import math
import numpy as np

rng = np.random.default_rng(1712)   # 向 arXiv:1712.04609 致意

S0, K, sigma, r, T = 100.0, 100.0, 0.20, 0.02, 0.25
mu = 0.05                            # 真實世界漂移,故意不等於 r
n_steps, n_paths = 24, 20000
dt = T / n_steps
gamma = math.exp(-r * dt)            # 一期折現因子

# 第一步:在真實世界測度下模擬股價路徑(機器只會看到這些「資料」)
z = rng.standard_normal((n_paths, n_steps))
logS = np.cumsum((mu - 0.5 * sigma**2) * dt + sigma * math.sqrt(dt) * z, axis=1)
S = S0 * np.exp(np.hstack([np.zeros((n_paths, 1)), logS]))   # (paths, steps+1)

# 狀態變數:去漂移的對數股價(QLBS 的 X_t),讓基底回歸更穩定
t_grid = np.arange(n_steps + 1) * dt
X = np.log(S) - (mu - 0.5 * sigma**2) * t_grid

def regress(x, y, deg=3):
    """橫斷面多項式回歸:估計 E[y | X_t = x],同一時點所有路徑一起回歸"""
    if x.std() < 1e-10:                  # t=0 所有路徑同一狀態:條件期望=全體平均
        return np.full_like(y, y.mean())
    xs = (x - x.mean()) / x.std()        # 標準化,避免高次項病態
    c = np.polyfit(xs, y, deg)
    return np.polyval(c, xs)

# 第二步:從到期日逆向走回今天(Bellman 逆向歸納)
Pi = np.maximum(S[:, -1] - K, 0.0)          # 終端條件:組合價值 = 買權履約義務
hedge_t = np.zeros((n_paths, n_steps))      # 存每期的最適對沖部位
var_sum = 0.0                                # 累積各期殘餘風險(算風險加價用)
for t in range(n_steps - 1, -1, -1):
    dS = S[:, t + 1] - S[:, t] / gamma      # 超額價格變動(扣掉無風險增長)
    # 最適對沖 = 條件回歸版的 Cov(下期組合, dS) / Var(dS)
    num = regress(X[:, t], dS * Pi) - regress(X[:, t], dS) * regress(X[:, t], Pi)
    den = regress(X[:, t], dS**2) - regress(X[:, t], dS) ** 2
    u = num / np.maximum(den, 1e-12)
    hedge_t[:, t] = u
    Pi = gamma * (Pi - u * dS)              # 自融資逆向遞迴
    resid = Pi - regress(X[:, t], Pi)       # 這一期消不掉的殘餘損益
    var_sum += gamma ** (2 * t) * np.mean(resid**2)

price_hedge = Pi.mean()                      # λ→0:純對沖成本
for lam in (0.0, 0.001, 0.01):
    print(f"λ = {lam:<6}|QLBS 要價 = {price_hedge + lam * var_sum:.4f}")

# 對照組一:BS 閉式解
d1 = (math.log(S0 / K) + (r + 0.5 * sigma**2) * T) / (sigma * math.sqrt(T))
d2 = d1 - sigma * math.sqrt(T)
N = lambda x: 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))
print(f"BS 閉式解 = {S0 * N(d1) - K * math.exp(-r * T) * N(d2):.4f}")

# 對照組二:天真的「預測式定價」——直接折現真實世界的期望報酬
naive = math.exp(-r * T) * np.maximum(S[:, -1] - K, 0.0).mean()
print(f"天真折現期望值(μ=5% 的預測式定價)= {naive:.4f}  <- 高估!")

# 今天的最適對沖 vs BS delta
print(f"t=0 平均最適對沖 = {hedge_t[:, 0].mean():.4f}|BS delta = {N(d1):.4f}")
# 輸出:
# λ = 0.0   |QLBS 要價 = 4.2364
# λ = 0.001 |QLBS 要價 = 4.2603
# λ = 0.01  |QLBS 要價 = 4.4760
# BS 閉式解 = 4.2322
# 天真折現期望值(μ=5% 的預測式定價)= 4.5866  <- 高估!
# t=0 平均最適對沖 = 0.5419|BS delta = 0.5398

三個對照讀出三課。第一,\(\lambda = 0\) 的 QLBS 要價 4.2364 對 BS 閉式解 4.2322,差距 0.1%——注意我們模擬用的是真實世界漂移 \(\mu = 5\%\),整個過程沒有做任何「風險中性測度轉換」,但最適對沖自動把漂移的影響對沖掉了,價格落回無套利水位;同時 t=0 的最適對沖 0.5419 幾乎就是 BS delta 0.5398。第二,\(\lambda\) 從 0 升到 0.01,要價從 4.2364 一路墊高到 4.4760——風險趨避越強、為殘餘風險收的加價越多,價格從一個點變成一條隨 \(\lambda\) 移動的曲線,這是離散世界的誠實。第三課最重要:如果有人「預測」股價(用 \(\mu = 5\%\) 的真實分布)然後直接折現期望履約金,會得到 4.5866——高估了 8%。定價不是預測遊戲,是對沖成本的核算;你能用 5% 的漂移賺錢是你的投機本事,不該把它算進賣別人選擇權的價格裡。

(一個技術注腳:完整的 QLBS 最適行動除了上面程式裡的「回歸對沖」項,還有一個與 \(\mathbb{E}[\Delta S]/\lambda\) 成正比的投機修正項——風險趨避越弱,越想順著漂移多押一點。本例省略它,等價於站在高風險趨避的純對沖立場;當 \(\mu = r\) 或 \(\Delta t \to 0\) 時該項自動消失。)

路線二:不知道模型,走 Fitted Q Iteration(FQI)。 上面的做法有個奢侈前提:你知道資料是 GBM 模擬的、知道 \(\mu\) 和 \(\sigma\)。真實櫃檯手上有的只是歷史:一批四元組 \((X_t, u_t, R_t, X_{t+1})\)——當時的狀態、當時實際做的對沖、實際實現的報酬、下一步的狀態。FQI 的做法:把每一期的 Q 函數參數化成一組基底函數的線性組合(係數是待學參數),從最後一期開始,逐期用最小平方法把 Q 擬合到 Bellman 目標「\(R_t + \gamma \max_{u'} Q_{t+1}\)」上——因為 Q 對 \(u\) 是二次的,max 仍有閉式解,整條管線只是一串回歸。跑完之後,價格與對沖政策純粹從資料裡長出來:不需要假設 GBM、不需要知道漂移與波動率、甚至不需要假設任何參數式的分布。這就是「Q-Learner 進了 Black-Scholes 世界」的完整意涵:模型已知時它重現理論價,模型未知時它直接向資料學。

五、G-learning:把「硬 max」軟化

QLBS 用的還是第 9 章的標準 Q-learning,核心運算是 Bellman 方程裡那個 \(\max\)。這個 max 有兩個職業病,金融資料會把兩個都放大。

第一是贏家詛咒。實務上 Q 永遠是估出來的,帶雜訊。對一排帶雜訊的估計值取 max,你挑到的往往不是「真的最好」的行動,而是「被雜訊高估最多」的行動——所以 max 出來的數字系統性偏高。這個高估不是一次性的:Bellman 逆向歸納每一期都取一次 max,偏誤逐期往回累積。金融資料樣本短、訊噪比低,正是雜訊最大、詛咒最靈的場域。第二是過早定型。max 產生的是確定性政策——同一狀態永遠做同一動作。學習過程中就把政策收死,等於放棄探索:你再也不會嘗試那些「目前估計第二好、但可能其實是第一好」的行動,估錯了也沒有機會發現。

G-learning 的解法:別把 max 取得那麼硬。具體做法是在目標函數裡加一條熵正則化罰款——政策 \(\pi\) 每偏離一個參考政策 \(\pi_0\),就按 KL 散度收費,費率是 \(1/\beta\):

$$ F_t(s) = \max_{\pi}\; \mathbb{E}_{\pi}\!\left[\sum_{t'=t}^{T} \gamma^{\,t'-t}\Big(R_{t'} - \tfrac{1}{\beta}\, \mathrm{KL}\big(\pi(\cdot\mid s_{t'})\,\|\,\pi_0(\cdot\mid s_{t'})\big)\Big)\right] $$

逐項拆解:\(F_t(s)\) 叫自由能(借物理的名字),是熵正則化版的狀態價值函數;\(\mathrm{KL}(\pi\|\pi_0)\) 量「你的政策偏離參考政策多遠」(第 1 章講過:KL 散度是兩個分布之間的資訊距離);\(\beta\) 是溫度的倒數——\(\beta\) 越大罰越輕、越敢偏離,\(\beta\) 越小罰越重、越貼著參考政策走。這個帶罰款的最佳化問題有漂亮的閉式解。定義 \(G_t(s,a)\)(G 函數,熵正則化版的 Q 函數):這期做 \(a\)、之後照最適軟政策走的價值。則:

$$ F_t(s) = \frac{1}{\beta}\,\log \sum_{a} \pi_0(a\mid s)\, e^{\beta\, G_t(s,a)}, \qquad \pi^*(a\mid s) = \frac{\pi_0(a\mid s)\, e^{\beta\, G_t(s,a)}}{\sum_{a'} \pi_0(a'\mid s)\, e^{\beta\, G_t(s,a')}} $$

逐項拆解:第一條是 log-sum-exp——軟化版的 max。\(\beta \to \infty\) 時,總和被最大那項獨佔,\(F \to \max_a G\),回到標準 Q-learning 的硬 max;\(\beta \to 0\) 時,展開對數會發現 \(F \to \mathbb{E}_{\pi_0}[G]\)——完全不優化,照參考政策的期望走。第二條是最適政策:不是全押最好的行動,而是按「參考權重 × 價值的指數」分配機率——每個行動都保留一點機會,價值越高機會越大,\(\beta\) 控制集中程度。G-learning 就是把 Q-learning 的「max + argmax」整組換成「log-sum-exp + softmax」,其餘 Bellman 逆向歸納的骨架原封不動;同一套邏輯也有一個以 F 函數為主角的等價表述,叫 F-learning——G 與 F 互為表裡,一個對「狀態+行動」記帳、一個對「狀態」記帳。

用數字看軟化長什麼樣、以及它怎麼對抗贏家詛咒:

PYTHON
import numpy as np

rng = np.random.default_rng(2020)

# 五個候選行動(例如五檔調倉方案)的「真實」長期價值
G_true = np.array([1.00, 0.95, 0.70, 0.40, 0.10])
prior = np.full(5, 0.2)                     # 參考政策:均勻分布(沒有偏好)

def soft_policy(G, beta):
    """π(a) ∝ π₀(a)·exp(β·G(a)):熵正則化的最適政策"""
    w = prior * np.exp(beta * (G - G.max()))   # 減 max 防溢位
    return w / w.sum()

def free_energy(G, beta):
    """F = (1/β)·log Σ π₀·exp(βG):軟化版的 max"""
    return G.max() + np.log(np.sum(prior * np.exp(beta * (G - G.max())))) / beta

for beta in (0.5, 2.0, 10.0, 100.0):
    p = soft_policy(G_true, beta)
    print(f"β = {beta:>5.1f}|π = {np.round(p, 3)}|F = {free_energy(G_true, beta):.3f}")
print(f"硬 max(β→∞ 極限):全押 A1,價值 = {G_true.max():.3f}")

# G 只能從資料「估計」:雜訊之下,硬 max 系統性高估自己(贏家詛咒)
n_trial, noise = 10000, 0.10
G_hat = G_true + rng.normal(0.0, noise, size=(n_trial, 5))
hard_est  = G_hat.max(axis=1).mean()                      # 硬 max 自以為拿到的價值
hard_real = G_true[G_hat.argmax(axis=1)].mean()           # 實際拿到的真實價值
soft_est  = np.mean([free_energy(g, 10.0) for g in G_hat])
soft_real = np.mean([soft_policy(g, 10.0) @ G_true for g in G_hat])
print(f"硬 max:自估 {hard_est:.4f}、實得 {hard_real:.4f}、高估 {hard_est-hard_real:+.4f}")
print(f"軟 max:自估 {soft_est:.4f}、實得 {soft_real:.4f}、高估 {soft_est-soft_real:+.4f}")
print(f"硬 max 押錯首選的比率:{(G_hat.argmax(axis=1) != 0).mean():.1%}")
# 輸出:
# β =   0.5|π = [0.237 0.231 0.204 0.176 0.151]|F = 0.658
# β =   2.0|π = [0.342 0.31  0.188 0.103 0.057]|F = 0.731
# β =  10.0|π = [0.603 0.366 0.03  0.001 0.   ]|F = 0.890
# β = 100.0|π = [0.993 0.007 0.    0.    0.   ]|F = 0.984
# 硬 max(β→∞ 極限):全押 A1,價值 = 1.000
# 硬 max:自估 1.0356、實得 0.9801、高估 +0.0555
# 軟 max:自估 0.9124、實得 0.9647、高估 -0.0522
# 硬 max 押錯首選的比率:36.7%

上半段:\(\beta = 0.5\) 時政策幾乎躺平在均勻參考分布上;\(\beta = 2\) 開始向好行動傾斜;\(\beta = 10\) 明顯集中但前兩名都保有機率;\(\beta = 100\) 已經接近全押。溫度參數就這樣在「完全不信自己的估計」與「完全相信」之間連續滑動。下半段是重點:當估計帶 \(\sigma = 0.1\) 的雜訊,硬 max 有 36.7% 的機率押錯首選,而且它自以為拿到 1.0356、實際只拿到 0.9801——過度自信 0.055,這份虛胖會被逆向歸納一路放大。軟 max(\(\beta=10\))的自估 0.9124 反而低於實得 0.9647:它是保守的,寧可低報也不虛胖。在「估計必然帶雜訊」的金融環境裡,一個自估保守的演算法比一個自估虛胖的演算法安全得多。

比喻: 你到一個陌生夜市,每攤只吃過一兩次,心裡的評分表雜訊很大。「每次都吃目前評分最高那攤」是硬 max:太早定型,而且你的第一名很可能只是運氣好被高估。聰明的吃法是按評分分配光顧比例——最高分常去、第二名也給機會,隨著吃的次數變多、評分變準,再慢慢收斂到真愛。\(\beta\) 就是你對自己評分表的信任度:剛來的第一週 \(\beta\) 開小一點,吃遍全場之後再開大。

比喻: 參考政策 \(\pi_0\) 像公司的 SOP:新來的交易員可以偏離 SOP 操作,但偏離越大、要寫的簽呈越厚,除非預期效益明顯划算。\(\beta\) 小等於公司管得嚴——大家的操作都貼著 SOP;\(\beta\) 大等於放手讓你發揮。這不只是比喻:資產管理實務裡「偏離基準配置要付追蹤誤差預算」的管理方式,數學形狀跟 KL 罰款一模一樣——G-learning 把業界早就在做的事,寫成了可以逆向歸納求解的方程。

六、動態投資組合:多期、市場衝擊與 LQR 極限

換一個舞台。第 1 章預告過的投資組合問題,教科書入門版是單期 Markowitz:給定各資產的期望報酬與共變異,解一次「期望報酬 − 風險罰款」的二次規劃,得到最適權重。但真實的資產管理是多期的:今天的交易決定明天的持倉,明天的持倉是明天決策的起點。能不能把單期解重複套 N 次了事?

不能,理由有二。第一,交易有慣性成本。調倉要付手續費與買賣價差,成本大致隨交易量的平方長(大單得越切越深入報價簿)。這讓「今天的部位」成為狀態的一部分:同一個目標部位,從近處調過去便宜、從遠處調過去貴,單期問題從此串成一條鏈。第二,也是更深刻的:市場衝擊。你的大單會把價格推向對自己不利的方向,而且推動的價格會影響之後所有期的報酬——你的行動改變了環境的動態。這正是第 1 章說過「監督式學習只能旁觀、強化學習必須下場」的那道分水嶺;把衝擊項寫進狀態轉移方程,動態就從線性變成非線性,一般情況只能數值解。

好消息是存在一個完全可解析的極限:零摩擦。把市場衝擊關掉,狀態轉移是線性的(部位與財富的演化是行動的線性函數)、報酬是二次的(期望報酬減風險罰款減交易成本,全是一二次項)——這正是控制理論裡的經典題型 LQR(線性二次調節器),Bellman 逆向歸納逐期都有閉式解。再疊上第五節的熵正則化:參考政策取高斯,二次報酬配高斯先驗,softmax 出來的最適政策仍是高斯——均值往高價值方向移、變異數由 \(\beta\) 控制,每一期都能解析地往回推。這就是 G-learning 在投資組合問題上「半解析可解」的原因:零摩擦時整條是公式,有摩擦時再用數值方法在公式附近修正。市場衝擊、多期、隨機政策三件事同框,是這套框架相對於單期 Markowitz 的真正增量——作業三會讓你在一個兩期玩具上親手摸到這一切。

七、財富管理:從 Merton 到退休金 glide path

多期投資最貼身的應用是退休。先看理論的起點:Merton 消費問題——一個人一生要同時決定「花多少」與「剩下的錢裡股票放多少」,在連續時間、CRRA 效用(相對風險趨避固定)之下最大化終身效用。這個 1969 年的問題有個著名的閉式解,其中投資部分出奇地簡潔:股票的最適比例是一個常數 \(\pi^* = (\mu - r)/(\eta\sigma^2)\)——股票超額報酬除以(風險趨避係數乘變異數),與你多有錢無關、與你幾歲也無關。白話:如果你的風險胃納不隨財富改變,那麼不管帳戶裡是十萬還是一千萬、不管離退休還有三十年還是三年,股票都該佔同一個比例。

這跟你在銀行聽到的建議完全相反。目標日期基金(例如「2050 退休基金」)賣的是 glide path——飛機降落的下滑道:年輕時股票九成,隨年齡逐步降到兩三成。誰對?兩個都對,因為問的不是同一題。Merton 的世界裡只有金融財富;真實的退休人有人力資本(未來薪水是一檔類債券的隱形資產,年輕時佔比極高)、有逐年的提撥現金流、有一個明確的目標財富。把這些放進問題,glide path 就不是行銷話術,而是動態規劃的自然解。用一個確定提撥制的玩具模型驗證——台灣讀者可以想像勞退帳戶自提的情境:每年固定提撥、三十年後希望帳戶達到目標水位,報酬函數用「離目標越遠罰越重」的二次懲罰(這正是目標式財富管理 G-learning 文獻的報酬設計):

PYTHON
import numpy as np

# 確定提撥退休帳戶:每年提撥 1 單位,工作 30 年,目標終端財富 W* = 60
# 行動 = 每年的股票配置比例 x ∈ [0, 1];報酬 = 不要偏離目標(二次懲罰)
T_years, W_star, contrib = 30, 60.0, 1.0
rf, mu_e, sig_e = 0.01, 0.055, 0.16          # 現金報酬、股票超額期望與波動(自創數字)

# 高斯報酬用 7 點 Gauss-Hermite 求積分(純 numpy)
gh_x, gh_w = np.polynomial.hermite_e.hermegauss(7)
gh_w = gh_w / gh_w.sum()

W_grid = np.linspace(0.0, 120.0, 241)         # 財富格點
x_grid = np.linspace(0.0, 1.0, 21)            # 股票比例格點
V = -(W_grid - W_star) ** 2                   # 終端價值:離目標越遠罰越重

policy = np.zeros((T_years, W_grid.size))
for t in range(T_years - 1, -1, -1):          # Bellman 逆向歸納
    Q = np.empty((x_grid.size, W_grid.size))
    for i, x in enumerate(x_grid):
        growth = 1.0 + rf + x * (mu_e + sig_e * gh_x)      # 7 種報酬情境
        W_next = np.clip((W_grid[None, :] + contrib) * growth[:, None], 0.0, 120.0)
        Q[i] = gh_w @ np.interp(W_next, W_grid, V)          # 期望下期價值
    policy[t] = x_grid[Q.argmax(axis=0)]
    V = Q.max(axis=0)

# 沿「期望路徑」讀出 glide path:每年財富照最適政策的期望報酬滾動
W, path = 0.0, []
for t in range(T_years):
    x = float(np.interp(W, W_grid, policy[t]))
    path.append(x)
    W = (W + contrib) * (1.0 + rf + x * mu_e)
for t in (0, 4, 9, 14, 19, 24, 29):
    print(f"第 {t+1:>2d} 年|股票比例 = {path[t]:.2f}")
print(f"期望路徑的終端財富 = {W:.1f}(目標 {W_star})")
# 輸出:
# 第  1 年|股票比例 = 1.00
# 第  5 年|股票比例 = 1.00
# 第 10 年|股票比例 = 1.00
# 第 15 年|股票比例 = 0.75
# 第 20 年|股票比例 = 0.45
# 第 25 年|股票比例 = 0.25
# 第 30 年|股票比例 = 0.11
# 期望路徑的終端財富 = 56.8(目標 60.0)

我們沒有在任何地方寫死「年紀大要保守」——只給了目標、提撥與二次懲罰,然後跑 Bellman 逆向歸納。跑出來的政策卻是教科書級的 glide path:前十一年股票全開(財富離目標還遠,錯過成長的代價大於波動的代價),第十二年起逐年調降,最後一年只剩 11%(目標在望,波動只會把煮熟的鴨子弄飛)。glide path 是「目標+現金流+逆向歸納」的湧現性質,不是理專的話術,也不與 Merton 矛盾——把目標與現金流從問題裡拿掉,解就退回 Merton 的固定比例。

真實世界的退休優化當然比玩具難:多資產、稅制、提撥規則隨薪資變動、中途可能領出、目標本身有彈性。狀態一高維,格點法立刻爆炸。這正是 G-learning 進場的地方:二次型報酬+高斯參考政策讓每期更新保持半解析,維度增長時計算量的膨脹遠比格點法溫和,同時隨機政策天生輸出「配置的分布」而非單點建議——對必須解釋不確定性的財富管理場景,這是特色不是缺陷。

八、誠實限界:這套方法什麼時候會騙你

本章的方法優雅,但把它搬進生產環境之前,四個限界必須攤開。

資料需求與模擬器依賴。 FQI 號稱純資料定價,但一檔標的的真實歷史只有一條路徑;橫斷面回歸需要的是「同一時點的大量平行樣本」,實務上多半仍靠模擬器生成——於是模型風險並沒有消失,只是從「定價公式假設」搬家到「模擬器假設」。誠實的說法是:QLBS 把對模型的依賴顯性化、模組化了,沒有把它變不見。

報酬設計的主觀性。 \(\lambda\)(風險換錢的匯率)、\(\beta\)(對估計的信任度)、目標財富 \(W^*\)——這些不是能從資料估計的參數,是價值觀參數。換一組數字,最適政策跟著變。這無可避免(風險偏好本來就因人而異),危險的是假裝它們客觀:報告一個 RL 定價結果而不報告它對 \(\lambda\) 的敏感度,跟報告一個 DCF 估值而不報告折現率假設一樣,是專業上的失格。

離線強化學習的分布外風險。 FQI 從歷史資料學 Q,但歷史資料只覆蓋「當時的政策去過的狀態—行動組合」。對資料沒去過的區域,Q 的值是基底函數外推出來的幻覺,而 max 運算恰恰最愛挑幻覺最美的那個行動——雜訊高估與外推誤差在這裡疊加。第五節的軟化是部分解方(保守自估、貼著參考政策),但根本的紀律是:只信資料覆蓋範圍內的政策,部署時給行動空間加硬邊界。

非平穩性。 整套框架假設狀態轉移的規律在訓練與部署期間不變。金融市場會換制度——第 1 章房貸模型在 2008 年的教訓原封不動地適用於此,而且 RL 的閉環性質讓它更凶險:政策影響市場、市場再影響政策。低利率年代訓練的退休配置政策,搬到高利率年代直接失效。監控輸入分布、定期重訓、保留人工覆核,不是可選項。

這些限界不是勸退。選擇權對沖與目標式財富管理恰好是金融裡環境相對可控(合約規則明確、報酬可驗證)的兩個角落,這正是它們成為 RL 招牌應用的原因。知道邊界在哪,才配得上把工具用在邊界之內。

§03原書對照

本課依原書草稿目錄重組了第十章的骨架(原書 PDF 無本章正文可核,以下依草稿目錄整理,無出版頁碼可標;可查證錨點為對應的 arXiv 論文)。依前言定位,該章一方面示範強化學習在金融的實戰應用,一方面順勢推進前一章的理論——開場點名的正是量化金融最常見的問題族:離散時間的最適組合交易,並指出許多交易與風險管理問題都是動態組合優化在不同準則、組成與約束下的變形。全章分九節:導論之後,§2 綜述 QLBS 模型,§3 處理離散時間 BSM——含對沖組合評價、最適對沖、離散定價與 BS 極限四小節,本課三、四節即其白話重組,但原書含完整的極限收斂推導。§4 是 QLBS 主體,依序為狀態變數、Bellman 方程、最適政策、DP 的蒙地卡羅實作、FQI 解法、數值例——與本課骨架一致;其中「數值例」「選擇權組合(option portfolios)」與「可能的延伸」三小節本課未展開:數值例展示不同 \(\lambda\) 與模擬設定下的收斂行為,選擇權組合處理多檔選擇權同時對沖時的 QLBS 推廣,延伸小節討論放寬狀態變數與報酬設定的方向,對應論文為 arXiv:1712.04609 及其後續 arXiv:1801.06077。§5 以十六個小節鋪陳 G-learning 股票組合:投資組合設定、終端條件、資產報酬模型、訊號動態與狀態空間、單期報酬、多期優化、隨機政策、參考政策、Bellman 最適方程、熵正則化 Bellman、G 函數、G-learning 與 F-learning、含市場衝擊的組合動態、零摩擦 LQR 極限、非零衝擊的非線性動態——本課第五、六節只取其概念主幹,原書以矩陣形式給出完整的半解析遞迴,對應論文為 arXiv:2002.10990。草稿前言對這條線的定位是「amounts to a probabilistic version of Linear Quadratic Regulators (LQR)」(依原書草稿目錄前言)。§6 RL 財富管理含 Merton 消費問題的完整推導(本課僅給結論與直覺)、確定提撥退休計畫的正式設定、G-learning 退休優化與討論——本課第七節的玩具模型是其極簡自創版,原書處理多資產與現金流的一般情形。§7–9 為總結、習題與文獻。整體而言,本課補進了原書該章沒有的離散對沖數值實驗與贏家詛咒模擬,略去了矩陣形式的嚴格推導與選擇權組合推廣;想追完整數學的讀者,兩篇 arXiv 論文是與原書內容最接近的公開替代品。

§04作業和解答

作業一:二項樹逆向歸納 vs QLBS 蒙地卡羅

用 CRR 二項樹為第四節同一口買權定價(\(S_0=100\)、\(K=100\)、\(\sigma=0.2\)、\(r=0.02\)、\(T=0.25\)):(1) 分別取 \(N=24, 200, 2000\) 期,觀察價格收斂到哪裡;(2) 比較 \(N=24\) 的二項樹價格與本章 24 期 QLBS 的 4.2364,解釋兩者誤差來源有何不同;(3) 說明二項樹逆向歸納與 QLBS 的 DP 解法在方法論上是什麼關係。

解答 SOLUTION
PYTHON
import math
import numpy as np

S0, K, sigma, r, T = 100.0, 100.0, 0.20, 0.02, 0.25
for N in (24, 200, 2000):
    dt = T / N
    u = math.exp(sigma * math.sqrt(dt)); d = 1.0 / u
    q = (math.exp(r * dt) - d) / (u - d)          # 風險中性上漲機率
    disc = math.exp(-r * dt)
    S_T = S0 * u ** np.arange(N, -1, -1) * d ** np.arange(0, N + 1)
    V = np.maximum(S_T - K, 0.0)
    for _ in range(N):                             # 逐期逆向歸納
        V = disc * (q * V[:-1] + (1 - q) * V[1:])
    print(f"N={N:>4d} 期二項樹價格 = {V[0]:.4f}")
# 輸出:
# N=  24 期二項樹價格 = 4.1909
# N= 200 期二項樹價格 = 4.2272
# N=2000 期二項樹價格 = 4.2317

(1) 價格隨 \(N\) 收斂到 BS 閉式解 4.2322:24 期差 0.041,200 期差 0.005,2000 期已到小數第三位。(2) 兩者誤差性質不同:二項樹的誤差是離散化偏差——用 24 步的二叉格子近似連續的對數常態分布,是系統性的、確定性的,加密格子就單調消失;QLBS 蒙地卡羅的 4.2364 誤差來自抽樣雜訊+回歸偏差——路徑有限、基底函數階數有限,是隨機的,加路徑與換基底才會縮小。同樣是「差 BS 一點點」,一個差在骨架、一個差在肌肉。(3) 方法論上兩者是同一件事的兩個端點:都是 Bellman 逆向歸納。二項樹是「模型完全已知且被離散成有限狀態」的 DP——每個節點的條件期望能精確算出;QLBS 的 DP 解是「模型已知但狀態連續」的版本——條件期望改用橫斷面回歸估計;FQI 再往前一步,連模型都不要了。從二項樹到 FQI,是同一條逆向歸納骨架上,「對模型的依賴」逐步讓位給「對資料的依賴」的光譜。

作業二:溫度參數 \(\beta\) 的兩個極限與單調性

沿用第五節的五行動例子(\(G = (1.00, 0.95, 0.70, 0.40, 0.10)\),均勻參考政策)。(1) 數值驗證:自由能 \(F(\beta)\) 隨 \(\beta\) 單調上升,且被夾在 \(\mathbb{E}_{\pi_0}[G]\) 與 \(\max G\) 之間;(2) 給出單調性的理論理由;(3) 用一句話說明兩個極限各對應什麼樣的決策者。

解答 SOLUTION
PYTHON
import numpy as np

G = np.array([1.00, 0.95, 0.70, 0.40, 0.10])
prior = np.full(5, 0.2)

def F(beta):
    return G.max() + np.log(np.sum(prior * np.exp(beta * (G - G.max())))) / beta

vals = [F(b) for b in (0.01, 0.1, 1, 10, 100, 10000)]
print("F(β) 掃描:", [round(v, 4) for v in vals])
print("E_prior[G] =", prior @ G, "| max G =", G.max())
print("單調上升:", all(vals[i] < vals[i + 1] for i in range(len(vals) - 1)))
# 輸出:
# F(β) 掃描: [0.6306, 0.6358, 0.6846, 0.8897, 0.984, 0.9998]
# E_prior[G] = 0.63 | max G = 1.0
# 單調上升: True

(1) 實跑確認:\(F\) 從 \(\beta=0.01\) 的 0.6306(貼著 \(\mathbb{E}_{\pi_0}[G] = 0.63\))單調爬到 \(\beta=10000\) 的 0.9998(貼著 \(\max G = 1\)),全程夾在兩個極限之間。(2) 理論理由用「變分表述」最乾淨:\(F(\beta) = \max_\pi\,\{\mathbb{E}_\pi[G] - \tfrac{1}{\beta}\mathrm{KL}(\pi\|\pi_0)\}\)——自由能是「期望價值減掉偏離罰款」對政策取最大。\(\beta\) 變大等於罰款費率變低:同一個最佳化問題,約束變鬆,最大值不可能變小,所以 \(F\) 對 \(\beta\) 單調不減(用包絡定理可以算出導數恰為 \(\mathrm{KL}(\pi_\beta\|\pi_0)/\beta^2 \ge 0\))。上界是因為罰款非負、\(\mathbb{E}_\pi[G] \le \max G\);下界是取 \(\pi = \pi_0\)(罰款為零)就至少拿到 \(\mathbb{E}_{\pi_0}[G]\)。(3) \(\beta \to 0\) 是「完全不信自己判斷的決策者」——永遠照參考政策(SOP)執行;\(\beta \to \infty\) 是「完全相信自己估計的決策者」——永遠全押帳面最優。實務的智慧在中間,而且應該隨估計品質動態調整:資料越多、估計越準,\(\beta\) 才配開越大。

作業三:兩期組合的 G-learning 玩具解

單一風險資產,期望超額報酬 \(\mu_a = 6\%\)、波動 \(\sigma_a = 18\%\),風險趨避 \(\lambda = 0.5\),調倉有市場衝擊成本 \(\kappa (a - a_{prev})^2\)(\(\kappa = 0.02\))。行動是每期的目標持倉 \(a\)(百萬元計,可做空),單期報酬 \(r(a, a_{prev}) = a\mu_a - \lambda(a\sigma_a)^2 - \kappa(a - a_{prev})^2\),共兩期,起始持倉 0,參考政策取 \(N(0,1)\)。(1) 在行動格點上逆向解出兩期 G-learning 的第一期政策,掃 \(\beta = 1, 5, 50\),觀察政策的均值與標準差怎麼動;(2) 對照單期 Markowitz 閉式解 \(a^* = \mu_a / (2\lambda\sigma_a^2)\),解釋差異從哪來。

解答 SOLUTION
PYTHON
import math
import numpy as np

mu_a, sig_a, lam_r, kappa = 0.06, 0.18, 0.5, 0.02
a_grid = np.linspace(-2.0, 2.0, 81)                  # 行動 = 目標持倉(百萬元)
prior_pi = np.exp(-0.5 * (a_grid / 1.0) ** 2)
prior_pi /= prior_pi.sum()                            # 參考政策 N(0,1) 離散化

def reward(a, a_prev):
    return a * mu_a - lam_r * (a * sig_a) ** 2 - kappa * (a - a_prev) ** 2

for beta in (1.0, 5.0, 50.0):
    # 第 2 期:G2(a_prev, a) = 當期報酬;F2(a_prev) = 對 a 取軟 max
    G2 = reward(a_grid[None, :], a_grid[:, None])     # 列 = a_prev、欄 = a
    m = G2.max(axis=1, keepdims=True)
    F2 = m[:, 0] + np.log((prior_pi[None, :] * np.exp(beta * (G2 - m))).sum(axis=1)) / beta
    # 第 1 期:G1(a) = r(a, 0) + F2(a)(不折現),起始持倉 0
    G1 = reward(a_grid, 0.0) + F2
    pi1 = prior_pi * np.exp(beta * (G1 - G1.max())); pi1 /= pi1.sum()
    mean1 = pi1 @ a_grid
    std1 = math.sqrt(pi1 @ a_grid**2 - mean1**2)
    print(f"β={beta:>5.1f}|第 1 期政策:均值 {mean1:.3f}、標準差 {std1:.3f}"
          f"|G1 硬 max 解 {a_grid[G1.argmax()]:.3f}")
print("單期 Markowitz 閉式解 =", round(mu_a / (2 * lam_r * sig_a ** 2), 3))
# 輸出:
# β=  1.0|第 1 期政策:均值 0.046、標準差 0.858|G1 硬 max 解 0.550
# β=  5.0|第 1 期政策:均值 0.200、標準差 0.766|G1 硬 max 解 0.650
# β= 50.0|第 1 期政策:均值 0.739、標準差 0.412|G1 硬 max 解 0.900
# 單期 Markowitz 閉式解 = 1.852

(1) \(\beta\) 從 1 升到 50,第一期政策的均值從 0.046 →0.200 → 0.739,一路從參考政策的 0 向多期硬 max 解靠攏;標準差從 0.858 → 0.412,政策越來越集中——正是第五節「信任度調大、探索收窄」的完整重演,只是這次發生在連續行動、帶狀態耦合的兩期問題裡(注意 G1 的硬 max 解也隨 \(\beta\) 動,因為第二期的軟價值 F2 本身含 \(\beta\))。(2) 單期 Markowitz 說押 1.852,兩期硬 max 只押 0.9 左右,差近一半。差異來源拆兩層:其一,市場衝擊把「建倉」變貴了——有效的風險罰款從 \(\lambda\sigma_a^2\) 變成 \(\lambda\sigma_a^2 + \kappa\)(0.0162 → 0.0362),光這一項就把單期最適壓到約 0.83;其二,兩期問題裡第一期建的倉第二期還要調整,衝擊成本會再收一次,逆向歸納把未來的調倉成本折進今天的決策。多期優化天生比單期保守:它看得到「今天衝進去,明天還得付錢挪出來」。這是把單期 Markowitz 重複 N 次永遠學不會的事。

§05參考資料