第一章:数据获取与清洗——打好地基才能盖高楼
做波动率套利,说白了就是跟数据打交道。我见过太多人一上来就搞模型、写策略,结果数据源没搞定,回测跑出来全是噪音。嗯,这章我们就聊聊最基础但也最容易踩坑的部分——数据获取与清洗。
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 个时间戳,对比期权价格和标的资产价格是否合理。这个小习惯帮我避免过好几次数据源出错的灾难。