8. 波动率曲面平滑:去噪与平滑技术(核平滑、局部回归)

各位同学,咱们今天聊点实在的。波动率曲面这东西,你从市场上拿到的原始数据,说白了就是一堆带毛刺的散点。直接拿去做交易?我劝你别这么干。我见过太多人,拿着带噪声的曲面去套利,结果被市场狠狠教育了一顿。

为什么要平滑?因为市场微观结构里藏着各种噪声——买卖价差、数据延迟、大单冲击。这些噪声会让曲面变得坑坑洼洼。你想想看,一个坑洼的曲面,你去做套利,那不是找坑跳吗?

今天我就把两种最实用的平滑方法掰开揉碎了讲给你听:核平滑局部回归。这两种方法,我在实盘里用了好几年,踩过的坑比你们吃过的盐还多。

8.1 噪声的来源:为什么曲面需要去噪?

先说说噪声从哪来。我总结了三类:

  • 报价噪声:买卖价差导致的报价波动。比如某期权最后一笔成交在2.10,下一笔直接跳到2.15,中间那0.05就是噪声。
  • 流动性噪声:深度虚值或深度实值期权,成交稀疏,报价基本靠做市商瞎猜。
  • 事件噪声:财报、宏观数据发布瞬间,曲面会剧烈抖动。

嗯,这里要注意:不是所有波动都是噪声。真正的市场信号和噪声混在一起,你得学会分辨。我个人习惯是,先看数据的时间戳,把那些明显异常的点标记出来,再做平滑。

⚠️ 避坑指南
我曾经犯过一个错误:把到期日附近的曲面也做了强平滑。结果呢?到期日附近的波动率结构被完全抹平了,套利信号全没了。后来我学乖了——到期日附近要保留更多细节

8.2 核平滑:简单粗暴但有效

核平滑,说白了就是加权平均。你想想看,一个点周围的邻居,离得越近,权重越大。这就是核函数干的事。

常用的核函数有几种:

核函数 公式 特点
高斯核 K(u) = (1/√(2π)) * exp(-u²/2) 平滑效果好,但计算稍慢
Epanechnikov核 K(u) = 0.75*(1-u²) for |u|≤1 计算快,边界表现好
均匀核 K(u) = 0.5 for |u|≤1 最简单,但平滑效果一般

我个人习惯用高斯核。为什么?因为它的权重衰减是连续的,不会出现边界处突然截断的情况。但如果你追求速度,Epanechnikov核是个不错的选择。

来看看代码怎么实现:

import numpy as np
from scipy.stats import norm

def gaussian_kernel_smooth(x, y, x_new, bandwidth=0.1):
    """
    高斯核平滑
    x: 原始数据点(比如行权价)
    y: 原始波动率值
    x_new: 需要预测的新点
    bandwidth: 带宽,控制平滑程度
    """
    y_smooth = np.zeros_like(x_new)
    
    for i, xi in enumerate(x_new):
        # 计算每个原始点到新点的距离
        distances = np.abs(x - xi)
        # 高斯核权重
        weights = norm.pdf(distances / bandwidth)
        # 加权平均
        y_smooth[i] = np.sum(weights * y) / np.sum(weights)
    
    return y_smooth

# 示例:平滑一条波动率微笑曲线
strikes = np.array([90, 95, 100, 105, 110])
vols = np.array([0.25, 0.22, 0.20, 0.23, 0.28])
smooth_strikes = np.linspace(90, 110, 50)
smooth_vols = gaussian_kernel_smooth(strikes, vols, smooth_strikes, bandwidth=5)
💡 小技巧
带宽的选择很关键。带宽太小,平滑效果不够;带宽太大,会把真实结构也抹掉。我一般用交叉验证来选带宽,或者直接用Silverman's rule of thumb作为起点。

8.3 局部回归:更聪明的平滑方式

核平滑有个问题:它在边界处表现不好。你想想看,曲面边缘的数据点少,加权平均的结果容易偏。这时候,局部回归就派上用场了。

局部回归(LOESS)的思路是:在每个点附近,用局部多项式去拟合。说白了,就是分段做回归。这样做的好处是:

  • 边界处表现更好
  • 能捕捉局部曲率变化
  • 对异常点更鲁棒

我在项目中遇到过这样的情况:用核平滑处理深度虚值期权时,曲面尾部总是翘得太高。换成局部回归后,尾部自然多了。

from sklearn.linear_model import Ridge
import numpy as np

def local_regression_smooth(x, y, x_new, bandwidth=0.3, degree=2):
    """
    局部回归平滑
    x: 原始数据点
    y: 原始波动率值
    x_new: 需要预测的新点
    bandwidth: 带宽比例(占数据范围的百分比)
    degree: 多项式次数
    """
    y_smooth = np.zeros_like(x_new)
    x_range = x.max() - x.min()
    
    for i, xi in enumerate(x_new):
        # 计算距离
        distances = np.abs(x - xi)
        # 使用三立方核(tricube kernel)
        u = distances / (bandwidth * x_range)
        weights = np.where(u <= 1, (1 - u**3)**3, 0)
        
        # 只选择权重大于0的点
        mask = weights > 0
        if np.sum(mask) < degree + 1:
            # 数据点太少,用全局平均
            y_smooth[i] = np.mean(y)
            continue
        
        # 构建局部多项式特征
        X_local = np.vander(x[mask] - xi, degree + 1, increasing=True)
        # 加权最小二乘
        W = np.diag(weights[mask])
        beta = np.linalg.inv(X_local.T @ W @ X_local) @ (X_local.T @ W @ y[mask])
        y_smooth[i] = beta[0]  # 常数项就是预测值
    
    return y_smooth
🔑 核心要点
局部回归的带宽和多项式次数需要配合。次数越高,对局部细节的捕捉能力越强,但也更容易过拟合。我一般用二次多项式,带宽选0.2-0.4之间。

8.4 两种方法的对比与选择

说了这么多,到底该用哪个?我直接给你个决策指南:

场景 推荐方法 理由
数据量大,追求速度 核平滑 计算简单,O(n)复杂度
边界处需要准确 局部回归 边界表现更好
曲面有尖峰结构 局部回归(低带宽) 能保留局部特征
快速原型验证 核平滑 实现简单,调参容易

说白了,没有绝对的好坏。我自己的做法是:先用核平滑快速看看整体形状,再用局部回归做精细调整。两个方法配合着用,效果最好。

8.5 实战中的注意事项

最后,我把自己踩过的坑总结一下:

  • 带宽不是越小越好:我见过有人把带宽设到0.01,结果平滑后的曲面比原始数据还毛糙。记住,平滑的目的是去噪,不是拟合。
  • 注意到期日效应:不同到期日的波动率结构差异很大。我建议按到期日分组,分别做平滑,再插值到整个曲面。
  • 异常点要预处理:有些报价明显是错的(比如波动率为负),这些点要先剔除,再做平滑。否则一个异常点能带偏一片。
  • 验证平滑效果:平滑后一定要回测。把平滑后的曲面代入定价模型,看看套利信号是否合理。如果信号变多了,说明平滑过度了。
⚠️ 重要提醒
平滑不是万能的。如果原始数据质量太差(比如大量缺失值),再好的平滑方法也救不了。我建议先做数据清洗,再做平滑。顺序不能乱。

好了,关于核平滑和局部回归,我就讲这么多。这两种方法,你回去一定要动手试试。光看代码没用,得自己调参数、看效果,才能真正理解。记住,平滑的目的是让曲面更干净,而不是更复杂

波动率曲面平滑知识体系 波动率曲面平滑 噪声来源识别 核平滑方法 局部回归方法 报价噪声 流动性噪声 事件噪声 高斯核 Epanechnikov核 均匀核 带宽选择 多项式次数 核心目标:去噪保真,保留真实波动率结构

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