14. 曲面平滑技术:核回归平滑、局部多项式平滑、平滑参数选择

好了,咱们接着聊。上一章我们把波动率曲面的骨架搭起来了——原始数据、插值、网格化。但说实话,那个曲面看起来还像个刺猬。为什么?因为市场报价本身就有噪音,买卖价差、交易摩擦、做市商的情绪波动,都会让曲面坑坑洼洼。

平滑,就是把这些毛刺去掉,让曲面回归到它该有的样子。我个人习惯把平滑看作是“给曲面做美容”——不是整容,是微调。今天咱们聊三种主流手法:核回归、局部多项式,以及那个让人又爱又恨的参数选择问题。

14.1 为什么需要平滑?

先看一个实际场景。我在做某个股指期权曲面时,发现ATM附近的隐含波动率突然跳了0.5个vol。查了半天,原来是某个大单成交导致的瞬时冲击。这种点,你如果不处理,后续的定价和风控全都会偏。

平滑要解决的核心问题有三个:

  • 去噪:剔除市场微观结构带来的随机波动
  • 保持形状:不能把真实的偏斜(skew)和期限结构给抹平了
  • 稳定性:今天和明天的曲面不能因为一个报价的变动就剧烈抖动

核心原则:平滑不是越光滑越好,而是要在“拟合度”和“光滑度”之间找到平衡。过拟合会保留噪音,过平滑会丢失结构。

14.2 核回归平滑(Nadaraya-Watson Estimator)

核回归,说白了就是加权平均。每个点的值,由它周围点的加权平均来估计。权重由核函数决定——距离越近,权重越大。

公式长这样:

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

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

这里K(·)是核函数,h是带宽参数。我常用的核函数有:

  • 高斯核:最常用,平滑效果好,但计算稍慢
  • Epanechnikov核:理论上最优,计算快,我比较偏爱
  • 均匀核:简单粗暴,但边界处容易有跳跃

代码实现其实不复杂:

import numpy as np
from scipy.spatial.distance import cdist

def kernel_regression(X, y, X_pred, h=0.1, kernel='gaussian'):
    """
    X: (n, 2) 形状,列是 [K, T]
    y: (n,) 隐含波动率
    X_pred: (m, 2) 待预测点
    h: 带宽
    """
    dists = cdist(X_pred, X, metric='euclidean')
    
    if kernel == 'gaussian':
        weights = np.exp(-0.5 * (dists / h)**2)
    elif kernel == 'epanechnikov':
        weights = np.maximum(0, 1 - (dists / h)**2)
    
    # 加权平均
    y_pred = np.sum(weights * y, axis=1) / np.sum(weights, axis=1)
    return y_pred

我的经验:核回归在数据密集区表现很好,但在边界处(比如深度虚值或远月合约)容易出问题。因为边界点的邻居不对称,估计会有偏。我曾经在远月合约上吃过这个亏,后来加了边界校正才解决。

14.3 局部多项式平滑(Local Polynomial Regression)

核回归有个问题——它本质上是用常数来拟合局部区域。如果曲面曲率比较大,常数拟合就不够用了。局部多项式就是升级版:在每个局部,用一个低阶多项式来拟合。

我个人最常用的是局部线性回归(局部多项式的一阶形式)。它在每个预测点附近,求解一个加权最小二乘问题:

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

代码实现:

def local_linear_regression(X, y, X_pred, h=0.1):
    y_pred = np.zeros(len(X_pred))
    
    for i, x0 in enumerate(X_pred):
        # 计算权重
        dists = np.linalg.norm(X - x0, axis=1)
        weights = np.exp(-0.5 * (dists / h)**2)
        W = np.diag(weights)
        
        # 构建设计矩阵(包含截距项和线性项)
        X_design = np.c_[np.ones(len(X)), X - x0]
        
        # 加权最小二乘
        beta = np.linalg.inv(X_design.T @ W @ X_design) @ (X_design.T @ W @ y)
        y_pred[i] = beta[0]  # 只取截距项
    
    return y_pred

局部多项式比核回归好在哪?

  • 边界表现更好:线性项能自动校正边界偏差
  • 导数估计:β₁和β₂直接给出了曲面的偏导数,这对greeks计算很有用
  • 适应性更强:能处理曲率变化大的区域

注意:局部多项式的阶数不是越高越好。二阶以上容易过拟合,而且计算量会暴增。我一般只用一阶(局部线性)或二阶(局部二次),再高就不推荐了。

14.4 平滑参数选择——那个让人头疼的问题

不管是核回归还是局部多项式,都绕不开一个参数:带宽h。h选大了,曲面太平滑,细节全丢;h选小了,曲面还是毛刺一堆。

我常用的方法有几种:

方法 原理 优缺点
交叉验证(CV) 留一法或k折,最小化预测误差 客观,但计算量大
广义交叉验证(GCV) CV的近似,计算更快 适合大样本,但可能过平滑
经验法则(Rule of Thumb) h = c · σ · n^(-1/5) 简单快速,但不够精确
目视检查 画几个不同h的曲面,凭经验选 主观,但实战中很有效

交叉验证的代码:

from sklearn.model_selection import KFold

def cv_bandwidth_selection(X, y, h_candidates):
    kf = KFold(n_splits=5, shuffle=True)
    cv_errors = []
    
    for h in h_candidates:
        errors = []
        for train_idx, val_idx in kf.split(X):
            X_train, y_train = X[train_idx], y[train_idx]
            X_val, y_val = X[val_idx], y[val_idx]
            
            y_pred = kernel_regression(X_train, y_train, X_val, h=h)
            errors.append(np.mean((y_val - y_pred)**2))
        
        cv_errors.append(np.mean(errors))
    
    best_h = h_candidates[np.argmin(cv_errors)]
    return best_h, cv_errors

我的习惯:先用经验法则算一个初始值,然后在它周围画一个网格做交叉验证。这样既不会盲目搜索,也不会完全依赖经验。另外,我建议对K和T维度分别设置带宽——因为它们的量纲不同,一个带宽很难同时适配两个维度。

14.5 实战中的避坑指南

这些年做曲面平滑,踩过的坑不少。挑几个典型的说说:

  • 带宽对数据密度敏感:ATM附近数据密集,带宽可以小一点;深度虚值数据稀疏,带宽要大一些。我曾经用全局带宽吃过亏,后来改成了自适应带宽。
  • 不要忽视异常值:平滑前最好先做一次异常值检测。我一般用3σ原则或MAD(中位数绝对偏差)来剔除明显的错误报价。
  • 平滑后的曲面要满足无套利约束:平滑完一定要检查——有没有出现波动率为负的情况?有没有蝶式套利机会?这些在平滑过程中很容易被忽略。
  • 计算效率:如果你要做实时曲面更新,核回归和局部多项式都要考虑计算效率。我一般用KD树来加速近邻搜索,能快一个数量级。

14.6 本章知识体系

下面这张图总结了曲面平滑的核心流程和选择逻辑:

曲面平滑技术核心流程 原始波动率曲面 选择平滑方法 核回归平滑 Nadaraya-Watson 局部多项式平滑 局部线性/二次 带宽参数选择 交叉验证 / GCV 带宽参数选择 经验法则 / 自适应 平滑后的波动率曲面 去噪 + 保持形状 + 稳定性 平滑参数选择是核心难点:带宽过小→过拟合,带宽过大→欠拟合

嗯,曲面平滑这块内容不少,但核心就三件事:选方法、调参数、验结果。核回归适合快速原型,局部多项式适合生产环境。参数选择没有银弹,交叉验证加经验判断是我目前觉得最靠谱的组合。

记住一点:平滑是为了让曲面更真实,而不是更漂亮。如果平滑后的曲面出现了新的套利机会,那说明你平滑过头了。

本章小结:核回归平滑适合数据密集区域,局部多项式平滑在边界和曲率变化大的区域表现更好。平滑参数选择推荐交叉验证结合经验法则。实战中要注意自适应带宽、异常值处理和无套利约束检查。


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