10. 克里金插值:地质统计学方法在波动率曲面中的应用
说实话,我第一次接触克里金插值的时候,心里是有点抵触的。
你想啊,一个搞地质的玩意儿,跟金融期权能扯上什么关系?
后来我在做一次深度虚值期权的波动率曲面构建时,发现传统的样条插值在数据稀疏区域简直惨不忍睹。嗯,那时候我才意识到——克里金这玩意儿,真香。
10.1 为什么是克里金?
传统的插值方法,比如线性插值、三次样条,它们只关心「距离」。离得近的点权重就大,离得远的就小。这逻辑在波动率曲面上其实有个致命问题:
波动率曲面是有空间相关性的,而且这种相关性不是各向同性的。
举个例子,ATM附近的波动率变化和尾部区域的波动率变化,它们的「变异模式」完全不同。普通插值法根本捕捉不到这种差异。
克里金不一样。它不光看距离,还看数据的空间结构——说白了,它知道哪些区域的数据「长得像」,哪些区域的数据「突变」。
核心思想:克里金是一种最优线性无偏估计器(BLUE)。它通过变异函数(Variogram)来描述数据的空间自相关性,然后给出每个未知点的最优权重。
10.2 变异函数:克里金的灵魂
我个人习惯把变异函数理解成「波动率的波动率」。它描述的是:两个点之间的波动率差异,跟它们的距离有什么关系。
数学上长这样:
γ(h) = 0.5 * E[(Z(x) - Z(x+h))²]
其中 Z(x) 是行权价 x 处的隐含波动率,h 是行权价差。
实际建模时,我们通常用几种理论模型来拟合经验变异函数:
| 模型名称 | 公式 | 特点 |
|---|---|---|
| 球状模型 | γ(h) = C₀ + C·(1.5h/a - 0.5(h/a)³) | 最常用,有基台值 |
| 指数模型 | γ(h) = C₀ + C·(1 - exp(-h/a)) | 渐近到达基台值 |
| 高斯模型 | γ(h) = C₀ + C·(1 - exp(-(h/a)²)) | 非常平滑,适合波动率曲面 |
我在项目中遇到过一个问题:用球状模型拟合短期期权时,效果还行。但一到长期限(比如2年期以上),球状模型就崩了——因为长期波动率的空间相关性衰减得更慢。后来我换成了高斯模型,才把曲面给「救」回来。
10.3 普通克里金 vs 泛克里金
这里有个坑,我踩过。
普通克里金假设波动率的均值是未知但恒定的。但实际中,波动率曲面有明显的「偏斜」和「期限结构」——均值根本就不是常数。
这时候就要用泛克里金(Universal Kriging)。它允许均值是一个关于行权价和期限的多项式函数。
我曾经踩过的坑:直接用普通克里金插值深度虚值期权,结果曲面在尾部出现了「负波动率」——这在金融上根本不可能。后来加入了一阶趋势项(行权价的一次项),才把这个问题压住。
10.4 实战:用Python实现克里金插值
我不喜欢讲太多理论,直接上代码。这是我在一个实盘项目中用过的简化版本:
import numpy as np
from scipy.optimize import minimize
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, WhiteKernel, ConstantKernel
def kriging_vol_surface(strikes, tenors, vols, target_strike, target_tenor):
"""
泛克里金插值波动率曲面
参数:
strikes: 已知行权价数组
tenors: 已知期限数组
vols: 已知波动率数组
target_strike: 目标行权价
target_tenor: 目标期限
"""
# 构建特征矩阵:行权价和期限
X = np.column_stack([strikes, tenors])
y = vols
# 定义核函数:RBF捕捉空间相关性 + 白噪声处理观测误差
kernel = ConstantKernel(1.0) * RBF(length_scale=[0.1, 0.1]) + WhiteKernel(noise_level=0.01)
# 拟合高斯过程(等价于泛克里金)
gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=10)
gp.fit(X, y)
# 预测
X_pred = np.array([[target_strike, target_tenor]])
vol_pred, sigma_pred = gp.predict(X_pred, return_std=True)
return vol_pred[0], sigma_pred[0]
# 示例:用5个已知点插值
strikes = np.array([0.8, 0.9, 1.0, 1.1, 1.2])
tenors = np.array([0.25, 0.25, 0.25, 0.25, 0.25])
vols = np.array([0.25, 0.22, 0.20, 0.21, 0.24])
vol, uncertainty = kriging_vol_surface(strikes, tenors, vols, 0.95, 0.25)
print(f"预测波动率: {vol:.4f}, 不确定性: {uncertainty:.4f}")
个人建议:用sklearn的GaussianProcessRegressor时,记得把n_restarts_optimizer设大一点(至少10次)。我一开始用默认值1,结果每次跑出来的曲面都不一样——因为优化陷入了局部最优。
10.5 克里金 vs 其他方法的对比
你可能会问:克里金比样条插值好在哪?我直接给你看对比:
| 方法 | 优点 | 缺点 |
|---|---|---|
| 三次样条 | 计算快,实现简单 | 无法处理数据稀疏区域,容易过拟合 |
| 径向基函数 | 灵活,可调参数多 | 参数选择主观,缺乏统计解释 |
| 克里金 | 提供不确定性估计,考虑空间结构 | 计算量大,需要拟合变异函数 |
说白了,如果你只是做快速原型验证,样条就够了。但如果你要上实盘交易系统,尤其是做套利监控或风控,克里金的不确定性估计就是无价之宝。
10.6 知识体系总览
下面这张图是我自己整理的克里金在波动率曲面中的应用框架,你看一眼就能明白整体逻辑:
10.7 避坑指南
最后分享几个我实战中总结的经验:
- 数据预处理很重要:克里金对异常值非常敏感。我建议先做一次3-sigma剔除,否则一个错误报价就能把整个曲面带偏。
- 不要盲目用高斯模型:虽然高斯模型平滑性好,但它假设数据无限可微。如果你的波动率曲面有「尖点」(比如临近到期时的ATM附近),高斯模型会过度平滑掉这些特征。
- 计算效率是个问题:克里金需要求解n×n的协方差矩阵,n是已知点数量。如果曲面有1000个点,矩阵求逆就够呛。我一般用局部克里金——只取目标点附近的50-100个点来插值。
一句话总结:克里金不是万能药,但在数据稀疏且需要不确定性估计的场景下,它比任何传统插值法都靠谱。前提是——你得把变异函数选对。
公众号:蓝海资料掘金营,微信deep3321