3. 数据获取与清洗:常见数据源、字段解析与处理技巧
做量化交易,尤其是商品期货,有一句老话:「数据决定下限,策略决定上限」。我个人做了这么多年风控,见过太多策略回测漂亮、实盘却崩盘的案例,十有八九是数据源头就出了问题。
这一章,咱们就聊聊数据获取与清洗那些事儿。说白了,就是怎么把「原材料」搞干净,让后续的策略能放心下锅。
3.1 常见数据源:你该选哪个?
国内做期货量化,主流的数据源就那么几个。我挨个说说我的使用感受。
3.1.1 Wind(万得)
金融圈的标配。数据全、质量高、更新快。但价格也贵,个人用户基本用不起。我建议机构团队直接上Wind,省心。
- 优点:覆盖全(期货、期权、现货、宏观),历史数据长,API稳定。
- 缺点:贵,接口是COM组件,Python调用略麻烦。
- 适用场景:机构实盘、深度研究。
3.1.2 Tushare
国内开源数据里做得最早的。免费版够用,但积分限制多。我早期做回测时常用它,后来数据量大了就转付费了。
- 优点:免费、社区活跃、文档清晰。
- 缺点:免费版有调用频率限制,历史数据偶尔有缺失。
- 适用场景:个人研究、小规模回测。
3.1.3 聚宽(JoinQuant)
量化平台自带数据,本地化做得不错。如果你用聚宽平台写策略,数据直接取就行,不用操心清洗。
- 优点:与平台无缝集成,数据预处理过,方便。
- 缺点:脱离平台后数据获取受限,自定义程度低。
- 适用场景:聚宽平台用户、快速原型验证。
3.2 期货数据字段解析
拿到数据后,先别急着跑策略。你得搞清楚每个字段到底代表什么。我见过有人把「结算价」当「收盘价」用,回测结果直接翻车。
以下是商品期货最核心的字段,我列个表:
| 字段名 | 含义 | 注意事项 |
|---|---|---|
| open | 开盘价 | 注意集合竞价时段,有些数据源会包含夜盘开盘 |
| high | 最高价 | 日内最高,注意涨跌停板时的价格 |
| low | 最低价 | 同上 |
| close | 收盘价 | 15:00的收盘价,夜盘品种注意区分 |
| settle | 结算价 | 关键字段:用于计算盈亏和保证金,不是收盘价! |
| volume | 成交量 | 注意是单边还是双边统计,国内期货是双边 |
| open_interest | 持仓量 | 反映市场参与度,换月时会有剧烈变化 |
| pre_settle | 前结算价 | 用于计算涨跌停板幅度 |
3.3 缺失值与异常值处理
数据拿到手,第一件事就是检查缺失值和异常值。我习惯用pandas的isnull()和describe()快速扫一眼。
3.3.1 缺失值处理
期货数据缺失常见原因:节假日、数据源漏采、合约刚上市或临近退市。
- 直接删除:如果缺失比例很小(<1%),直接dropna()。
- 向前填充:比如用前一天的结算价填充当天的缺失值。适合非活跃合约。
- 插值:线性插值或时间加权插值。我一般只在分钟数据上用。
import pandas as pd
# 示例:向前填充缺失的结算价
df['settle'].fillna(method='ffill', inplace=True)
# 或者用前一天的数据填充
df['settle'].fillna(df['pre_settle'], inplace=True)
3.3.2 异常值处理
异常值比缺失值更隐蔽。我常用的方法:
- 涨跌停板检查:如果价格超过当日涨跌停板,直接标记为异常。
- 3-sigma原则:计算收益率的标准差,超过3倍标准差的视为异常。
- 业务逻辑检查:比如成交量不能为负,持仓量不能小于0。
3.4 复权与连续合约构建
这是期货数据清洗里最头疼的一环。股票有复权,期货有换月。你想想看,一个合约只有几个月生命周期,你要做长周期回测,必须把不同合约连起来。
3.4.1 为什么要构建连续合约?
因为单个合约有到期日。你不能用主力合约直接做回测,否则换月那天会出现价格跳空,策略信号会乱掉。
3.4.2 常见的连续合约构建方法
- 主力合约连续:每天取持仓量最大的合约。简单,但换月时会有跳空。
- 加权连续:按持仓量加权多个合约。平滑,但计算复杂。
- 复权连续:在换月时对历史价格进行调整,消除跳空。我最推荐这种方法。
3.4.3 复权连续合约的构建逻辑
说白了,就是找到每次换月的价差,然后把这个价差累加到历史价格上。这样回测时就不会有跳空。
# 伪代码示例:复权连续合约构建
def build_adjusted_series(df_old, df_new, roll_date):
# 计算换月当天的价差
spread = df_new.loc[roll_date, 'close'] - df_old.loc[roll_date, 'close']
# 将价差累加到旧合约的历史价格上
df_old['close_adjusted'] = df_old['close'] + spread
# 拼接
return pd.concat([df_old, df_new])
3.5 本章知识体系
下面这张图,是我自己总结的数据获取与清洗的完整流程。你可以把它当作一个检查清单。
嗯,这张图基本把本章的核心逻辑串起来了。从数据源到最终可用的清洗数据,每一步都不能跳过。我见过太多人直接拿原始数据跑策略,结果换月那天信号乱跳,还以为是策略问题,查了半天才发现是数据没处理干净。