4. 数据源与数据获取
在Tick事件回放与撮合仿真中,数据源决定了回放的真实性与撮合结果的可信度。很多初学者把精力全部放在撮合算法上,却忽略了数据获取与清洗环节,最终导致回放价格跳变、时间戳乱序、成交量对不上。本节围绕“数据从哪里来、怎么下载、如何清洗、统一成什么格式”四个问题展开,帮助你搭建一条稳定可复用的数据链路。
一、核心原理:Tick数据的三种来源与数据链路
Tick数据本质上是带时间戳的逐笔成交或逐笔委托记录。按获取方式,常见数据源可分为三类:
- 实时行情接口:交易所或行情商提供的WebSocket/TCP推送,如国内期货CTP、股票Level-2、加密货币交易所的WebSocket。特点是低延迟、字段丰富,但需要处理断线重连与订阅管理。
- REST/HTTP历史接口:通过HTTP请求按时间区间拉取历史成交,如币安的aggTrades、部分券商的历史成交查询。适合小批量补数,但受限于频率与分页。
- 离线历史数据文件:交易所或数据商提供的CSV、Parquet、二进制打包文件,按日或按月下载。适合大规模回测,但格式差异大、需要清洗。
无论哪种来源,最终都要经过“采集→落地→清洗→标准化→入库”这条链路。下图展示了从多源数据到统一Tick格式的完整数据流。
二、方法流程:历史数据下载与清洗步骤
以加密货币交易所的逐笔成交为例,一个可复用的下载与清洗流程通常包含以下步骤:
- 确定时间范围与标的:明确交易对、起止日期,按天或按月切分,避免单次请求过大。
- 调用接口或下载文件:REST接口注意分页与限频;离线文件注意校验MD5,防止下载不完整。
- 字段映射:把不同来源的字段统一为:
timestamp, symbol, price, volume, side, trade_id。 - 时间戳标准化:统一为毫秒或微秒UTC,避免本地时区混用。
- 去重与排序:按
trade_id去重,按时间戳升序排列。 - 异常值处理:剔除价格为0、成交量为负、时间戳越界的记录。
- 落地存储:输出为Parquet或CSV,按日期分区,便于回放时按需加载。
下面是一段Python清洗示例,展示如何把原始DataFrame标准化为统一Tick格式:
import pandas as pd
def normalize_ticks(df, symbol):
df = df.rename(columns={
'T': 'timestamp', 'p': 'price', 'q': 'volume',
'm': 'is_buyer_maker', 'a': 'trade_id'
})
df['symbol'] = symbol
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms', utc=True)
df['side'] = df['is_buyer_maker'].apply(lambda x: 'sell' if x else 'buy')
df = df[['timestamp', 'symbol', 'price', 'volume', 'side', 'trade_id']]
df = df.drop_duplicates(subset=['trade_id'])
df = df[(df['price'] > 0) & (df['volume'] > 0)]
df = df.sort_values('timestamp').reset_index(drop=True)
return df
三、案例场景:跨交易所数据对齐
假设你要回放某标的在两个交易所的Tick,并做跨市场套利仿真。两个交易所的时间戳精度不同:A所是毫秒,B所是微秒;字段名也不同。此时标准化格式的价值就体现出来了。你需要先分别清洗,再统一到同一时间基准,最后按时间戳归并。常见做法是:
- 统一使用UTC毫秒时间戳,微秒数据向下取整到毫秒。
- 为每条Tick增加
exchange字段,区分来源。 - 回放时使用最小堆按时间戳合并多路数据流。
建议在清洗阶段就固定Schema,并在入库时做一次Schema校验。这样回放引擎只需面对一种格式,撮合逻辑不会被数据差异污染。
四、误区与复盘:数据清洗中最容易踩的坑
在实际项目中,以下问题反复出现,值得警惕:
| 误区 | 后果 | 正确做法 |
|---|---|---|
| 时间戳混用本地时区 | 回放顺序错乱,跨日数据错位 | 统一UTC,入库前校验时区 |
| 不去重直接回放 | 成交量虚增,撮合结果失真 | 按trade_id去重 |
| 忽略接口限频 | 请求被封,数据缺口 | 加退避重试与断点续传 |
| 字段含义想当然 | 买卖方向反了,策略反向 | 对照官方文档确认字段语义 |
| 清洗后不校验 | 脏数据流入回放,难以定位 | 统计缺失率、重复率、时间跨度 |
清洗不是一次性工作。交易所接口字段可能变更,历史文件格式也可能升级。建议把清洗脚本纳入版本管理,并对每次下载的数据生成校验报告,记录行数、时间范围、去重数量,便于复盘。
总结来说,数据源与数据获取是Tick回放与撮合仿真的地基。选择合适的数据源,建立稳定的下载与清洗流程,统一为标准Tick格式,才能让后续的回放与撮合建立在可信的数据之上。把这条链路做扎实,后面的策略验证才有意义。