2、波动率曲面构建方法(上):插值法(线性插值、样条插值)、参数化模型(SVI模型)、SVI模型参数拟合实战
各位同学,今天我们来啃一块硬骨头——波动率曲面的构建。说实话,我刚入行那会儿,看着屏幕上那个扭曲的曲面,心里直犯嘀咕:这东西到底怎么来的?后来自己动手搭了一套系统,才明白其中的门道。
波动率曲面,说白了就是把不同行权价、不同到期日的隐含波动率,拼成一个三维曲面。但问题来了——市场上能看到的期权合约是有限的,比如只有几个行权价、几个到期日。中间那些点怎么办?这就需要我们「猜」出来。
怎么猜?两种主流思路:插值法和参数化模型。今天我们先讲插值法,再讲SVI参数化模型,最后带大家跑一遍拟合实战。
一、插值法:最朴素的想法
插值法的思路很直接:既然已知点上有数据,那未知点就用已知点「推算」出来。就像你知道了今天和明天的温度,那今天中午的温度大概是多少?线性插值告诉你:取个平均值呗。
1. 线性插值
线性插值是最简单的方法。给定两个点 (x₁, y₁) 和 (x₂, y₂),中间任意 x 对应的 y 值就是:
y = y₁ + (x - x₁) * (y₂ - y₁) / (x₂ - x₁)
嗯,就是一条直线。用在波动率曲面上,我们通常对行权价方向和期限方向分别做线性插值。
实际案例:假设你有一个30天到期的期权,行权价100的IV是20%,行权价110的IV是22%。现在你想知道行权价105的IV是多少?
套公式:IV = 20% + (105-100) * (22%-20%) / (110-100) = 20% + 5 * 2% / 10 = 21%
我的经验:线性插值虽然简单,但有个致命问题——它会产生「尖角」。在曲面边缘附近,这个尖角会导致套利机会的误判。我早期做回测时就被这个坑过,后来果断换成了样条插值。
2. 样条插值
样条插值比线性插值「聪明」一点。它不再用直线连接点,而是用平滑的曲线。最常用的是三次样条——每两个点之间用一个三次多项式连接,并且保证连接处一阶、二阶导数连续。
这样做的好处是:曲面更光滑,不会出现突兀的尖角。而且样条插值能更好地捕捉波动率曲面的「微笑」形态。
注意:样条插值也不是万能的。如果数据点太少,或者分布不均匀,样条可能会「过拟合」——在点之间画出奇怪的波浪。我曾经在只有5个行权价的数据上跑样条,结果曲面中间鼓出一个大包,完全不符合市场逻辑。
插值法最大的优点是简单、快速。但缺点也很明显:它只是数学上的拟合,没有任何金融逻辑支撑。说白了,它不知道波动率曲面应该长什么样,只是机械地「连点成线」。
二、参数化模型:SVI模型登场
既然插值法不够「聪明」,那我们就给模型加点金融直觉。参数化模型的核心思想是:用一组参数来描述整个波动率曲面。你只需要拟合这几个参数,就能得到任意点的波动率。
在众多参数化模型中,SVI(Stochastic Volatility Inspired)模型是我个人最常用的。为什么?因为它参数少、拟合快、而且能很好地刻画波动率微笑。
SVI模型的数学形式
SVI模型把波动率表示为行权价的函数。对于给定到期日,波动率与行权价的关系是:
w(k) = a + b * (ρ * (k - m) + sqrt((k - m)² + σ²))
其中:
- w(k):总方差(波动率平方 × 到期时间)
- k:对数行权价(ln(K/S))
- a:整体水平参数(决定波动率基准线)
- b:倾斜度参数(决定微笑的陡峭程度)
- ρ:偏度参数(决定微笑是否对称)
- m:偏移参数(决定微笑中心位置)
- σ:平滑参数(决定微笑的弯曲程度)
直观理解:你可以把SVI想象成一个「可塑形的微笑」。a控制微笑的高度,b控制微笑的宽度,ρ控制微笑是左歪还是右歪,m控制微笑的中心,σ控制微笑的圆润程度。
三、SVI模型参数拟合实战
理论讲完了,咱们来点实际的。怎么用Python拟合SVI模型?我给你一个完整的流程。
步骤1:准备数据
假设我们从市场上拿到了以下数据(行权价、隐含波动率、到期时间):
| 行权价 | 隐含波动率(%) | 到期时间(年) |
|---|---|---|
| 90 | 25.0 | 0.25 |
| 95 | 23.5 | 0.25 |
| 100 | 22.0 | 0.25 |
| 105 | 22.5 | 0.25 |
| 110 | 24.0 | 0.25 |
步骤2:定义SVI函数
import numpy as np
from scipy.optimize import minimize
def svi(k, a, b, rho, m, sigma):
"""SVI模型:输入对数行权价k,返回总方差w"""
return a + b * (rho * (k - m) + np.sqrt((k - m)**2 + sigma**2))
def svi_vol(k, a, b, rho, m, sigma, T):
"""将总方差转换为波动率"""
w = svi(k, a, b, rho, m, sigma)
return np.sqrt(w / T) * 100 # 转换为百分比
步骤3:定义损失函数并拟合
# 准备数据
strikes = np.array([90, 95, 100, 105, 110])
spot = 100
k = np.log(strikes / spot) # 对数行权价
vols = np.array([25.0, 23.5, 22.0, 22.5, 24.0])
T = 0.25
# 损失函数:最小化拟合误差
def loss(params):
a, b, rho, m, sigma = params
# 参数约束:b > 0, |rho| < 1, sigma > 0
if b <= 0 or abs(rho) >= 1 or sigma <= 0:
return 1e10
pred = svi_vol(k, a, b, rho, m, sigma, T)
return np.sum((pred - vols)**2)
# 初始猜测(根据经验设置)
init_params = [0.04, 0.1, -0.3, 0.0, 0.1]
# 执行优化
result = minimize(loss, init_params, method='Nelder-Mead')
a_opt, b_opt, rho_opt, m_opt, sigma_opt = result.x
print(f"拟合结果:a={a_opt:.4f}, b={b_opt:.4f}, rho={rho_opt:.4f}, m={m_opt:.4f}, sigma={sigma_opt:.4f}")
避坑指南:我曾经在拟合时遇到过参数不收敛的问题。后来发现是初始值选得太离谱。建议先用线性插值跑一遍,把结果作为SVI的初始猜测,这样收敛会快很多。
步骤4:验证拟合效果
拟合完成后,一定要画图看看。把原始数据点和拟合曲线叠在一起,检查是否合理。如果偏差太大,可能需要调整初始值或者换优化算法。
我的习惯:拟合完成后,我会额外检查两个东西:
- 无套利检验:确保拟合出的波动率曲面不存在蝶式套利机会
- 参数稳定性:用不同日期的数据分别拟合,看参数是否稳定变化
如果参数跳来跳去,说明模型可能有问题,或者市场结构发生了变化。
四、插值法 vs SVI:怎么选?
说了这么多,到底该用哪个?我个人的经验是:
- 快速原型、数据点密集:用样条插值,简单高效
- 需要参数解释性、数据点稀疏:用SVI,参数有金融含义
- 高频交易、追求速度:线性插值就够了,别整太复杂
- 做市商、风险管理:SVI更靠谱,因为参数稳定,便于监控市场变化
你想想看,如果你的交易策略需要实时计算波动率,每次都用优化算法拟合SVI,那速度肯定跟不上。但如果你是做日终风险分析,SVI的稳定性和解释性就很有价值。
好了,今天的内容就到这里。插值法和SVI模型是波动率曲面构建的两大基石。下一章我们会继续深入,讲讲更高级的构建方法——包括如何把不同期限的SVI参数连接起来,形成完整的曲面。