数据源与数据清洗:Tick级数据、Level 1/Level 2数据、数据质量检查与存储方案
做高频交易,数据就是你的石油。但原油不能直接烧,对吧?
我见过太多团队,策略逻辑写得天花乱坠,结果一跑实盘就崩。查到最后,问题全出在数据上。时间戳歪了、价格跳空了、买卖档对不上……这些坑,我几乎都踩过一遍。
今天咱们就把数据这块地基打牢。从数据源分类,到清洗检查,再到存储方案,一条线捋清楚。
4.1 数据源的层级:Tick、Level 1、Level 2
先搞清楚我们面对的是什么数据。交易所给的数据,按颗粒度分三层。
| 数据层级 | 包含内容 | 典型频率 | 用途 |
|---|---|---|---|
| Tick级 | 每一笔成交的逐笔数据(价格、数量、时间、成交标识) | 微秒级 | 订单流分析、微观结构建模 |
| Level 1 | 最优买卖价(Best Bid/Offer)、最新成交价、成交量 | 毫秒级或秒级快照 | 常规策略信号、盘口监控 |
| Level 2 | 多档买卖盘口(通常5-10档)、逐笔委托、逐笔成交 | 微秒级快照+逐笔流 | 订单簿重建、流动性分析、高频做市 |
我个人习惯,做高频策略至少要用Level 2数据。Level 1太粗糙了,你看不到订单簿的深度变化。举个例子,有一次我发现某只股票Level 1的买一价没变,但Level 2显示买二到买五全被撤单了——这明显是有人在挂假单诱多。如果你只看Level 1,就被骗进去了。
核心原则:策略的时间尺度越短,需要的数据颗粒度就越细。做秒级以上的策略,Level 1勉强够用;做毫秒级甚至微秒级的策略,必须上Tick级或Level 2。
4.2 数据质量检查:缺失值、异常值、时间戳对齐
数据拿到手,别急着用。先做三件事:查缺、查异、对时间。
4.2.1 缺失值处理
高频数据里缺失值很常见。网络抖动、交易所撮合引擎短暂卡顿、数据供应商丢包,都可能导致某一段数据空白。
怎么查?我一般用两个方法:
- 时间间隔检查:计算相邻两条数据的时间差。如果间隔超过正常频率的3倍以上,就标记为缺失区间。
- 成交量/笔数检查:如果某段时间内成交量为0,但前后都有活跃交易,那大概率是数据丢了。
处理方式分情况:
- 缺失区间很短(比如几毫秒),可以用前值填充或线性插值。
- 缺失区间较长(比如几秒以上),我建议直接丢弃这段数据。高频策略对数据真实性要求极高,插值出来的假数据比缺失更危险。
注意:千万不要在Tick级数据上做均值插值。Tick数据是离散事件,不是连续信号。你插一个中间价格出来,这个价格在真实市场中从未存在过,策略会学歪。
4.2.2 异常值检测
异常值在高频数据里很要命。一个错误的价格,可能让你的策略瞬间爆仓。
我遇到过最离谱的一次,某数据源把一笔成交价多打了一个零,100块变成1000块。我的策略以为价格暴涨,直接下了空单……还好风控拦住了。
常用的检测方法:
- 价格突变检测:计算相邻Tick的价格变化率。如果变化率超过阈值(比如0.5%),标记为可疑。
- 买卖价差检测:如果某时刻的买价高于卖价(价差为负),这肯定是数据错误。
- 成交量异常:单笔成交量超过该股票日均成交量的某个倍数(比如10倍),需要人工复核。
我的经验:异常值不要直接删除,先标记出来。有时候多个异常值同时出现,反而能发现数据源的系统性bug。我曾经通过分析异常值的分布规律,发现某个数据供应商在特定时间段会重复发送旧数据。
4.2.3 时间戳对齐
这是高频数据清洗里最头疼的一环。不同数据源的时间戳精度不一样,有的精确到毫秒,有的精确到微秒,有的甚至用的是本地时间而非交易所时间。
对齐的核心思路:
- 统一时区:全部转为交易所本地时间(比如美股用美东时间,A股用北京时间)。
- 统一精度:以精度最低的数据源为准,或者全部截断到毫秒级。
- 对齐算法:使用最近邻匹配。比如你有Tick级成交数据和Level 2快照数据,把每条成交数据匹配到离它最近的快照上。
# 伪代码:时间戳对齐示例
def align_timestamps(trade_data, snapshot_data, tolerance=1e-3):
"""
trade_data: 成交数据,时间戳精度微秒
snapshot_data: 快照数据,时间戳精度毫秒
tolerance: 最大允许时间差(秒)
"""
aligned = []
for trade in trade_data:
# 找到时间差最小的快照
nearest = find_nearest(snapshot_data, trade.timestamp)
if abs(nearest.timestamp - trade.timestamp) < tolerance:
aligned.append((trade, nearest))
return aligned
避坑指南:我曾经遇到过一个数据源,它的时间戳是数据到达服务器的时间,而不是交易所的撮合时间。这两者可能差几十毫秒。做高频策略时,几十毫秒的偏差足以让信号完全失效。一定要确认时间戳的基准。
4.3 数据存储方案:Parquet vs HDF5
清洗好的数据,得找个地方存起来。高频数据量巨大,一天可能几个GB,选错存储格式,读写速度会让你崩溃。
| 特性 | Parquet | HDF5 |
|---|---|---|
| 压缩率 | 高(列式存储,按列压缩) | 中(支持压缩,但不如Parquet) |
| 读取速度 | 快(列裁剪,只读需要的列) | 快(但读取整表时较慢) |
| 写入速度 | 中(需要先缓存再写入) | 快(支持追加写入) |
| 跨语言支持 | 好(Python、R、Java、C++都支持) | 好(但Python的h5py和R的rhdf5接口略有差异) |
| 适用场景 | 分析查询、机器学习特征工程 | 实时写入、大规模数据归档 |
我个人偏好Parquet。原因很简单:做量化研究时,我经常只需要读取某几列数据(比如价格和成交量),Parquet的列式存储让我不用加载整张表,速度快很多。
但如果你需要实时写入大量数据,HDF5的追加写入能力更友好。我有个朋友做高频回测系统,每天写入几十GB的Tick数据,他用HDF5,因为Parquet的写入需要先攒一批再写,延迟较高。
我的建议:研究阶段用Parquet,生产环境的数据管道用HDF5。或者两者都用——Parquet存分析用的宽表,HDF5存原始数据流。
4.4 知识体系总览
下面这张图,把本章的核心逻辑串起来了。从数据源到清洗,再到存储,每一步都有坑,每一步都有对策。
你看,整个流程其实不复杂。但每一步都有细节。数据源选不对,后面全白干;清洗不干净,策略就是垃圾进垃圾出;存储选不好,回测跑得比乌龟还慢。
我刚开始做高频的时候,觉得策略才是核心,数据只是工具。后来被市场教育了几次,才明白:数据质量决定了策略的上限,策略代码只是把这个上限兑现出来而已。