使用 BCHJ (2018) 的 IG 組件 GARCH 模型的定價選項
Bbaoglu、Christoffersen、Heston 和 Jacobs(2018 年)早在 2018 年就引入了具有逆高斯創新和指數二次定價核心的組件 GARCH 模型。這篇文章不應該在付費牆後面,但如果是的話,我可以嘗試在其他地方找到它.
此型號的定價非常方便。與 Heston 和 Nandi (2000) 中一樣,該模型承認指數仿射矩生成函式,因此我們有一個準解析公式。作為參考,我們這裡的相關頁面是:
- 第 193-194 頁:它們給出了定價公式以及通常的遞歸(我放棄在這裡寫它們,因為它們很長,我不想冒險犯錯誤);
- 第 202 頁:您有一組 IG-GARCH(C) 模型的參數,可用於檢查函式是否有意義。
現在,我用 Python 編寫了定價公式,但它的行為似乎不正常。具體來說,例如,我知道我知道適用於 Heston 和 Nandi (2000) 模型的函式。我從 Christoffersen 的網站得到它們,將它們翻譯成 Python,然後我將 MATLAB 輸出匹配到小數點後的第 9 位或第 10 位。所以,我選擇了一個例子:
- 無風險利率 = 0.05/365
- 標的資產的目前價格 = 100
- 執行價格 = 100
- 初始波動率 = 0.21^2/365
- 到期天數 = 60 天
對於 Heston 和 Nandi (2000),該期權價值3.7778美元;對於 Black-Scholes-Merton,它值3.3968美元。
所以,我期待在那些水域中發生一些事情。但是,使用第 202 頁中風險中性過程的參數,並初始化兩個波動率過程( $ q_t $ 和 $ h_t $ 在模型中)在 0.21^2/365 時,我得到了 47 美元以上,這顯然是胡說八道。這可能是一個編碼錯誤,但也可能是我看不到的縮放問題,或者可能是我輸入了錯誤的參數……我一直在檢查,但我的鼻子卡住了,我只是不看不出問題出在哪裡。
我選擇在此處發布此內容是因為我需要對期權定價足夠熟悉的人來查看問題所在,如果這不僅僅是編碼錯誤的話。從好的方面來說,如果我們在這裡修復範常式式碼,論壇上的每個人都將享受到具有非常酷特性的最先進模型的開原始碼。請注意,我評論了另一種僅使用一個積分進行定價的方法。暫時沒關係。我的 Python 程式碼:
import numpy as np from numpy import sqrt, exp, log from scipy.integrate import quad # BCHJ2018, p.20 # mu_t,wq,rho1,ah,ch,rho2,aq,cq,eta param = [-0.5, 2.415e-6, 0.745, 1.033e6, 9.682e-7, 0.989, 4.911e7, 4.660e-6, -5.399e-4] # To try the functions BSvol = 0.21 qt = BSvol**2/365 ht = BSvol**2/365 St = 100 K = 100 tau = 60 rF = 0.05/365 #==========================================================================# def CF_IG_GARCH_C(u,St,rF,ht,qt,tau,param): ''' Author: Stephane Surprenant, UQAM Creation: 14/03/2020 Description: This function provides the generating function used in the valuation of European call options for the IG-GARCH(C) model of Babaoglu, Christoffersen, Heston and Jacobs (2018). INPUTS DESCRIPTION u : (float) Value over which the CGF is integrated St: (float) Stock/index level at time t ht: (float) Daily variance in the 2nd period (t+1) (Vol.daily = Vol.yearly^2/365) qt: (float) Daily long term variance in the 2nd period (t+1) tau: (int) Time to maturity (days) r : (float) Daily risk-free rate (rf.daily = rf.yearly/365) param: (float) Array: [mu_t,wq,rho1,ah,ch,rho2,aq,cq,eta] Note: Those are the risk-neutral component model risk-neutral parameters. References: See Babaoglu, Christoffersen, Heston and Jacobs (2018). REQUIRES: numpy (import sqrt, log, exp) ''' # Assign parameter values mu_t,wq,rho1,ah,ch,rho2,aq,cq,eta = param mu = mu_t - eta**(-1) # (p.11, top) # Complex argument #u1 = u*1j u1 = u T = tau # Matrices for the recursion (impose A(T)=B(T)=0) Amat = np.zeros(shape=(T), dtype=complex) Bmat = np.zeros(shape=(T), dtype=complex) Cmat = np.zeros(shape=(T), dtype=complex) e2 = eta**2 e4 = eta**4 # Initialize matrices at T-1 Amat[0] = u1*rF Bmat[0] = mu*u1 + eta**(-2) - eta**(-2)*sqrt(1-2*eta*u1) Cmat[0] = 0 # Recursion backward in time (first is last in the matrix) for tt in range(1,T): Amat[tt] = Amat[tt-1] + u1*rF + (wq - ah*e4 - aq*e4)*Bmat[tt-1] \ + (wq - aq*e4)*Cmat[tt-1] \ -0.5*log(1 - 2*(ah+aq)*e4*Bmat[tt-1] \ - 2*aq*e4*Cmat[tt-1]) Bmat[tt] = u1*mu + (rho1 - (ch+cq)*eta**(-2) -(ah+aq)*e2)*Bmat[tt-1] \ - (cq*eta**(-2) + aq*eta**2)*Cmat[tt-1] + eta**(-2) \ - eta**(-2)*sqrt((1 - 2*(aq+ah)*e4*Bmat[tt-1]\ - 2*aq*e4*Cmat[tt-1])\ *(1 - 2*eta*u1-2*(cq+ch)*Bmat[tt-1] \ - 2*cq*Cmat[tt-1])) Cmat[tt] = (rho2-rho1)*Bmat[tt-1] + rho2*Cmat[tt-1] # g_t(u1,T) : (St**u1)*exp(A(t)+B(t)*h(t+1)+C(t)*q(t+1)) gt = exp(log(St)*u1 + Amat[tau-1] + Bmat[tau-1]*ht + Cmat[tau-1]*qt) return(gt) #==========================================================================# def Price_IG_GARCH_C(St,K,rF,ht,qt,tau,param): ''' Author: Stephane Surprenant, UQAM Creation: 15/03/2020 Description: Valuation of European call options for the IG-GARCH(C) model of Babaoglu, Christoffersen, Heston and Jacobs (2018) using IFT. INPUTS DESCRIPTION K : (float) Strike price St: (float) Stock/index level at time t ht: (float) Daily variance in the 2nd period (t+1) (Vol.daily = Vol.yearly^2/365) qt: (float) Daily long term variance in the 2nd period (t+1) tau: (int) Time to maturity (days) r : (float) Daily risk-free rate (rf.daily = rf.yearly/365) param: (float) Array: [mu_t,wq,rho1,ah,ch,rho2,aq,cq,eta] Note: Those are the risk-neutral component model risk-neutral parameters. References: See Babaoglu, Christoffersen, Heston and Jacobs (2018). REQUIRES: numpy (import sqrt, log, exp), scipy.integrate (quad) ''' # Integrands f1 = lambda u: np.real(K**(-u*1j)*\ CF_IG_GARCH_C(u*1j+1,St,rF,ht,qt,tau,param)/(St*u*1j)) f2 = lambda u: np.real(K**(-u*1j)*\ CF_IG_GARCH_C(u*1j,St,rF,ht,qt,tau,param)/(u*1j)) # Pricing formula (p.11) cPrice = St*(0.5 + exp(-rF*tau)/np.pi*quad(f1,0,10000)[0]) \ - K*exp(-rF*tau)*(0.5+1/np.pi*quad(f2,0,10000)[0]) # ============================================================================= # t_Hk = lambda u: np.imag(CF_IG_GARCH(u-1j,St,ht,tau,rF,param) \ # *exp(-1j*u*log(K))/(1j*u+1))/u # cPrice = 0.5*St + exp(-rF*tau)/np.pi*quad(t_Hk, 0, 1000)[0] # ============================================================================= return(cPrice)
我今天找出了問題的根源,這確實是在深夜工作時出現的愚蠢錯誤。在本文中,報告風險中性估計為 $ \tilde{\mu} = \mu + 1/\eta $ . 自從 $ \eta $ 太小了,太消極了,我嚴重低估了 $ \mu $ …
我直接糾正了上面的錯誤,以便有興趣使用該程式碼的任何人都可以輕鬆地做到這一點。現在,據我所知,該功能似乎工作得很好——至少,如果沒有,當我將它們與我知道可以正常工作的其他功能進行比較時,它會吐出非常合理的價格。