AAD演算法在金融中的應用

AAD演算法在金融中的應用

來自專欄 Jiang的金融窩

很久沒寫文章了,之前一直在忙著找工作和PhD論文的事情,如今都弄得差不多了,算是可以緩口氣了,借著在上海交大開會這段時間,寫寫之前做的一些東西,覺得對實業還是能有很大幫助的。今天的東西看起來比較technical,但其實不難。

說到金融工程,很多人應該都知道Greeks,比如Delta對沖里的Delta,Gamma Scalping里的Gamma,等等。Greeks其實就是衍生品價格對於某個變數的偏導數,是敏感度的一個量化值。如果一個資產組合對於某個變數的敏感度為0,那麼在瞬時間,這個變數的變化,對這個資產組合價值的影響是為0的,也就是說,在哪瞬時,這個變數的風險是不存在的。這裡強調一下是瞬時,是因為對沖是動態的,隨著動態變化,資產組合對某個變數的敏感度可能將不再是0,那麼將會產生價格變化,也就是風險。一個典型例子就是Delta對沖就是dynamic的。

Greeks在金融工程里的應用是十分廣泛的,可以說,你定價一個東西,你就需要計算出它的一部分Greeks,以此為依據來判斷當時的風險。而且隨著變數和價格的變化,我們需要重新的做calibration,重新的定價,重新的算Greeks,這樣的過程在實業中是希望能夠做到real-time的,也就是實時的。對於Black Scholes formula這種的解析解公式來說,Greeks是解析的,可以直接代入變數獲得結果,real-time自然不是問題。然而,如今的很多產品和模型用到的都是沒有解析解的蒙特卡洛模擬定價或數值微分方程定價,對於這類情況,我們又該如何儘可能快的計算出它們的Greeks呢?

最naive的思路,我們使用finite difference的方法來估算,也就是微調一個變數,然後重新計算價格作差,再除以微調量,使用數值微分的方法來挨個計算Greeks。這種方法的優點是簡單,缺點也顯而易見:你有多少的變數,你就需要重新計算多少次定價函數。舉個例子,可能你的資產組合的價值需要跑1小時的蒙特卡洛計算出來,裡邊有20個變數,那麼你需要挨個微調著20個變數,然後跑20次1小時的蒙特卡洛,然後來計算這20個變數的敏感度,總共耗時是21小時。這無疑是大大的增加了計算時間的。在現實中,在歐美的銀行里,計算一個XVA所花費的時間是巨大的(遠比例子里的1小時大),而且裡邊變數的個數也是幾十甚至幾百個,花費如此巨大的時間來佔用計算資源是非常浪費的行為,因此是不可以考慮的。

前幾年在歐美的銀行里流行了一種新的演算法,Adjoint Algorithmic Differentiation,簡稱AAD。這裡剖析一下這個演算法的思路和原理,把它神秘的面紗揭開。

我們考慮一個程序,可能是一個蒙特卡洛求期權價格,也可能是一個PDE的數值解求期權價格,如下:

Y = 	exttt{Function}(X)

其中,因為Y是價格,所有 Y in {mathbb{R}^1} ,而X是模型參數,假設是K維的,也就是 X in mathbb{R}^K 。對於代碼來說,你寫的函數肯定是一步步來執行的,也就是下述過程:

X
ightarrow A 
ightarrow B 
ightarrow cdots 
ightarrow U 
ightarrow V 
ightarrow Y

如果你的這些步驟是Lipschitz 連續的,那麼這些步驟之間的Jacobian matrix就可以是closed form的。比如,如果 U in mathbb{R}^M, V in mathbb{R}^N ,那麼V關於U這個步驟的Jacobian Matrix是可以analytical寫出的,而且是 mathbb{R}^{M	imes N} 的,其中

overline{U}_m = sum_{n=1}^N overline{V}_n frac{partial V_n}{partial U_m}

這裡, overline{U}_m 代表的是最終變數Y對於U向量里第m個元素的偏導數, overline{V}_n 代表的是最終變數Y對於V向量里第n個元素的偏導數,被稱為Adjoint。偏導部分因為Lipschitz 連續,至少可以piecewise求導。而 overline{V}_n 更簡單,直接是

overline{V}_n = frac{partial Y}{partial V_n}

所以,我們可以逆過來求解 overline{X} 向量(也就是我們想求得的敏感度),如下步驟:

overline Xleftarrow overline A leftarrow overline B leftarrow cdots leftarrow overline U leftarrow overline V leftarrow overline Y

其中, overline{Y}=1 。這樣,我們就得到了計算 overline X 的函數

overline X= 	exttt{Function}{	ext{_b}}(Y, X, A, cdots, U, V, Y)

而且這樣的計算也是可以矩陣化的,從而大大節省計算複雜度。牛津大學的Mike Giles教授有一篇文章,詳細的寫了矩陣運算之間的Adjoint計算規則,我這裡列一下結果:

1,矩陣加法:如果正向步驟是由矩陣 C = A + B完成,那麼反向Adjoint滿足 overline A = overline C, overline B= overline C

2,矩陣乘法:如果正向步驟是由矩陣 C = A cdot B完成,那麼反向Adjoint滿足 overline A = overline C cdot B^T, overline B= A^T cdot overline C

3,矩陣逆運算:如果正向步驟是由矩陣 C = A^{-1}完成,那麼反向Adjoint滿足 overline A = - A^{-T} overline C A^{-T} = - C^{-T} overline C C^{-T}

這樣,AAD就也可以使用矩陣化來完成,而不是非要訴諸於循環。

AAD代碼寫起來會挺複雜的,但為什麼還這麼受歡迎呢?因為有一個運算速度的公式:

frac{	ext{TimeCost}(	exttt{Function}	ext{_b})}{	ext{TimeCost}(	exttt{Function})} leq omega_A, qquad 	ext{ where }omega_A in [3, 4]

也就是說,不論X變數的維度是多少,我總是能花最多4倍原先定價函數的時間來得到價格Y關於變數X的所有Greeks。即使X有上千上萬個變數,我所花費的時間不會超過原先定價正向函數的4倍。這就是AAD的強大之處!

現在話說回來,AAD沒有缺點嗎?自然是有,有兩點:

1,AAD未經優化的反向計算會消耗巨大的內存,因為可以看到,正向函數 Y = 	exttt{Function}(X) 只需要一個輸入變數,而反向函數 overline X= 	exttt{Function}{	ext{_b}}(Y, X, A, cdots, U, V, Y) 的輸入變數巨大,而且在運算過程中會出現很多Jacobian matrix,會佔用極大的內存。這個問題其實是可以被解決的,因為反向函數的運算也是線性的,可以在下一個步驟結束時將先前的步驟生成變數所佔用的內存清除掉,這樣可以大大的優化內存消耗。

2,AAD的代碼的library構造所需較大人力物力。眾所周知,對銀行來說,很多框架和模型都有現成的library函數,而AAD這個和XVA一起火起來的演算法,並沒有在先前被寫入library,將嚴格的algorithmic differentiation寫入library相當於把所有的函數的逆向演算法寫一遍,需要極大的人力物力。但據我所知,還是有銀行和機構完成這項壯舉的。而懶惰一點,可以使用operator overloading的方法來做automatic algorithmic differentiation,這樣可以加快寫library的速度,但是,如果這樣做,演算法耗時公式會變成

frac{	ext{TimeCost}(	exttt{Function}	ext{_b})}{	ext{TimeCost}(	exttt{Function})} leq omega_B, qquad 	ext{ where }omega_B approx 9sim15

而且這個式子只是我們在實踐中嘗試的empirical結果,會不會突破15倍我們也不知道。

國內賣方業務還是初期階段,定價函數的相對應AAD library其實可以從一開始就寫入,這樣會在以後國家引入了更多東西(比如XVA)時省下不少計算時間的。


推薦閱讀:

山田的金融日記(0)-INDEX
山田的金融日記(8)-停時應用(1)
結構化產品概述 Structured Products(一)
山田的金融日記(3)-Quadratic Variation
山田的金融日記(7)-LU分解,逆矩陣

TAG:演算法 | 金融數學 | 金融 |