期權定價

加速計算:何時在定價中使用準和標準蒙地卡羅

  • June 3, 2015

我熟悉數值積分中的蒙特卡羅技術理論,最近我開始嘗試將這些方法應用於衍生品定價。我正在使用 Glasserman 的書作為參考。

我首先計算了簡單的 ATM 普通呼叫價格。我的設置非常基本:我有 100 個時間步長,我使用 Black-Scholes 模型(這樣我可以檢查結果的有效性)和 Euler-Maryuama、Runge Kutta 和 Milstein 數值積分方案。看來我需要關於 $ 10^6 $ 樣本以獲得穩定的結果,而在 Python 中大約需要 6 分鐘。我有一台相當快的機器,我發現這個速度真的很低。最初我認為原因是隨機採樣器。但是根據我的檢查,對正常隨機變數進行採樣確實需要一些時間,但是 $ 80% $ 計算時間的一部分來自算術運算(加法和乘法) - 即使在基本的 Euler-Maryuama 方案的情況下也是如此。我確實明白,在幾何布朗運動的情況下,有很多更智能的模擬技術可用,但我想保持它的通用性,因為我最終想使用更通用的擴散。

也許這只是 Python 的一個問題,但這個問題讓我想到了加快我的蒙地卡羅模擬。在格拉瑟曼的書中,有兩種​​超速程序:

  1. 變異數減少,包括控制變數、對立抽樣和重要性抽樣。我以前用過後者,我最好的選擇就是這個。
  2. 準蒙特卡羅(低差異序列)。我以前從未使用過它,現在在我看來,它只能用於以通常的形式計算積分,比如當明確給出密度時,積分的解析公式是未知的。

到目前為止,我的目標是能夠使用 Monte-Carlo 為異國情調的期權定價。比如說,模型是一維的,而奇異的收益是非常依賴於路徑的。也許稍後我還需要為此類選項的美國版本定價。我的問題如下:

  1. 我應該特別注意哪些變異數減少方法?
  2. QMC 是否完全有可能應用於此類問題,或者我真的需要對期權價格有一個積分錶達式(在有限維域上)?

澄清我關於 QMC 的問題。我看到 (Q)MC 可以應用於定價的三種方式。

  • 在實線上對布朗運動/跳躍分量的增量進行採樣
  • 從路徑空間中採樣整個路徑
  • 如果發現該選項的值具有形狀 $$ \tag{1} \int_D p(S_1,\dots,S_n)f(S_1,\dots,S_n);\mathrm dS_1\dots \mathrm dS_n $$ 在哪裡 $ p $ 是一個支付函式, $ S_1,\dots, S_n $ 是收益變數(多隻股票或股票的多次時間測量或其他),那麼 (Q)MC 也可用於從 $ D $ 計算積分 $ (1) $ 數字上。

據我從 Glasserman 的書中了解到,他只認為 QMC 是最後一種方法的一個很好的解決方案,他提供了一些證據證明它優於通常的隨機 MC。相反,對於前兩種方法,他談到了通常的 MC 和變異數減少技術。所以我關於 QMC 的問題是:它能否成功應用於前兩種方法,或者它不是為他們精心設計的,我不期望在前兩種方法中使用 QMC 有太多好處?

根據定義,期權的公允價值由收益的預期值給出, $ \mathbf{E}\left[\textrm{payoff}(\textit{paths})\right] $ . 的機率分佈paths是風險中性度量。這只是您編寫的表格的完整表達。這適用於所有期權價格。當然,許多選項是特殊的,因為它們僅取決於每條路徑的最終值。

因此,準蒙特卡羅總是可以應用在這些情況下,路徑的採樣與計算積分(即期權收益的期望值)本質上是一樣的。Glasserman 的評論很可能是指這樣一個事實,即如果您只是使用 QMC 生成路徑,那麼您可能無法從 QMC 生成的隨機數的低差異中獲得全部好處。換句話說:樣本的差異很小,但生成的路徑可能沒有。所以變異數減少可能不會那麼大。這都是相對的,因為其他變異數減少方法可能會產生更好的結果(即價格估計中的變異數更低)。

現在,您的實際問題與 QMC 沒有任何關係。阻礙你的是 Python。Euler-Maryuama、Runge Kutta 和 Milstein 等方案都是無法矢量化的方案。因此,我懷疑您可能有一些表單的程式碼片段:

import numpy as np
... some definitions ...
for i in range(1, N_timesteps):
   for j in range(N_paths):
       S[i, j] = S[i - 1, j] * np.exp((r-0.5*sigma**2)*dt
                + sigma*np.sqrt(dt) * np.random.randn())

問題是這在 Python 中通常非常慢。你可以做的事情來加快這個速度:

  • 對循環外的所有隨機數進行採樣。
  • 盡可能多地預先計算循環之外的因素。
  • 如果可能,矢量化內部循環。
  • 盡可能避免在循環內呼叫函式。

所以一般來說,我們希望在 for 循環中做的盡可能少,因為這些呼叫在 Python 中非常昂貴。

例如,我們可以將上面的內容寫成:

import numpy as np
... some definitions ...
Z = sigma*np.sqrt(dt) *np.random.randn(N_timesteps -1, N_paths)
drift = (r-0.5*sigma**2)*dt
for i in range(1, N_timesteps):
       S[i] = S[i - 1] * np.exp(drift + Z[i-1])

所以內部循環已經被“矢量化”了,採樣都是在循環之前完成的,等等。

如果這仍然不夠(如果您有很多 for 循環,這很可能),那麼您將需要執行一些更“高級”的優化。這通常歸結為獲取程式碼中最慢的部分(幾乎總是 for 循環),將其放入函式中,然後在 Cython 或 Numba 中編寫此函式。

例如,使用 Numba,我們可以將修飾函式中的 for 循環定義為:

from numba import autojit

@autojit
def my_loop(S, Z, drift):
   for i in range(1, S.shape[0]):
       for j in range(S.shape[1]):
           S[i, j] = S[i - 1, j] * np.exp(drift + Z[i-1, j])

... some definitions ...
Z = sigma*np.sqrt(dt) *np.random.randn(N_timesteps -1, N_paths)
drift = (r-0.5*sigma**2)*dt
my_loop(S, Z, drift)

第一次呼叫 my_loop 時,Numba 將編譯函式(需要半秒)。這導致了一個執行得更快的函式。這是優化程式碼中某些常式的好方法。

但要真正幫助您,我們需要查看一些程式碼!

引用自:https://quant.stackexchange.com/questions/18015