11. 曲面平滑技术:核回归、局部多项式、正则化方法

做波动率曲面的人,早晚会遇到一个头疼的问题——你从市场上扒下来的隐含波动率数据,它不干净。

我刚开始做期权做市的时候,每天收盘后第一件事就是看曲面。嗯,那叫一个丑。有的点高得像山峰,有的低得像盆地。你要是直接用这些点去插值定价,隔壁交易员能拿着风控报告来找你打架。

所以,我们需要平滑。说白了,就是把那些噪声滤掉,保留曲面的真实形状。今天咱们就聊聊三种主流方法:核回归、局部多项式、正则化。

为什么需要平滑?

先想一个问题:你从市场上拿到的隐含波动率,真的是「真实」波动率吗?

不是。那是做市商报价、大单成交、流动性溢价、甚至交易员手滑的综合产物。我见过一个极端案例——某天下午3点29分,某个深度虚值期权突然冒出一个波动率80%的成交。你信吗?反正我不信。那是某个倒霉蛋敲错了单。

所以,平滑的本质是:在拟合优度和光滑度之间找一个平衡。你拟合得太紧,噪声全进来了;你拟合得太松,真实结构又丢了。

核心思想: 用局部信息估计全局形状,同时控制过拟合。

方法一:核回归(Nadaraya-Watson 估计)

核回归的思路很直观:对于曲面上任意一点 (K, T),用它周围的数据点加权平均来估计波动率。权重由核函数决定,距离越近权重越大。

公式长这样:

σ̂(K, T) = Σ wᵢ · σᵢ / Σ wᵢ

其中 wᵢ = K( (K - Kᵢ)/h_K ) · K( (T - Tᵢ)/h_T )

这里 K(·) 是核函数,h 是带宽。带宽越大,平滑程度越高。

我个人习惯用高斯核,因为它的数学性质好,而且在实际中表现稳定。但要注意——带宽的选择比核函数本身重要得多

我的经验: 带宽太小,曲面会「毛茸茸」的,像没刮胡子。带宽太大,曲面会「糊成一团」,连微笑曲线都看不出来。我一般用交叉验证来选带宽,或者直接用 Scott 规则做初始估计再微调。

核回归的优点是简单、直观、容易实现。缺点呢?在数据稀疏的区域,估计值会偏向零——说白了就是边缘效应严重。你想想看,在期限很长的远端,数据点本来就少,核回归估计出来的波动率往往偏低。

方法二:局部多项式回归

核回归本质上是在每个点做一个常数拟合。那如果我们用局部线性甚至局部二次拟合呢?这就是局部多项式回归。

它的思路是:在每个估计点附近,用一个低阶多项式去拟合局部数据。权重还是由核函数给出。

局部线性回归的优化问题:

min Σ wᵢ · (σᵢ - β₀ - β₁(Kᵢ - K) - β₂(Tᵢ - T))²

解出 β₀ 就是该点的估计值。

为什么局部多项式比核回归好?因为它能更好地捕捉曲面的局部趋势。核回归在边界处会有偏差,而局部线性回归可以自动修正这个偏差。我做过对比测试,在同样的带宽下,局部多项式的均方误差通常比核回归低 15%-20%。

注意: 阶数不是越高越好。局部二次拟合在数据密集区表现很好,但在稀疏区容易过拟合。我一般只在数据点超过30个的区域用二次,否则就用线性。

实际应用中,我推荐用 locpoly 或者自己写一个加权最小二乘。Python 里用 numpy 配合 scipy.spatial 就能搞定。

方法三:正则化方法(惩罚样条)

前面两种方法都是「局部」的。那有没有「全局」的方法?有,正则化方法就是。

正则化的核心是:用一个基函数展开来表示曲面,然后对展开系数的「粗糙度」进行惩罚。最常用的是惩罚样条(P-spline)。

目标函数:

min Σ (σᵢ - f(Kᵢ, Tᵢ))² + λ · ∫ (f'')² dK dT

第一项是拟合误差,第二项是惩罚项。λ 越大,曲面越光滑。

我特别喜欢惩罚样条的一点是:你可以显式控制光滑程度。不像核回归,你得靠调带宽来间接控制。惩罚样条直接告诉你:λ=0 就是插值,λ=∞ 就是平面。

在实际项目中,我用 B-spline 基函数配合二阶差分惩罚。效果很稳定。而且计算效率高——一旦基函数选好,求解就是一个线性系统。

避坑指南: 我曾经在一个项目中把 λ 设得太小,结果曲面在 ATM 附近出现了奇怪的波浪。后来发现是惩罚不够,导致样条在数据密集区过度弯曲。从那以后,我养成了一个习惯:先用广义交叉验证(GCV)选 λ,然后人工看一眼曲面形状,再微调。

三种方法的对比

方法 优点 缺点 适用场景
核回归 简单、直观、计算快 边缘偏差、带宽敏感 数据密集、快速原型
局部多项式 偏差小、精度高 稀疏区不稳定 数据质量好、需要高精度
惩罚样条 全局控制、灵活 基函数选择有技巧 生产环境、需要稳定输出

实战代码片段

下面是一个用惩罚样条平滑波动率曲面的简单示例。我用的是 scipy.interpolateLSQBivariateSpline,配合手动调 λ。

import numpy as np
from scipy.interpolate import LSQBivariateSpline

# 假设我们有数据:K, T, sigma
K = np.array([...])  # 行权价
T = np.array([...])  # 期限
sigma = np.array([...])  # 隐含波动率

# 定义样条节点
kx = np.linspace(K.min(), K.max(), 10)
ky = np.linspace(T.min(), T.max(), 8)

# 拟合惩罚样条,lambda 控制光滑度
spline = LSQBivariateSpline(K, T, sigma, kx, ky, lambda_=0.01)

# 在网格上评估
K_grid = np.linspace(K.min(), K.max(), 50)
T_grid = np.linspace(T.min(), T.max(), 30)
sigma_smooth = spline(K_grid, T_grid)

嗯,这里要注意:lambda_ 的取值很关键。我一般先试 0.001、0.01、0.1 三个数量级,然后看哪个在验证集上表现最好。

知识体系图

下面这张图总结了三种平滑方法的核心逻辑和关系:

波动率曲面平滑技术 核回归 局部多项式 惩罚样条 核心思想:在拟合优度与光滑度之间寻找平衡 关键参数:带宽 h 关键参数:带宽 h + 阶数 p 关键参数:惩罚系数 λ 优点:简单快速 缺点:边缘偏差 优点:精度高 缺点:稀疏区不稳 优点:全局可控 缺点:调参有技巧 选择建议 快速原型用核回归 → 高精度用局部多项式 → 生产环境用惩罚样条

我的建议

如果你刚开始做曲面平滑,我建议从核回归入手。代码简单,效果直观,能让你快速理解「平滑」到底在干什么。

等你对数据有了感觉,再切换到局部多项式或者惩罚样条。我个人在实盘系统中用的是惩罚样条,因为它的稳定性最好,而且方便做压力测试——我可以直接调 λ 来模拟不同光滑程度下的定价差异。

最后说一句:没有万能的方法。不同的市场、不同的数据质量、不同的业务需求,适合的方法都不一样。多试、多看、多对比,才是正道。

一个小技巧: 每次做完平滑,把原始数据点和平滑曲面画在同一张图上。如果曲面穿过了大部分数据点的「中心」,说明平滑程度合适。如果曲面离数据点太远,那就是过度平滑了。

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