Skip to main content

ETF 期权隐含波动率曲面计算方法文档

朱陈毓 2025.04.30

1 隐含波动率计算公式

ETF 期权的价格公式可以由 Black-76 公式得到:

C=erT(FN(d1)KN(d2))\begin{equation} C = e^{-rT}\left( F\cdot N(d_1) - K\cdot N(d_2) \right) \end{equation}

其中:

  • CC 为看涨期权的价格
  • rr 为无风险利率
  • TT 为距离到期的剩余时间
  • N()N(\cdot) 为标准正态分布的累计概率密度 (cdf) 函数
  • KK 为看涨期权的行权价
  • FF 为 ETF 的远期价格

d1,d2d_1, d_2 的定义为:

d1=logF/K+σ2T2σT,d2=d1σT\begin{equation} d_1 = \frac{\log F/K +\frac{\sigma^2 T}{2}}{\sigma\sqrt{T}}, d_2 = d_1 - \sigma\sqrt{T} \end{equation}

其中:

  • σ\sigma 为看涨期权的隐含波动率

对于行权价 KK 与剩余时间 TT 相同的看涨期权价格 CC 和看跌期权价格 PP,有 put-call parity 成立:

CP=erT(FK)\begin{equation} C - P = e^{-rT}(F - K) \end{equation}

ETF 的远期价格 FF 定义为:

F=Se(rq)T\begin{equation} F = S e^{(r-q) T} \end{equation}

其中:

  • qq 为 ETF 的隐含持有成本
  • SS 为 ETF 的价格

将 公式.(3) 和公式.(4) 联立可以得到 qq 的计算公式为:

q=logS(CP)+KerTT\begin{equation} q = \frac{ \log \frac{S}{(C-P) + K e^{-rT}}}{T} \end{equation}

2 ETF隐含波动率曲面计算步骤

在完成步骤 1 后,对于同一个到期日,每10s选取一个期权数据切片,每个时间切片上有 MM 个行权价 K1,K2,..KMK_1,K_2,..K_M 的看涨和看跌期权对,我们会得到看涨和看跌期权一共 (2×M)(2\times M) 个隐含波动率数据点 σ\sigma

σ=[σC1σC2...σCMσP1σP2...σPM]\begin{equation} \sigma= \begin{bmatrix} \sigma_{C1} & \sigma_{C2} & ... & \sigma_{CM} \\ \sigma_{P1} & \sigma_{P2} & ... & \sigma_{PM} \\ \end{bmatrix} \end{equation}

t 时刻的波动率曲面的计算步骤如下:

  1. 计算期权当前时间的各行权价所对应的 (1×M)(1\times M)Moneyness, ForwardMoneynessLogForwardMoneyness
Moneyness=ETFPrice/KForwardMoneyness=ETFForwardPrice/KLogForwardMoneyness=log(ForwardMoneyness)\text{Moneyness} = \text{ETFPrice}/K\\ \text{ForwardMoneyness} = \text{ETFForwardPrice}/K\\ \text{LogForwardMoneyness} = \log(\text{ForwardMoneyness})
  1. 对于看涨期权,选取 ForwardMoneyness >= 1-MoneynessCutoff 的隐含波动率,对于看跌期权,选取 ForwardMoneyness <= 1+MoneynessCutoff 的隐含波动率,并将 FwdMoneyness 有交叉的数据点加权平均得到 (1×M)(1\times M) 个隐含波动率数据点:
ImpliedVol=[σP1,σP2,...σPjLogForwardMoneyness1CurOff,σBj+1,σBj+2,...σBi+iσB=σCFwdMoneyness41+FwdMoneyness4+σP11+FwdMoneyness4,σCj+i+1,σCj+i+2,...σCj+MLogForwardMoneyness1+CurOff]\begin{equation} \begin{aligned} \text{ImpliedVol} = [ \underbrace{ \sigma_{P1}, \sigma_{P2},...\sigma_{Pj}}_{\text{LogForwardMoneyness} \leq 1-\text{CurOff}}, \underbrace{\sigma_{B{j+1}},\sigma_{B{j+2}}, ...\sigma_{B_{i+i}}}_ { \sigma_B = \sigma_C\frac{\text{FwdMoneyness}^4}{1+\text{FwdMoneyness}^4}+\sigma_P\frac{1}{1+\text{FwdMoneyness}^4} }, \underbrace{\sigma_{C{j+i+1}},\sigma_{C{j+i+2}}, ...\sigma_{C_{j+M}}}_{\text{LogForwardMoneyness} \geq 1+\text{CurOff}} ] \end{aligned} \end{equation}
  1. 重复步骤1-2,得到每一个时间点上的 Moneyness, Forwardmoneyness, LogForwardMoneynessImpliedVol

  2. 计算每一个数据点的拟合的 Weight

Weight=n(1/ForawrdMoneyness+12σin2TenorinσiTenorin)\text{Weight} = n\left(\frac{1/\text{ForawrdMoneyness}+\frac{1}{2}\sigma_{in}^2\text{Tenor}_{in}}{\sigma_i\sqrt{\text{Tenor}_{in}}}\right)

其中:

  • n()n(\cdot) 为标准正态分布的概率密度函数
  • σin\sigma_{in}min(ImpliedVol)*1.1
  • Tenorin\text{Tenor}_{in} 取 20.0 天

计算完 Weight 后需要将 Weight 归一化:

Weight = Weight / sum(Weight)
  1. 调用 QE_SVI.py 中的 CalibrateSVI() 函数,得到当前到期日的隐含波动率曲线拟合参数:
LB_Sigma_Init = 1e-3
UB_Sigma_Init = 20
SVIPar, Resid, ATMVol = CalibrateSVI(Moneyness, ImpliedVol, R, yield_rt, Tenor, Weight, TargetRes = 1e-4, Method='SQP')
  1. 对每一个到期日,重复步骤1-5

注意

  • CalibrateSVI() 进行了版本更新,需要使用最新版的API
  • 更新前的 API 调用格式为
def CalibrateSVI( Moneyness, ImpVol, R, Q, T, Weights, TargetRes = 1e-4, Method = 'SQP' ):
...
  • 更新后的 API 在 Method 后新增了两个输入变量 InitialPargrad_tol,但设有默认值,请勿更改:
def CalibrateSVI(Moneyness, ImpVol, R, Q, T, Weights, TargetRes = 1e-4,  Method='SQP',
InitialPar=(1e-6, 1e-4, 0.0, 0.0, NP.sqrt(LB_Sigma_Init * UB_Sigma_Init)),
grad_tol=1e-16)

因此在调用该函数时仍然可以保持原来的格式不变:

# 保持原来的调用格式
SVIPar, Resid, ATMVol = CalibrateSVI( Moneyness, ImpVol, R, Q, T, Weights, TargetRes = 1e-4,
Method = 'SQP' )

错误处理

  1. 计算隐含波动率时,筛掉无法计算的行权价:
idxCalc = ( ~NP.isnan(ETFForwardPrice) & \ # ETFForwardPrice 不为 nan
~NP.isnan(OptionForwardPrice) & \ # OptionForwardPrice 不为 nan
~NP.isnan(Tenor) & \ # Tenor is not nan
# OptionForwardPrice 大于期权内在价值
(OptionForwardPrice > NP.maximum( Optiontype * ( UnderlyingForwardPrice_rt - K ), 0 )) & \
# OptionForwradPrice 大于 ETFForwradPrice
(ETFForwardPrice > OptionForwardPrice)
)
# 看涨期权 Optiontype == 1
# 看跌期权 Optiontype == 1
  1. 调用CalibrateSVI()之前,检查 Moneyness, ImpliedVol, R, yield_rt, Tenor, Weight,如果这些 Input 数据存在 nan 值,则计算失败
  2. CalibrateSVI() 返回的 Residinf/-inf/nan ,或者 SVIPar,ATMVol 中有 nan/inf/-inf 值,则说明波动率曲面计算失败,此时再调用一次 CalibrateSVI() 重新计算,保持其他输入变量不变,改用 Method='NL'