第一章:数据获取与清洗——打好地基才能盖高楼

做波动率套利,说白了就是跟数据打交道。我见过太多人一上来就搞模型、写策略,结果数据源没搞定,回测跑出来全是噪音。嗯,这章我们就聊聊最基础但也最容易踩坑的部分——数据获取与清洗。

1.1 获取期权链数据

期权链数据,就是某个标的物所有到期日、所有行权价的期权合约报价。我个人习惯用 yfinance 或者 tushare 来拉数据。为什么?因为免费、够用、社区活跃。

举个例子,你想拿上证50ETF的期权数据:

import yfinance as yf
import pandas as pd

# 获取上证50ETF期权链
ticker = "510050.SS"
stock = yf.Ticker(ticker)
expirations = stock.options  # 获取所有到期日

# 选一个最近的到期日
opt_chain = stock.option_chain(expirations[0])
calls = opt_chain.calls
puts = opt_chain.puts

print(calls.head())

这里要注意:yfinance 拿到的期权数据是延迟15分钟的。如果你做日内交易,这个延迟会让你亏到怀疑人生。我在项目中遇到过这种情况,后来换成了券商提供的实时接口。

⚠️ 避坑指南: 我曾经用免费数据跑回测,结果发现买卖价差被忽略了。回测年化收益30%,实盘一跑直接亏手续费。记住:期权数据一定要包含 bid/ask 价格,别只看 last price。

1.2 获取标的资产数据

标的资产数据相对简单。你需要的是:开盘价、收盘价、最高价、最低价、成交量。这些数据用来计算波动率、Delta、Gamma 等 Greeks。

我建议用 yfinance 拉日线数据,然后用 pandas 做对齐:

# 获取标的资产日线数据
stock_data = yf.download("510050.SS", start="2024-01-01", end="2024-12-31")
stock_data = stock_data[['Open', 'High', 'Low', 'Close', 'Volume']]
stock_data.columns = ['open', 'high', 'low', 'close', 'volume']
print(stock_data.tail())

你想想看,如果标的资产数据和期权数据的时间戳对不上,算出来的隐含波动率就是错的。嗯,这里要注意:一定要用同一个数据源,或者确保时间戳精确到秒级对齐。

1.3 数据清洗与对齐

数据清洗,说白了就是去掉脏数据。常见的脏数据有:

  • 空值:某个时间点没有报价
  • 异常值:价格突然跳变(比如从3块跳到300块)
  • 重复数据:同一个时间戳出现两次
  • 时间戳错位:期权数据和标的资产数据时间不对齐

我一般这样处理:

# 清洗期权数据
opt_data = opt_data.dropna()  # 去掉空值
opt_data = opt_data[opt_data['bid'] > 0]  # 去掉bid为0的合约
opt_data = opt_data[opt_data['ask'] > opt_data['bid']]  # 确保ask > bid

# 对齐时间戳
stock_data.index = pd.to_datetime(stock_data.index)
opt_data.index = pd.to_datetime(opt_data.index)

# 只保留两个数据集都有的时间点
common_times = stock_data.index.intersection(opt_data.index)
stock_data = stock_data.loc[common_times]
opt_data = opt_data.loc[common_times]
💡 个人经验: 我习惯在清洗后加一个「合理性检查」。比如:期权价格不能低于内在价值,隐含波动率不能为负。这些检查能帮你提前发现数据源的问题。

1.4 数据存储方案

数据存哪里?我推荐用 HDF5 或者 Parquet 格式。为什么不用 CSV?因为 CSV 存期权链数据太慢了,而且不支持压缩。

我的存储方案是这样的:

import h5py

# 存储到 HDF5 文件
with h5py.File('option_data.h5', 'w') as f:
    f.create_dataset('calls', data=calls.values)
    f.create_dataset('puts', data=puts.values)
    f.create_dataset('stock', data=stock_data.values)
    f.attrs['description'] = '上证50ETF期权数据'

如果你需要频繁读取,可以考虑用 SQLite 或者 DuckDB。我个人偏好 DuckDB,因为它支持 SQL 查询,而且速度比 Pandas 快很多。

📌 核心要点:
  • 期权数据必须包含 bid/ask,别只看 last price
  • 标的资产数据和期权数据必须时间对齐
  • 清洗时做合理性检查,别偷懒
  • 存储用 HDF5 或 Parquet,别用 CSV

1.5 知识体系图

下面这张图展示了本章的核心逻辑:

数据获取与清洗 获取期权链数据 获取标的资产数据 数据清洗与对齐 数据存储方案 核心流程:数据源 → 清洗 → 对齐 → 存储 → 回测 注意:每一步都要做合理性检查,否则后面全是垃圾

这张图其实就说明了一件事:数据流是单向的,但每一步都要回头检查。我见过太多人把数据拉下来就直接跑模型,结果回测漂亮、实盘惨淡。嗯,数据清洗这一步,值得你花 70% 的时间。

🔧 实用技巧: 我习惯在清洗完数据后,手动抽查几个时间点的数据。比如:随机选 5 个时间戳,对比期权价格和标的资产价格是否合理。这个小习惯帮我避免过好几次数据源出错的灾难。

蓝海数据掘金营,专注资料整理