3. 隐含波动率曲面拟合与做市报价
任务目标
本案例基于市场期权价格数据,构建隐含波动率曲面(Implied Volatility Surface),并利用该曲面为任意行权价与到期日的期权生成一致性的做市报价。核心解决两个问题:一是从离散的期权报价中提取隐含波动率;二是对提取的波动率进行曲面拟合,消除单一合约报价的噪声与套利不一致性。
实现思路
采用两步法:第一步,利用Black-Scholes模型对市场期权价格反解出各合约的隐含波动率(IV);第二步,以行权价与到期时间为自变量,对IV进行二维插值拟合,形成光滑曲面。做市报价时,输入任意行权价与剩余期限,从曲面插值得到IV,再代回BS公式计算理论价格,并叠加买卖价差输出双边报价。
关键代码
import numpy as np
from scipy.optimize import brentq
from scipy.interpolate import griddata
from scipy.stats import norm
# ---------- 1. Black-Scholes定价与IV反解 ----------
def bs_price(S, K, T, r, sigma, option_type='call'):
if T <= 0 or sigma <= 0:
return np.nan
d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
if option_type == 'call':
return S*norm.cdf(d1) - K*np.exp(-r*T)*norm.cdf(d2)
else:
return K*np.exp(-r*T)*norm.cdf(-d2) - S*norm.cdf(-d1)
def implied_vol(market_price, S, K, T, r, option_type='call'):
"""反解隐含波动率,使用二分法"""
if T <= 0 or market_price <= 0:
return np.nan
try:
return brentq(lambda sig: bs_price(S, K, T, r, sig, option_type) - market_price,
1e-4, 5.0)
except ValueError:
return np.nan
# ---------- 2. 曲面拟合 ----------
def fit_iv_surface(quotes, S, r):
"""
quotes: list of dict, 每个元素含 K, T, market_price, option_type
返回插值函数 f(K, T) -> sigma
"""
points = []
values = []
for q in quotes:
iv = implied_vol(q['market_price'], S, q['K'], q['T'], r, q['option_type'])
if not np.isnan(iv):
points.append([q['K'], q['T']])
values.append(iv)
points = np.array(points)
values = np.array(values)
# 使用三次立方插值,fill_value设为边界值外推
def surface(K_new, T_new):
return griddata(points, values, (K_new, T_new), method='cubic',
fill_value=np.nan)
return surface
# ---------- 3. 做市报价 ----------
def market_make_quote(surface, S, K, T, r, spread_bps=50, option_type='call'):
"""
基于曲面给出双边报价
spread_bps: 买卖价差,单位基点(1bp=0.0001)
"""
sigma = surface(K, T)
if np.isnan(sigma):
return None # 曲面外无法报价
mid_price = bs_price(S, K, T, r, sigma, option_type)
half_spread = mid_price * spread_bps / 10000
bid = mid_price - half_spread
ask = mid_price + half_spread
return {'mid': mid_price, 'bid': bid, 'ask': ask, 'iv': sigma}
# ---------- 4. 演示:构造模拟市场数据并报价 ----------
if __name__ == '__main__':
S = 100.0 # 标的现价
r = 0.02 # 无风险利率
# 模拟市场报价:不同K和T的看涨期权
market_quotes = []
for K in [90, 95, 100, 105, 110]:
for T in [0.1, 0.2, 0.5, 1.0]:
true_iv = 0.2 + 0.05*(K-100)/100 + 0.03*T # 真实曲面
price = bs_price(S, K, T, r, true_iv, 'call')
market_quotes.append({'K': K, 'T': T, 'market_price': price,
'option_type': 'call'})
# 拟合曲面
surf = fit_iv_surface(market_quotes, S, r)
# 对曲面内任意点报价
test_points = [(98, 0.3), (103, 0.7), (105, 0.15)]
for K, T in test_points:
quote = market_make_quote(surf, S, K, T, r)
if quote:
print(f"K={K}, T={T}: mid={quote['mid']:.3f}, "
f"bid={quote['bid']:.3f}, ask={quote['ask']:.3f}, IV={quote['iv']:.4f}")
else:
print(f"K={K}, T={T}: 曲面外,无法报价")
运行与验证
运行方式:直接执行脚本,需安装 numpy、scipy。代码末尾的演示部分会输出三个测试点的双边报价。
验证方法:
- IV反解正确性:将输出的IV与构造数据时使用的真实IV(
true_iv)对比,误差应小于0.001(因插值平滑略有偏差属正常)。 - 曲面一致性:对任意测试点,输出的
bid < mid < ask,且价差约为mid的50bp。 - 无套利检查:对同一到期日,不同行权价的报价应满足凸性关系(可自行验证相邻K的价差合理性)。
预期输出示例(数值随插值略有浮动):
K=98, T=0.3: mid=4.123, bid=4.102, ask=4.144, IV=0.2187
K=103, T=0.7: mid=5.876, bid=5.846, ask=5.906, IV=0.2265
K=105, T=0.15: mid=1.234, bid=1.228, ask=1.240, IV=0.2078
常见问题
- 插值出现NaN:当请求的点超出已知数据范围时,
griddata返回NaN,此时做市商应拒绝报价或使用边界IV近似,本实现选择拒绝。 - 实盘数据噪声:本案例使用模拟无噪声数据演示核心逻辑。实盘需先对异常报价(如买卖价差倒挂、深度虚值流动性差)进行过滤,否则反解IV可能失败。
- 曲面外推风险:对超出样本范围的期限或行权价,插值结果不可靠。建议实盘中限制报价范围在曲面覆盖区域内。