13. 曲面平滑度检测:如何检测曲面是否存在异常凸起或凹陷

做波动率曲面套利,说白了就是找「不正常」的地方。

曲面上的异常凸起或凹陷,就是我们的猎物。我做了这么多年,发现很多人盯着曲面看半天,却不知道哪里不对劲。今天咱们就聊聊,怎么用数学工具把这些「畸形」揪出来。

13.1 为什么平滑度这么重要?

一个正常的波动率曲面,应该是光滑的、渐变的。就像一块丝绸,摸起来顺滑。但市场有时候会抽风——某个深度虚值期权突然被大量买入,或者某个近月合约出现流动性真空,曲面上就会鼓起一个包,或者塌下去一个坑。

这些异常点,往往意味着:

  • 套利机会:凸起处可能被高估,适合做空波动率
  • 数据错误:交易所的报价有时会抽风,我遇到过某合约报价比合理值高了30%
  • 市场情绪:某个行权价突然被大量交易,可能是大资金在布局

核心观点:平滑度检测不是学术游戏,它是发现套利机会的第一道筛子。

13.2 检测方法一:局部曲率分析

我最常用的方法,就是计算曲面上每个点的「局部曲率」。曲率大的地方,就是异常点。

具体怎么做?

  1. 对曲面进行网格化,每个网格点对应一个行权价和到期时间
  2. 计算每个点的一阶偏导和二阶偏导
  3. 用二阶偏导的绝对值作为「异常分数」

代码实现其实不复杂:

import numpy as np
from scipy.ndimage import gaussian_filter

def detect_anomalies(surface, sigma=1.5, threshold=2.0):
    """
    surface: 二维数组,行=行权价,列=到期时间
    sigma: 高斯平滑参数
    threshold: 异常阈值(标准差倍数)
    """
    # 先平滑,得到基准曲面
    smooth_surface = gaussian_filter(surface, sigma=sigma)
    
    # 计算残差
    residual = surface - smooth_surface
    
    # 计算局部曲率(用拉普拉斯算子近似)
    from scipy.ndimage import laplace
    curvature = laplace(surface)
    
    # 异常分数 = 残差 * 曲率
    anomaly_score = np.abs(residual * curvature)
    
    # 标记异常点
    mean_score = np.mean(anomaly_score)
    std_score = np.std(anomaly_score)
    anomalies = anomaly_score > (mean_score + threshold * std_score)
    
    return anomalies, anomaly_score

我的经验:sigma参数很关键。太小了会把正常波动也当成异常,太大了又会漏掉真正的异常。我一般先用1.5试跑,然后根据结果调整。

13.3 检测方法二:样条插值对比法

这个方法更直观。你想想看,如果我们用光滑的样条曲线去拟合曲面,拟合误差大的地方,就是异常点。

步骤是这样的:

  1. 用三次样条对曲面进行插值,得到一个光滑版本
  2. 计算原始曲面和插值曲面的差值
  3. 差值超过3个标准差的地方,标记为异常

我曾经用这个方法发现过一个很有意思的现象:某个行权价的波动率连续三天都比样条插值结果高5%以上。后来一查,原来是某家做市商在故意拉高那个行权价的报价,想诱多。嗯,这种信息对交易决策很有价值。

from scipy.interpolate import SmoothBivariateSpline

def spline_detection(strikes, maturities, iv_surface, s=0.1):
    """
    strikes: 行权价数组
    maturities: 到期时间数组
    iv_surface: 波动率曲面(二维数组)
    s: 平滑参数,越大越光滑
    """
    # 构建网格
    X, Y = np.meshgrid(strikes, maturities)
    x_flat = X.flatten()
    y_flat = Y.flatten()
    z_flat = iv_surface.flatten()
    
    # 拟合样条
    spline = SmoothBivariateSpline(x_flat, y_flat, z_flat, s=s)
    
    # 在原始网格点上评估
    smooth_surface = spline.ev(X.flatten(), Y.flatten()).reshape(X.shape)
    
    # 计算残差
    residual = iv_surface - smooth_surface
    
    # 异常检测
    threshold = 3 * np.std(residual)
    anomalies = np.abs(residual) > threshold
    
    return anomalies, residual

注意:样条插值的平滑参数s需要谨慎选择。s太小,样条会过拟合,把正常波动也当成异常;s太大,又会过度平滑,漏掉真正的异常。我建议先用交叉验证选一个合适的s值。

13.4 检测方法三:相邻点比较法

这个方法最简单,也最实用。不需要复杂的数学,就是比较每个点跟它周围邻居的差异。

具体来说:

  • 对每个点,取它周围8个邻居(3x3窗口)
  • 计算该点与邻居均值的差异
  • 如果差异超过某个阈值,标记为异常

为什么这个方法好用?因为真正的异常往往是孤立的。如果一大片区域都偏高,那可能是整个期限结构在变化,不是异常。只有那种「鹤立鸡群」的点,才值得我们关注。

def neighbor_comparison(surface, window_size=3, threshold=2.5):
    from scipy.ndimage import uniform_filter
    
    # 计算局部均值
    local_mean = uniform_filter(surface, size=window_size)
    
    # 计算局部标准差
    local_std = np.sqrt(uniform_filter((surface - local_mean)**2, size=window_size))
    
    # 避免除零
    local_std = np.maximum(local_std, 1e-8)
    
    # Z-score
    z_scores = (surface - local_mean) / local_std
    
    # 标记异常
    anomalies = np.abs(z_scores) > threshold
    
    return anomalies, z_scores

避坑指南:我曾经用3x3窗口检测5分钟级别的数据,结果发现异常点太多。后来改成5x5窗口,效果就好多了。窗口大小要根据数据频率来调整,没有万能参数。

13.5 三种方法的对比

方法 优点 缺点 适用场景
局部曲率分析 数学严谨,能检测微小异常 对噪声敏感,需要调参 高频数据、精细检测
样条插值对比 直观易懂,结果可解释 计算量大,边界效应明显 日频数据、研究分析
相邻点比较 简单快速,鲁棒性好 可能漏掉区域性异常 实时监控、快速筛查

13.6 实战中的注意事项

说了这么多方法,最后分享几个实战心得:

  • 不要只看一个指标:我习惯同时用三种方法跑一遍,取交集。三个方法都标记为异常的点,基本就是真异常了。
  • 注意边界点:曲面边缘的点,邻居少,检测结果不可靠。我一般会忽略最外两行两列的点。
  • 结合成交量看:如果一个点被标记为异常,但成交量很小,那可能是数据错误。如果成交量很大,那才是真正的套利机会。
  • 动态调整阈值:市场波动大的时候,阈值要放宽。波动小的时候,阈值要收紧。我一般用滚动窗口的动态阈值。

记住:平滑度检测只是第一步。发现异常后,还要分析原因、评估风险、制定策略。但如果没有这一步,你就像在黑暗中找东西,全靠运气。

13.7 知识体系图

下面这张图总结了曲面平滑度检测的核心逻辑:

曲面平滑度检测知识体系 波动率曲面 局部曲率分析 拉普拉斯算子 样条插值对比 三次样条拟合 相邻点比较 局部Z-score 异常分数矩阵 残差曲面 Z-score矩阵 综合判断 → 异常点标记 套利机会 / 数据错误 / 市场信号

这张图把整个检测流程串起来了。从原始曲面出发,三种方法并行计算,最后综合判断。我个人习惯把三种方法的异常点取交集,这样误报率最低。


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