波动率曲面构建(一):数据清洗与预处理、期权筛选标准、插值方法对比

做波动率曲面,说白了就是给期权市场拍一张「三维照片」。横轴是行权价,纵轴是到期日,竖轴是隐含波动率。这张照片拍得清不清楚,关键看两个事:原始数据干不干净,插值手法到不到位。

我个人习惯,拿到期权数据的第一件事不是跑模型,而是先盯着原始数据看十分钟。为什么?因为交易所来的数据,脏得能让你怀疑人生。我见过有人把深度虚值期权的报价直接扔进模型,结果曲面长成了「心电图」——全是锯齿。

一、数据清洗与预处理

数据清洗,听着像体力活,其实是最容易翻车的地方。我踩过的坑,一个一个说。

1.1 缺失值与异常值处理

期权数据常见的缺失情况有两种:一是某个行权价全天没有成交,二是某个到期日只有零星几笔。我的处理原则很简单——

  • 日内缺失:用前后两个交易日的同一合约数据填充,别用当天其他合约的平均值。不同行权价的波动率结构差异很大,平均会抹平特征。
  • 跨期缺失:如果某个到期日的数据量少于10笔,我建议直接剔除。强行插值出来的曲面,近月端会扭曲得厉害。

异常值呢?我最常遇到的是「胖手指」——有人把0.05的波动率敲成了0.5。怎么识别?我一般用两步:

  1. 计算每个到期日下,所有行权价的波动率中位数和绝对偏差(MAD)。
  2. 把偏离中位数超过3倍MAD的点标记为异常,直接剔除。

核心原则:宁可少一个数据点,也不要让一个脏数据污染整个曲面。

1.2 时间戳对齐与交易日统一

嗯,这里要注意。不同交易所的期权数据,时间戳格式可能不一样。比如上交所是毫秒级时间戳,而有些数据商给的是字符串。我习惯把所有时间统一转换成UTC+8的datetime格式,然后按交易日对齐。

我曾经犯过一个低级错误:把下午3点收盘后的数据也算进了当天。结果曲面在尾盘附近出现了一个「翘尾」——因为收盘后的大宗交易波动率偏低,把整个曲面拉歪了。后来我加了一条规则:只取交易时段内(9:30-11:30, 13:00-15:00)的数据。

1.3 代码示例:数据清洗流水线

import pandas as pd
import numpy as np
from datetime import datetime

def clean_option_data(df):
    # 1. 时间戳统一
    df['trade_time'] = pd.to_datetime(df['timestamp'], unit='ms')
    df = df[(df['trade_time'].dt.hour >= 9) & (df['trade_time'].dt.hour <= 15)]
    
    # 2. 剔除交易量小于10手的合约
    df = df[df['volume'] >= 10]
    
    # 3. 异常值检测(基于MAD)
    for expiry in df['expiry'].unique():
        mask = df['expiry'] == expiry
        med = df.loc[mask, 'implied_vol'].median()
        mad = np.abs(df.loc[mask, 'implied_vol'] - med).median()
        df = df[~((df['expiry'] == expiry) & 
                  (np.abs(df['implied_vol'] - med) > 3 * mad))]
    
    return df

小技巧:清洗完数据后,我习惯画一张散点图——把每个合约的波动率按行权价和到期日标出来。一眼就能看出有没有「飞点」。肉眼检查永远比代码检查靠谱。

二、期权筛选标准

数据洗干净了,接下来要挑「好」的期权。不是所有期权都值得放进曲面。你想想看,一个还有3天到期的深度虚值期权,流动性差得一天只成交5手,它的报价能信吗?

2.1 流动性筛选

流动性是命根子。我定过一套硬性标准:

筛选维度 标准 原因
日成交量 ≥ 100手 低于这个数,买卖价差太大,报价失真
持仓量 ≥ 500张 持仓量低说明市场关注度不够,容易被操纵
买卖价差 ≤ 中间价的5% 价差太宽,隐含波动率的计算误差会放大

我记得有一次做50ETF期权曲面,发现近月合约的买卖价差普遍在2%以内,但远月合约到了10%以上。后来我直接把远月合约的价差阈值放宽到8%,才保留了足够的数据点。说白了,远月流动性天生差,标准要适当调整。

2.2 到期日筛选

到期日太近的合约,时间价值衰减快,波动率容易「发疯」。我一般剔除到期日小于7天的合约。为什么?因为临近到期,Gamma会变得极大,稍微一点价格波动就能把隐含波动率推到天上。

反过来,到期日太远的合约(比如超过1年),流动性差,而且远期波动率的预测意义不大。我个人习惯只保留30天到365天之间的合约。

2.3 行权价筛选

行权价这块,核心是剔除「深度实值」和「深度虚值」的合约。怎么定义「深度」?我用的标准是:

  • 实值侧:行权价偏离标的价格超过20%的,剔除。
  • 虚值侧:行权价偏离标的价格超过30%的,剔除。

为什么实值侧比虚值侧更严格?因为实值期权的时间价值低,隐含波动率对模型假设(比如分红、利率)特别敏感。虚值期权虽然也有噪音,但至少还能反映尾部风险。

注意:筛选标准不是死的。如果市场波动率特别高(比如VIX > 30),我会把行权价的筛选范围放宽到±40%。因为高波动环境下,极端行权价也有交易价值。

三、插值方法对比

数据选好了,接下来要在行权价和到期日这两个维度上「填空」。插值方法选不对,曲面就会变形。我试过三种主流方法,一个一个说。

3.1 线性插值

线性插值最简单,也最「暴力」。在两个已知点之间画一条直线,中间的值按比例算。

优点:速度快,不会出现「过冲」——也就是插出来的值不会跑到已知点的范围之外。

缺点:在波动率曲面上,线性插值会抹掉「微笑」特征。你想想看,波动率在平值附近是凹的,线性插值硬生生把它拉成了直线,曲面看起来就像「塌了」。

我什么时候用线性?只在数据点非常密集的时候用。比如近月合约,行权价间隔只有50点,线性插值的误差可以接受。

3.2 三次样条插值

三次样条是我最常用的方法。它在每两个已知点之间拟合一个三次多项式,并且保证连接处的一阶和二阶导数连续。

优点:曲线光滑,能很好地保留波动率的「微笑」和「偏斜」特征。

缺点:容易「过冲」。如果数据点分布不均匀,三次样条会在两端「翘起来」。我遇到过最夸张的一次,插出来的波动率变成了负数——这显然不合理。

怎么解决?加边界条件。我习惯用「自然样条」——强制两端点的二阶导数为0。这样能有效抑制过冲。

3.4 多项式插值

多项式插值,就是用一条高次多项式穿过所有已知点。听起来很完美,对吧?但实际用起来,问题很大。

优点:理论上,如果数据点足够多,多项式可以完美拟合任何形状。

缺点:高次多项式在边界处会剧烈震荡——这就是著名的「龙格现象」。我试过用5次多项式拟合10个数据点,结果两端的值直接飞到了天上。

说实话,我在实际项目中几乎不用多项式插值。除非数据点少于5个,而且你知道这些点大致在一条平滑曲线上。

3.5 三种方法对比表

方法 光滑度 过冲风险 计算速度 适用场景
线性插值 极快 数据密集、近月合约
三次样条 中等 大多数情况,推荐使用
多项式插值 极高 中等 数据点极少时谨慎使用

我的建议:默认用三次样条,加上自然边界条件。如果发现过冲,就改用「分段线性+平滑」的混合方法——在数据密集区用线性,稀疏区用样条。

四、本章知识体系

下面这张图,把本章的核心逻辑串起来了。从原始数据到最终曲面,每一步都有坑,每一步都有对策。

波动率曲面构建流程 数据清洗与预处理 缺失值填充 · 异常值剔除 期权筛选标准 流动性 · 到期日 · 行权价 插值方法 线性 · 三次样条 · 多项式 数据清洗要点 • 时间戳统一为UTC+8 • MAD法剔除异常值 • 只保留交易时段数据 筛选标准 • 成交量 ≥ 100手 • 到期日 7~365天 • 行权价偏离 ≤ 20%~30% 插方法选择 • 默认:三次样条+自然边界 • 数据密集:线性插值 • 避免:高次多项式 最终目标:光滑、合理、可交易的波动率曲面

这张图里,每一步都是环环相扣的。数据洗不干净,后面插值再漂亮也没用。筛选标准定得太松,曲面就会「失真」;定得太严,数据点不够,插值就会「发散」。说白了,这是个平衡的艺术。

好了,这一章的内容就到这儿。数据准备好了,下一章我们就要真正动手构建曲面了——到时候会用到今天讲的所有内容。


专注资料整理