20、波动率曲面校准:市场数据校准流程、校准目标函数、优化算法选择

波动率曲面这东西,看着漂亮,但真正要用它来做定价或风控,你得先把它「校准」到当前市场。说白了,模型参数不能拍脑袋,得让模型输出的价格,跟市场上真实交易的价格对上。这个过程,就是校准。

我刚开始做这块时,觉得校准不就是个拟合嘛,调调参数让误差最小就完了。后来发现,坑多着呢。今天咱们就把这个流程掰开揉碎,讲清楚。

20.1 市场数据校准流程

校准的第一步,不是跑代码,而是拿数据。你想想看,模型再牛,输入的数据是脏的,结果肯定废。

我个人习惯的流程是这样的:

  1. 获取市场报价:从数据源拿到期权链的隐含波动率数据。通常是不同行权价、不同到期日的 IV 矩阵。
  2. 数据清洗:剔除明显异常的点。比如深度虚值期权流动性差,报价经常跳来跳去,这些点我会直接扔掉。
  3. 筛选有效数据:只保留近月到远月几个关键期限,行权价覆盖平值附近 ±2 个标准差就够了。太远的尾部数据,对校准贡献不大,反而引入噪声。
  4. 构建目标矩阵:把清洗后的数据整理成 (期限, 行权价) 的二维表,这就是我们要拟合的目标。
  5. 执行校准:用优化算法去调模型参数,让模型输出的曲面尽量贴近目标矩阵。
  6. 验证与诊断:校准完了别急着用,先看看拟合误差分布,有没有系统性偏差。

我曾经在一个项目中,数据源给的波动率是百分数,我当成小数直接喂进去了,结果校准出来的参数完全离谱。嗯,这种低级错误,犯一次就长记性了。

核心原则:校准不是越精确越好。过度拟合市场噪声,会导致参数不稳定,明天开盘数据一变,参数就乱跳。我们追求的是「稳健拟合」。

20.2 校准目标函数

目标函数,就是衡量「模型输出」和「市场数据」之间差距的数学表达式。选什么样的目标函数,直接影响校准结果。

常见的几种目标函数:

目标函数类型 公式 特点
均方误差 (MSE) ∑(σ_model - σ_market)² / N 最常用,对大偏差惩罚重,容易受异常点影响
平均绝对误差 (MAE) ∑|σ_model - σ_market| / N 对异常点鲁棒性更好,但收敛速度慢
加权均方误差 (WMSE) ∑ w_i · (σ_model - σ_market)² / N 给不同区域赋权重,比如平值附近权重高,尾部权重低
价格误差 ∑ (Price_model - Price_market)² 直接对期权价格拟合,但价格对波动率敏感度不同

我个人更倾向于用 加权均方误差。为什么?因为平值附近的期权流动性最好,报价最可靠,理应给更高权重。深度虚值期权,报价经常是理论值,参考意义不大,权重给低点。

权重的设定,我一般用 vega 的倒数或者交易量的比例。你想想看,vega 大的地方,波动率稍微一变价格就剧烈波动,这些点更应该被精确拟合。

小技巧:校准目标函数里,我习惯加一个正则化项,比如 λ·||θ||²,惩罚参数过大。这样能防止参数跑到极端值,提高模型的稳定性。

20.3 优化算法选择

目标函数定好了,接下来就是怎么找到让目标函数最小的参数。优化算法选不对,可能跑半天都不收敛,或者收敛到局部最优。

常用的优化算法,我按经验排个序:

  • Levenberg-Marquardt (LM):我的首选。它结合了梯度下降和高斯-牛顿法的优点,收敛快,适合中小规模参数优化。SVI、SABR 这类参数不多的模型,LM 基本够用。
  • BFGS / L-BFGS:拟牛顿法,适合参数稍多的情况。不需要计算 Hessian 矩阵,内存占用小。我一般在参数超过 10 个时用 L-BFGS。
  • Nelder-Mead (单纯形法):无导数方法,适合目标函数不光滑的情况。但收敛慢,我一般只用来做初值搜索。
  • 全局优化 (差分进化、模拟退火):当目标函数有很多局部极小点时,先用全局优化找个好初值,再用 LM 或 BFGS 精调。我遇到过 SABR 模型的 β 参数,局部极小点特别多,不用全局优化根本找不到全局最优。

这里有个经验:初值很重要。我习惯先用历史数据跑一遍,把历史参数的平均值作为初值。或者用简单模型(比如平值波动率)先估算一下参数范围,再喂给优化器。

避坑指南:我曾经直接用默认初值去校准 SVI 模型,结果优化器报错说 Hessian 矩阵奇异。查了半天,发现是初值导致某个参数组合让模型退化。后来我改成先用网格搜索粗筛一遍初值,再跑 LM,问题就解决了。

20.4 校准流程的 SVG 框架图

下面这张图,把整个校准流程串起来了。你可以把它当作操作手册,每次校准前扫一眼,避免漏步骤。

波动率曲面校准流程 获取市场报价 数据清洗与筛选 构建目标波动率矩阵 选择模型 & 目标函数 优化算法迭代求解 验证拟合误差 & 诊断 不满足精度 关键参数 • 期限结构 • 行权价范围 • 权重设定 • 正则化系数 常用优化算法 • Levenberg-Marquardt • L-BFGS • 差分进化 (全局)

20.5 代码示例:SVI 模型校准

光说不练假把式。下面我用 Python 演示一个 SVI 模型的校准过程。SVI 模型参数少(5个),校准起来比较快,适合做例子。

import numpy as np
from scipy.optimize import minimize

# SVI 模型:w(k) = a + b * (rho * (k - m) + sqrt((k - m)^2 + sigma^2))
def svi_vol(k, a, b, rho, m, sigma):
    return a + b * (rho * (k - m) + np.sqrt((k - m)**2 + sigma**2))

# 目标函数:加权均方误差
def objective(params, strikes, target_vols, weights):
    a, b, rho, m, sigma = params
    # 参数约束:b > 0, sigma > 0, |rho| < 1
    if b <= 0 or sigma <= 0 or abs(rho) >= 1:
        return 1e10
    model_vols = svi_vol(strikes, a, b, rho, m, sigma)
    return np.sum(weights * (model_vols - target_vols)**2)

# 模拟市场数据(实际使用时替换为真实数据)
strikes = np.linspace(-0.3, 0.3, 11)  # 对数行权价
target_vols = 0.15 + 0.05 * strikes**2 + 0.02 * np.random.randn(11)
weights = np.ones_like(strikes)  # 简单等权

# 初值设定(经验值)
initial_guess = [0.1, 0.1, -0.3, 0.0, 0.1]

# 执行优化
result = minimize(objective, initial_guess, 
                  args=(strikes, target_vols, weights),
                  method='L-BFGS-B',
                  bounds=[(0.01, 0.5), (0.01, 0.5), (-0.99, 0.99), 
                          (-0.2, 0.2), (0.01, 0.3)])

print("校准参数:", result.x)
print("最终误差:", result.fun)

经验之谈:代码里我加了参数边界约束,这是必须的。SVI 模型的参数如果没有约束,优化器可能给出 b 为负值,那波动率曲面形状就完全反了。我刚开始写代码时没加约束,结果画出来的曲面像个倒扣的碗,闹了笑话。

20.6 校准后的检查清单

校准跑完了,别急着部署。我每次都会过一遍这个清单:

  • 拟合误差是否在可接受范围? 一般均方根误差 (RMSE) 小于 0.5 个 vol 点算不错。
  • 参数是否在合理区间? 比如 SVI 的 rho 应该在 -1 到 1 之间,如果接近边界,说明数据可能有问题。
  • 曲面形状是否平滑? 用肉眼扫一眼曲面图,有没有不该出现的尖刺或凹陷。
  • 参数稳定性如何? 用昨天的数据校准一次,今天的再校准一次,参数变化大不大?如果一天一变,说明模型过拟合了。

嗯,校准这件事,说白了就是「用模型去解释市场」。模型永远是对现实的简化,校准的目标不是让模型完美拟合每一个点,而是抓住市场的核心特征——偏斜、期限结构、微笑形态。抓住了这些,你的曲面就能用了。

公众号:蓝海资料掘金营,微信deep3321