3. 市场数据获取:从交易所获取期权链数据、清洗与预处理、构建标准期限与行权价网格

做波动率曲面,第一步不是建模,不是写代码——是搞数据。

我见过太多人一上来就调参数、画曲面,结果数据源就有问题。嗯,那后面全是白费功夫。今天这章,咱们就聊聊怎么把交易所的原始期权数据,变成能用的、干净的、标准化的数据。

3.1 从交易所拿数据,没那么简单

不同交易所的数据格式、字段命名、甚至数据频率都不一样。我个人习惯先把数据源摸清楚,再动手写代码。

常见的期权链数据字段包括:

  • 合约代码:比如“510050C2309M03000”,里面藏着标的、到期日、行权价、看涨看跌
  • 最新价 / 收盘价:注意是昨收还是今收
  • 成交量、持仓量:判断流动性,没量的合约别用
  • 隐含波动率:有些交易所直接给,但我不建议直接用——你永远不知道他们怎么算的
  • Delta、Gamma、Vega:部分交易所会提供,但精度参差不齐
我的小习惯: 每次拿到新数据源,先拉一个月的日线数据,手动核对几个关键合约的价格和波动率。数据对不上,后面全是白做。

3.2 数据清洗——脏数据是波动率曲面的天敌

交易所的数据,说实话,没那么干净。我遇到过的情况包括:

  • 某天某合约成交量突然为0,但价格还在跳——明显是数据缺失
  • 行权价出现重复,同一个行权价有两个不同的价格
  • 到期日格式不统一,有的用“20230930”,有的用“2023-09-30”

清洗流程我一般这么走:

  1. 去重:按合约代码+时间戳去重,保留最新一条
  2. 过滤异常值:价格小于0、成交量小于0的直接删掉
  3. 处理缺失值:如果某合约连续缺失超过3个交易日,直接剔除
  4. 统一格式:到期日统一成YYYY-MM-DD,行权价统一保留两位小数
避坑指南: 我曾经遇到过一个数据源,某天所有看涨期权的价格突然翻倍,但看跌期权没动。后来发现是交易所数据推送时把“最新价”和“昨收价”搞反了。所以,清洗时一定要做交叉验证——看涨看跌的隐含波动率应该大致对称。

3.3 构建标准期限网格

交易所的期权到期日五花八门——有周度、月度、季度,甚至还有非标准到期日。但波动率曲面需要的是标准期限,比如7天、14天、30天、60天、90天、180天、365天。

怎么做?说白了就是插值。

我个人习惯用线性插值,简单、稳定。代码大概长这样:

import numpy as np
from scipy.interpolate import interp1d

# 原始到期日(以年为单位)
raw_maturities = np.array([0.02, 0.05, 0.08, 0.16, 0.25, 0.5, 1.0])
# 对应的隐含波动率
raw_ivs = np.array([0.25, 0.26, 0.27, 0.28, 0.27, 0.26, 0.25])

# 标准期限
standard_maturities = np.array([7/365, 14/365, 30/365, 60/365, 90/365, 180/365, 365/365])

# 线性插值
f = interp1d(raw_maturities, raw_ivs, kind='linear', fill_value='extrapolate')
standard_ivs = f(standard_maturities)

print(standard_ivs)
注意: 插值不是万能的。如果原始数据在某个期限附近特别稀疏,插出来的值可能不靠谱。我一般会加一个“数据密度检查”——如果某个期限附近少于3个合约,我就标记为“低置信度”,在曲面上用虚线表示。

3.4 构建标准行权价网格

行权价网格更麻烦。不同标的的价格范围不一样,比如上证50ETF大概在2.5-3.5之间,而沪深300ETF可能在3.5-5.0之间。你不能用同一个网格去套所有标的。

我的做法是:

  • 先算标的当前价格S
  • 然后以S为中心,往两边扩展,比如0.7S到1.3S
  • 再按固定步长(比如0.05或0.1)生成网格

举个例子:

import numpy as np

S = 3.0  # 标的当前价格
step = 0.05
lower = 0.7 * S
upper = 1.3 * S

strike_grid = np.arange(lower, upper + step, step)
strike_grid = np.round(strike_grid, 2)

print(strike_grid)

输出大概是:

[2.1  2.15 2.2  2.25 2.3  2.35 2.4  2.45 2.5  2.55 2.6  2.65 2.7  2.75
 2.8  2.85 2.9  2.95 3.   3.05 3.1  3.15 3.2  3.25 3.3  3.35 3.4  3.45
 3.5  3.55 3.6  3.65 3.7  3.75 3.8  3.85 3.9 ]
一个小技巧: 行权价网格的步长不要固定死。对于近月合约,步长可以小一点(比如0.05),因为近月波动率变化更敏感;远月合约步长可以大一点(比如0.1),因为远月波动率相对平滑。

3.5 整体流程——一张图说清楚

下面这张图,是我自己画的数据处理流程。每次做新项目,我都会先看一眼这张图,确保没漏掉哪一步。

市场数据获取与预处理流程 步骤1:数据获取 从交易所拉取期权链数据 步骤2:数据清洗 去重、过滤异常、处理缺失 步骤3:标准期限网格 线性插值生成标准到期日 步骤4:标准行权价网格 以标的价为中心生成网格 步骤5:合并成标准网格 期限 × 行权价 = 标准波动率曲面网格 输出:标准波动率曲面

3.6 几个容易踩的坑

  • 不要直接用交易所给的隐含波动率——他们用的定价模型可能和你不一样,算出来有偏差
  • 注意合约的流动性:深度虚值、深度实值的合约成交量极低,价格参考意义不大,建议过滤掉
  • 到期日换算成年化:别直接用“剩余天数”,要除以365(或者252,看你的习惯)
  • 行权价网格要覆盖足够宽:我见过有人只覆盖0.9S到1.1S,结果市场一波动,曲面就缺了一块
总结一下: 数据获取和清洗,占整个波动率曲面构建工作量的60%以上。别嫌麻烦,这一步做扎实了,后面建模、画图、回测都会顺很多。

专注资料整理