数据获取与清洗:期货Tick/分钟/日线数据来源、数据格式解析、缺失值处理、异常值检测与修复、数据对齐与重采样
做量化交易,有一句话我特别认同:数据决定策略的天花板,而清洗决定你能不能摸到天花板。
期货数据不像股票那样好拿,Tick 数据更是金贵。我刚开始做期货量化那会儿,花在数据上的时间比写策略还多。今天咱们就把这块硬骨头啃下来。
数据来源:你的弹药库在哪?
我个人习惯把数据来源分成三类:
- 交易所直连:比如 CTP(综合交易平台)。延迟最低,但需要期货公司授权,而且你得自己存。适合高频策略。
- 第三方数据商:像 Wind、聚宽、Tushare、RiceQuant。省心,但 Tick 数据通常要付费。适合回测和研究中低频策略。
- 开源/社区数据:比如一些 GitHub 项目爬取的。嗯,这里要提醒一句:用之前一定检查数据完整性,我踩过坑,少了几天的数据,回测曲线漂亮得不像话,实盘直接崩。
数据格式解析:Tick、分钟、日线长什么样?
说白了,不同频率的数据,就是不同时间颗粒度的快照。
| 数据类型 | 核心字段 | 典型行数/天(单品种) |
|---|---|---|
| Tick | 时间戳、最新价、成交量、持仓量、买卖盘口(五档或十档) | 4000~8000 行(取决于品种活跃度) |
| 1分钟线 | 时间戳、开高低收、成交量、持仓量 | 约 270 行(4小时夜盘+3小时日盘) |
| 日线 | 日期、开高低收、成交量、持仓量 | 1 行 |
举个例子,一个典型的 Tick 数据 CSV 长这样:
datetime,last_price,volume,open_interest,bid_price1,bid_volume1,ask_price1,ask_volume1
2024-01-15 09:00:00.500,3850.0,1200,50000,3849.5,10,3850.5,15
2024-01-15 09:00:00.750,3850.5,1201,50000,3850.0,8,3851.0,12
注意那个时间戳,精确到毫秒甚至微秒。我见过有人把时间戳当字符串处理,排序时按字典序排,结果 09:00:00.500 排到了 09:00:00.1000 后面——这显然是错的。
缺失值处理:数据里的「黑洞」
为什么会缺失?原因很多:网络断连、交易所休市、合约换月、甚至数据商抽风。
我的处理原则是:先分类,再动手。
- 连续缺失(比如连续 5 分钟没数据):直接标记为 NaN,或者用前一个有效值填充(ffill)。但要注意,如果缺失发生在开盘或收盘附近,ffill 可能会引入偏差。
- 单点缺失(比如某个 Tick 丢了):用前后 Tick 的均值插值。对于分钟线,可以用线性插值。
- 合约换月导致的「缺失」:这不是真缺失,是数据源切换。我建议用主力合约连续数据,或者自己写个换月逻辑。
异常值检测与修复:揪出那些「离谱」的数据
异常值比缺失值更隐蔽,也更危险。它会让你的策略在回测里看起来完美,实盘却一塌糊涂。
我常用的方法有几种:
- 价格突变检测:如果当前 Tick 价格相比前一个 Tick 变动超过 1%(或者 3 个标准差),就标记为异常。举个例子,螺纹钢正常波动 1~2 个点,突然跳了 50 个点,那大概率是数据错误。
- 成交量异常:成交量突然放大到前 10 根 K 线均值的 10 倍以上,且没有对应的价格大幅波动,可能是数据商合并了多笔交易。
- 买卖盘口倒挂:买一价高于卖一价。这在正常市场里几乎不可能发生。如果出现,直接丢弃该 Tick。
数据对齐与重采样:让不同频率的数据「对上话」
这是最考验耐心的一步。你的策略可能需要同时用到 Tick 数据和 1 分钟数据,但它们的索引(时间戳)往往对不齐。
对齐的核心思路是:以低频数据的时间戳为基准,将高频数据聚合到该时间点上。
举个例子,把 Tick 数据重采样成 1 分钟线:
import pandas as pd
# 假设 df_tick 是 Tick 数据,索引是 datetime
df_1min = df_tick.resample('1min', label='right', closed='right').agg({
'last_price': 'ohlc', # 生成开高低收
'volume': 'sum',
'open_interest': 'last'
})
这里有个细节:label 和 closed 参数决定了时间窗口的归属。我个人习惯用 label='right', closed='right',这样 09:01:00 这根 K 线包含的是 09:00:00 到 09:00:59.999 的数据。不同数据商可能用不同的约定,一定要先确认。
对齐不同品种的数据时,我建议用 pd.merge_asof。它比普通的 merge 更智能,能自动找到最近的时间戳进行匹配。
# 对齐螺纹钢和铁矿石的 1 分钟数据
df_merged = pd.merge_asof(df_rb_1min, df_i_1min, on='datetime', direction='nearest')
嗯,这里要注意:merge_asof 要求两个 DataFrame 都按时间排序。我吃过亏,没排序直接 merge,结果数据全乱了。
知识体系总览
下面这张图,是我自己梳理的数据处理流程。每次拿到新数据,我都会按这个步骤走一遍。
你看,整个流程是环环相扣的。数据没拿对,后面全白干。格式没解析好,缺失值处理就会出错。所以我的建议是:每做完一步,都停下来看一眼数据。画个图,打印几行,确认没问题再往下走。