第16章 数据获取与清洗:如何获取期权市场数据,并进行清洗和预处理

做波动率曲面套利,说白了就是跟数据打交道。我见过太多人策略逻辑写得漂亮,结果一上实盘就崩——十有八九是数据源出了问题。

这一章,我带你走一遍数据获取和清洗的全流程。嗯,都是我在实盘里踩过的坑,你直接拿去用就行。

16.1 数据源的选择:从哪里拿数据?

期权数据不像股票那么方便。国内期权市场的数据源,我归纳下来就三类:

数据源类型 代表 优点 缺点
交易所直连 上交所、深交所 最原始、最准确 门槛高、费用贵
金融数据终端 Wind、Choice、Tushare 接口成熟、文档全 有延迟、字段可能被加工
第三方量化平台 聚宽、米筐、掘金 上手快、有回测环境 数据深度有限

我个人习惯用Wind Pro接口做主力数据源。为什么?因为它覆盖了全市场的期权链数据,而且历史数据回溯比较完整。但要注意——Wind的实时数据有2-3秒延迟,做高频套利的话得用交易所直连。

⚠️ 避坑提醒: 我曾经用某免费接口拿50ETF期权数据,跑了三个月回测收益曲线漂亮得不行。结果一查,发现它的隐含波动率计算方式跟交易所对不上,差了整整0.5个vol点。嗯,从那以后我再也不敢用免费数据做策略了。

16.2 数据字段:你需要拿哪些东西?

做波动率曲面,不是把行情数据拉下来就完事。你需要的是完整的期权链快照。我一般要求至少包含以下字段:

  • 合约代码:唯一标识,注意区分不同月份和行权价
  • 交易时间:精确到毫秒的时间戳
  • 最新价:期权合约的成交价
  • 行权价:K值,这是曲面横轴
  • 剩余到期时间:T值,这是曲面纵轴
  • 标的资产价格:比如50ETF的现价
  • 无风险利率:一般用Shibor 1M或国债收益率
  • 隐含波动率:这个最好自己算,别信第三方给的
  • 买卖价差:判断流动性,价差太大的合约直接扔掉
  • 持仓量:辅助判断合约活跃度
💡 我的小技巧: 每次拉数据时,顺手把标的资产价格也拉下来。为什么?因为后面算隐含波动率时,标的价稍微偏一点,IV就差好几个点。我见过有人用收盘价算盘中IV,结果曲面直接变形。

16.3 数据获取的代码实现

这里给一个我用Python写的通用数据获取框架。你根据自己的数据源改一下接口就行。

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

def fetch_option_chain_data(source='wind', symbol='50ETF', date=None):
    """
    获取期权链快照数据
    """
    if date is None:
        date = datetime.now().strftime('%Y%m%d')
    
    # 这里以Wind接口为例
    # 实际使用时替换成你的数据源API
    
    # 1. 获取所有期权合约代码
    option_codes = get_all_option_codes(symbol, date)
    
    # 2. 批量拉取行情数据
    raw_data = []
    for code in option_codes:
        quote = get_quote(code, date)
        raw_data.append({
            'code': code,
            'time': quote['time'],
            'last_price': quote['last_price'],
            'strike': quote['strike_price'],
            'expiry': quote['expiry_date'],
            'underlying': quote['underlying_price'],
            'bid': quote['bid_price'],
            'ask': quote['ask_price'],
            'open_interest': quote['open_interest']
        })
    
    df = pd.DataFrame(raw_data)
    return df

你想想看,如果每次只拉一个合约,100个合约就要循环100次,效率太低了。我建议用批量接口,一次拉完整个链。

16.4 数据清洗:脏数据是策略的杀手

数据拿到手,别急着算曲面。先做清洗。我总结了一套「四步清洗法」:

第一步:剔除无效合约

有些合约一天都没成交一笔,持仓量为0,这种直接扔掉。还有快到期的合约(T+0或T+1),流动性极差,也建议剔除。

第二步:处理异常价格

期权价格不能为负,这个不用我说。但还有更隐蔽的问题——买卖价差过大。我一般设定一个阈值:如果(ask - bid) / mid_price > 30%,就认为这个合约流动性太差,直接过滤掉。

🔍 实战经验: 我曾经在实盘中发现,某个深度虚值认购期权的隐含波动率突然飙到80%。一查,原来是最后一笔成交是1手,价格被拉上去了。这种「假信号」如果不清洗,曲面套利策略会疯狂开仓,然后亏得你怀疑人生。

第三步:时间对齐

不同合约的成交时间可能不同。比如9:35:01秒,有的合约成交了,有的没成交。你需要做时间对齐——要么用最新价填充,要么用插值法。我个人习惯用「前向填充」,也就是用最近一笔成交价代替。

第四步:计算隐含波动率

这一步最容易被忽视。很多数据源直接给你一个「IV」字段,但你要问自己:它用的定价模型是什么?利率参数是多少?股息率考虑了没有?

我建议自己算IV。用经典的Newton-Raphson方法,迭代求解Black-Scholes公式:

def calculate_implied_volatility(option_price, S, K, T, r, option_type='call'):
    """
    使用Newton-Raphson方法计算隐含波动率
    """
    from scipy.stats import norm
    
    def bs_price(sigma):
        d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
        d2 = d1 - sigma*np.sqrt(T)
        if option_type == 'call':
            return S*norm.cdf(d1) - K*np.exp(-r*T)*norm.cdf(d2)
        else:
            return K*np.exp(-r*T)*norm.cdf(-d2) - S*norm.cdf(-d1)
    
    # 初始猜测
    sigma = 0.3
    for i in range(100):
        price = bs_price(sigma)
        vega = calculate_vega(S, K, T, r, sigma)
        diff = price - option_price
        if abs(diff) < 1e-6:
            break
        sigma = sigma - diff / vega
    
    return sigma

16.5 数据预处理:为曲面构建做准备

清洗完的数据,还需要做几步预处理,才能用来构建波动率曲面。

第一步:标准化时间
把剩余到期时间统一转换成「年化」形式。比如还有30天到期,T = 30/365。注意,国内期权市场用自然日还是交易日?我建议用自然日,因为波动率是连续累积的。

第二步:标准化行权价
用「货币化程度」代替绝对行权价。公式是:moneyness = K / S。这样不同标的、不同时间点的合约才能放在一起比较。

第三步:剔除异常IV点
计算完IV后,你会发现有些合约的IV明显偏离周围合约。比如深度实值期权的IV经常异常高。我一般用3倍标准差法剔除:

def remove_outliers(df, column='implied_vol', z_thresh=3):
    mean = df[column].mean()
    std = df[column].std()
    df = df[(df[column] > mean - z_thresh*std) & 
            (df[column] < mean + z_thresh*std)]
    return df
⚠️ 注意: 剔除异常值要谨慎。有时候IV异常恰恰是套利机会所在。我建议先保留原始数据,在构建曲面时再做平滑处理,而不是直接扔掉。

16.6 数据存储:别让清洗白费

清洗好的数据,建议存成HDF5或Parquet格式。为什么不用CSV?因为期权数据量大——一天全市场快照可能有几千条记录,一年下来就是几百万条。CSV读写太慢,而且不支持列式压缩。

我的存储结构是这样的:

# 按日期分文件存储
data/
  2024/
    01/
      2024-01-02.h5
      2024-01-03.h5
      ...
    02/
      ...
  
# 每个H5文件内按合约类型分组
/50ETF/call/2024-01-02
/50ETF/put/2024-01-02
/300ETF/call/2024-01-02

这样查询某一天的数据,直接读一个文件就行,不用全量扫描。

16.7 数据质量检查清单

每次跑完清洗流程,我都会过一遍这个清单:

  • ✅ 所有合约的标的资产价格是否一致?如果不一致,说明时间没对齐
  • ✅ 隐含波动率是否在合理范围(5%-80%)?超出这个范围的,大概率是数据问题
  • ✅ 同一行权价、不同到期日的合约,IV是否呈现合理的期限结构?如果出现倒挂,先别急着套利,检查数据
  • ✅ 买卖价差是否都在阈值以内?价差大的合约,即使有套利机会也吃不到
  • ✅ 数据量是否完整?比如某一天突然少了50%的合约,可能是接口出了问题
💡 我的习惯: 每天开盘前,我会先跑一遍数据质量检查脚本。如果发现异常,直接发邮件报警。嗯,宁可错过一天的交易机会,也别用脏数据做决策。

16.8 本章知识体系

下面这张图,概括了数据获取与清洗的完整流程:

数据获取与清洗流程 数据源选择 字段获取 数据清洗 交易所直连 金融数据终端 第三方量化平台 ⚠ 注意数据延迟 合约代码、时间戳 最新价、行权价 剩余到期时间 ⚠ 标的价必须同步 剔除无效合约 处理异常价格 时间对齐 ⚠ 自己算IV 数据预处理 数据存储 质量检查 标准化时间(年化) 标准化行权价(moneyness) 剔除异常IV点 HDF5 / Parquet格式 按日期分文件 按合约类型分组 IV范围检查 期限结构检查 数据完整性检查

数据获取与清洗,是整个波动率曲面套利策略的基石。你想想看,如果地基没打好,上面盖的房子再漂亮也没用。我见过太多人花90%的时间写策略,只花10%的时间处理数据——结果就是策略在回测里跑得飞起,实盘一塌糊涂。

记住一句话:垃圾进,垃圾出。数据质量决定了你的策略天花板。