3、市场数据获取与清洗:期权链数据、标的资产数据、数据源选择(如Wind、Tushare)、数据清洗与对齐
做期权回测,最怕什么?
不是策略写错了,是数据有问题。数据错了,再牛的策略也是白搭。我见过太多人,策略跑出来曲线漂亮得不行,结果一查,是数据对齐出了偏差——期权数据和标的物数据差了那么几毫秒,整个回测就失真了。
这一章,咱们就聊聊怎么把数据这关把好。
3.1 数据源选择:Wind vs Tushare,怎么选?
做量化,数据源是第一步。国内主流就两个:Wind(万得)和 Tushare(通联数据)。
我个人习惯是:生产环境用 Wind,研究环境用 Tushare。为什么?
- Wind:数据全、质量高、实时性好。但贵,一年几万块。适合机构或资金充裕的团队。
- Tushare:免费(积分制),社区活跃,接口友好。但数据有延迟,历史数据偶尔有缺失。适合个人研究、策略验证。
嗯,这里要注意:千万别混用。我见过有人 Wind 拿期权数据,Tushare 拿标的物数据,结果两个数据源的时间戳格式不一样,对齐时出了大问题。选一个,用到底。
3.2 期权链数据:你需要哪些字段?
期权链数据,说白了就是一张大表。每一行是一个期权合约,每一列是一个属性。你需要关注的核心字段有这些:
| 字段 | 说明 | 重要性 |
|---|---|---|
| 合约代码 | 唯一标识,如 "510050C2303M03000" | 必选 |
| 交易日期 | 数据日期,格式 YYYY-MM-DD | 必选 |
| 行权价 | 期权合约约定的行权价格 | 必选 |
| 到期日 | 合约到期日期 | 必选 |
| 看涨/看跌 | Call 或 Put | 必选 |
| 收盘价 | 当日收盘价(或结算价) | 必选 |
| 成交量 | 当日成交量 | 推荐 |
| 持仓量 | 当日持仓量 | 推荐 |
| 隐含波动率 | 由期权价格反推的波动率 | 可选 |
你想想看,做期权做市,核心就是看隐含波动率和实际波动率的差值。所以隐含波动率这个字段,虽然数据源不一定直接给,但你自己得算出来。
3.3 标的资产数据:别小看它
期权是衍生品,它的价格跟标的物高度相关。所以标的资产数据必须同步获取。
对于 50ETF 期权,标的物就是 510050 这只 ETF。你需要的数据包括:
- 日线数据:开盘价、收盘价、最高价、最低价、成交量
- 分钟线数据:如果做高频回测,至少需要 1 分钟或 5 分钟线
- 分红除权信息:ETF 分红会影响期权合约的调整,这个很容易被忽略
3.4 数据清洗:脏数据怎么处理?
数据拿到手,别急着用。先洗一洗。我一般按以下步骤来:
- 去重:同一合约同一天出现多条记录,保留第一条或最后一条
- 缺失值处理:成交量、持仓量为空的行,直接删除。收盘价为空,用前一日数据填充(但要注意:如果连续多日缺失,说明合约可能已经退市)
- 异常值检测:比如某期权收盘价突然涨了 1000%,明显是数据错误。用 3-sigma 或 IQR 方法剔除
- 合约状态过滤:只保留正常交易的合约,剔除已到期、暂停交易的合约
说白了,数据清洗就是「去伪存真」。你想想看,如果数据里混了一个错误的价格,回测时可能就会触发一次虚假的套利信号,整个策略评估就偏了。
3.5 数据对齐:期权和标的物必须同步
这是最容易被忽视的一步,也是最重要的一步。
期权数据和标的物数据,时间戳必须严格对齐。比如你用 50ETF 的 1 分钟线,那期权数据也得是 1 分钟线,而且时间点要完全一致。
我遇到过的情况:Tushare 的期权数据是 15:00 收盘,但标的物数据是 15:01 收盘。差了这 1 分钟,计算出来的期权理论价格就完全不对了。
对齐的方法很简单:
- 以标的物数据的时间轴为基准
- 将期权数据按时间戳匹配到最近的标的物数据点上
- 如果某个时间点没有期权数据,用前向填充(forward fill)
3.6 知识体系总览
下面这张图,是我自己整理的数据获取与清洗流程。你可以照着这个框架来搭建你的数据管道。
3.7 代码示例:用 Tushare 获取期权数据
下面是一个简单的示例,展示如何用 Tushare 获取 50ETF 期权链数据:
import tushare as ts
import pandas as pd
# 设置 token(需要先在 tushare 官网注册)
ts.set_token('your_token_here')
pro = ts.pro_api()
# 获取 50ETF 期权合约列表
# 注意:Tushare 的期权数据需要先获取合约列表,再获取行情
opt_basic = pro.opt_basic(exchange='SSE',
fields='ts_code,name,exercise_type,list_date,delist_date')
print(opt_basic.head())
# 获取某合约的历史行情
# 以 50ETF 购 3 月 3000 为例
df = pro.opt_daily(ts_code='510050C2303M03000',
start_date='20230101',
end_date='20231231',
fields='trade_date,open,high,low,close,vol,amount')
print(df.head())
3.8 数据对齐实战:分钟级对齐
假设你拿到了期权 1 分钟数据和标的物 1 分钟数据,怎么对齐?
# 假设 opt_df 是期权分钟数据,underlying_df 是标的物分钟数据
# 两者都有 'datetime' 列
# 以标的物数据为基准
aligned_df = underlying_df.copy()
# 将期权数据按时间匹配到最近的标的物时间点
# 使用 merge_asof 进行时间对齐
aligned_df = pd.merge_asof(aligned_df,
opt_df,
on='datetime',
direction='nearest',
tolerance=pd.Timedelta('1min'))
# 检查对齐后的数据
print(aligned_df.head())
嗯,这里要注意:merge_asof 要求两个 DataFrame 都按时间排序。如果数据量很大,建议先排序再合并,否则会报错。
好了,数据获取与清洗这块就聊到这儿。记住一句话:数据质量决定回测质量。花 80% 的时间在数据上,剩下的 20% 写策略,这才是做量化该有的态度。