实战案例:商品期货期权曲面异常检测

各位做量化交易的朋友,今天咱们来聊一个实战性很强的话题——商品期货期权曲面的异常检测。

说实话,我刚入行那会儿,对波动率曲面这玩意儿是又爱又恨。爱的是它确实能反映市场情绪,恨的是它经常莫名其妙地出现一些"鬼影"。有一次我在做玉米期权的套利策略,突然发现曲面上一块区域的价格明显偏离,差点让我亏掉一个月的利润。从那以后,我就把异常检测当成了每天的必修课。

为什么要检测曲面异常?

你想想看,商品期货期权市场和股票期权不太一样。商品市场受季节性、天气、政策等因素影响更大,曲面更容易出现"毛刺"。这些异常点如果不处理,会带来三个问题:

  • 定价失真:异常点会让你的定价模型跑偏,该买的卖贵了,该卖的买便宜了
  • 套利陷阱:看起来是套利机会,其实是数据错误,一进去就被套
  • 风控失效:VaR计算依赖曲面数据,异常点会让风险指标完全失真

核心观点:异常检测不是可选项,而是期权交易的"安全带"。不系安全带也能开车,但出了事就晚了。

异常检测的三种方法

我个人习惯把异常检测分成三个层次,从简单到复杂,逐步排查。

1. 统计阈值法

这是最基础的方法。说白了,就是看某个点的隐含波动率偏离均值多少个标准差。

import numpy as np
import pandas as pd

def detect_outliers_by_zscore(surface_data, threshold=3):
    """
    基于Z-score的异常检测
    surface_data: DataFrame, 行是到期日,列是执行价
    """
    mean = np.nanmean(surface_data.values)
    std = np.nanstd(surface_data.values)
    
    z_scores = (surface_data - mean) / std
    outliers = np.abs(z_scores) > threshold
    
    return outliers, z_scores

我在项目中遇到过一个问题:直接用全局均值做Z-score,结果把深度虚值期权全标成了异常。为什么?因为深度虚值期权的波动率天然就高,这不是异常,是市场特性。所以后来我改成了按行(同一到期日)或按列(同一执行价)分别计算。

2. 局部异常因子(LOF)法

这个方法更聪明一些。它不看全局,而是看每个点周围邻居的密度。如果一个点周围的密度明显低于邻居,那它就很可疑。

from sklearn.neighbors import LocalOutlierFactor

def detect_outliers_lof(surface_data, n_neighbors=20):
    """
    使用LOF检测曲面异常点
    """
    # 将曲面展平为二维数组
    X = surface_data.values.reshape(-1, 1)
    
    # 创建LOF模型
    lof = LocalOutlierFactor(n_neighbors=n_neighbors, contamination=0.05)
    y_pred = lof.fit_predict(X)
    
    # 重塑回曲面形状
    outlier_mask = (y_pred == -1).reshape(surface_data.shape)
    
    return outlier_mask, lof.negative_outlier_factor_

小技巧:n_neighbors参数别设太小。我一般设20-30,太小了容易把正常点误判为异常,太大了又可能漏掉真正的异常。

3. 模型残差法

这是我最喜欢的方法。先用一个平滑模型拟合曲面,然后看每个点的残差。残差大的就是异常点。

from scipy.interpolate import SmoothBivariateSpline
import numpy as np

def detect_outliers_by_residual(moneyness, time_to_expiry, implied_vol, smoothing=0.1):
    """
    基于模型残差的异常检测
    """
    # 用平滑样条拟合曲面
    spline = SmoothBivariateSpline(
        moneyness, time_to_expiry, implied_vol,
        s=smoothing * len(moneyness)
    )
    
    # 计算拟合值
    fitted_vol = spline.ev(moneyness, time_to_expiry)
    
    # 计算残差
    residuals = implied_vol - fitted_vol
    
    # 残差超过3倍标准差的视为异常
    threshold = 3 * np.std(residuals)
    outliers = np.abs(residuals) > threshold
    
    return outliers, residuals, fitted_vol

嗯,这里要注意:smoothing参数很关键。设太大,曲面太平滑,真实的市场结构都被抹掉了;设太小,又过度拟合,异常点反而被"解释"掉了。我一般先用交叉验证选一个合适的值。

实战案例:豆粕期权曲面

咱们拿豆粕期权来练练手。豆粕这个品种很有意思,受大豆进口、猪周期、饲料需求等多重因素影响,曲面经常出现"怪相"。

我记得有一次,2022年3月,豆粕期权曲面突然出现了一个"尖刺"——某个执行价的隐含波动率比周围高了将近10个百分点。当时很多人以为是套利机会,但我用模型残差法一查,发现这个点明显偏离了平滑曲面。后来一查,原来是某个大机构在大量买入那个执行价的看涨期权,造成了暂时的流动性失衡。

下面是我当时用的检测流程:

  1. 数据清洗:剔除交易量过小的合约(日交易量<100手)
  2. 曲面构建:用SVI模型拟合原始数据
  3. 异常检测:三种方法同时跑,取交集
  4. 人工复核:对标记的异常点,查看当时的市场新闻

警告:千万别完全依赖算法。有一次算法把真实的市场冲击误判为异常,我差点错过了一个重要的交易信号。算法是工具,不是上帝。

异常处理策略

检测出异常之后怎么办?我一般分三种情况处理:

异常类型 处理方式 适用场景
数据错误 直接剔除,用插值填补 明显的数据录入错误、报价异常
流动性异常 标记但不剔除,降低权重 大单冲击、流动性枯竭
市场结构变化 保留并分析原因 政策变化、供需突变

我曾经犯过一个错误:把所有异常点都直接剔除。结果曲面变得过于平滑,完全失去了市场的真实波动信息。后来我学乖了,异常点也要分门别类处理。

可视化检测结果

光看数字不够直观,我习惯把检测结果画出来。下面是一个简单的可视化代码:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def plot_surface_with_outliers(moneyness, tte, iv, outlier_mask):
    fig = plt.figure(figsize=(12, 8))
    ax = fig.add_subplot(111, projection='3d')
    
    # 正常点用蓝色
    ax.scatter(moneyness[~outlier_mask], tte[~outlier_mask], 
               iv[~outlier_mask], c='blue', alpha=0.6, label='Normal')
    
    # 异常点用红色
    ax.scatter(moneyness[outlier_mask], tte[outlier_mask], 
               iv[outlier_mask], c='red', s=100, marker='x', label='Outlier')
    
    ax.set_xlabel('Moneyness')
    ax.set_ylabel('Time to Expiry')
    ax.set_zlabel('Implied Vol')
    ax.legend()
    
    plt.show()

说实话,三维图看起来挺酷的,但实际工作中我更喜欢用热力图。热力图能更清楚地显示异常点的位置和程度。

知识体系总览

下面这张图是我自己总结的异常检测知识体系,你可以参考一下:

商品期货期权曲面异常检测知识体系 异常检测 统计阈值法 Z-score检测 IQR方法 分位数法 局部异常因子法 密度比较 邻居选择 局部离群因子 模型残差法 平滑样条拟合 残差分析 SVI模型 异常处理策略 数据错误 → 剔除插值 流动性异常 → 降权 结构变化 → 保留分析 核心原则:算法是工具,市场判断是灵魂

这张图把整个知识体系串起来了。从三种检测方法到处理策略,再到核心原则,一目了然。

避坑指南

最后,分享几个我踩过的坑:

  • 别只看一个指标:我曾经只靠Z-score检测,结果把正常的波动率微笑当成了异常。现在我会三种方法交叉验证。
  • 注意到期日效应:临近到期的期权,波动率会剧烈波动,这不一定是异常。我一般会剔除到期前3天的数据。
  • 别忽略交易量:交易量小的合约,报价本身就不靠谱。我习惯先按交易量过滤,再做异常检测。

我的习惯:每天开盘前跑一遍异常检测,把结果存下来。时间长了,你就能摸清市场的"脾气",哪些异常是噪音,哪些是信号,心里就有数了。

好了,关于商品期货期权曲面的异常检测,今天就聊到这儿。记住一句话:异常检测不是目的,目的是让你更清楚地看到市场的真实面貌。工具再好,也得靠人来判断。

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