2. 逐笔委托数据结构解析:Level2行情数据字段详解
好,咱们直接进入正题。上一章聊了订单流分析的价值,这一章就得动手了——先把数据搞清楚。
说实话,我刚开始接触Level2数据时,第一反应是「这玩意儿怎么这么乱?」。一堆字段,每个交易所的命名还不一样。但后来我发现,只要抓住几个核心字段,剩下的都是锦上添花。
今天咱们就拆解逐笔委托数据的核心结构。我会结合自己踩过的坑,帮你把这块啃下来。
2.1 逐笔委托的核心字段
逐笔委托数据,说白了就是交易所把每一笔挂单和撤单都记录下来了。每条记录包含以下几个关键字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
| OrderID | 字符串 | 委托单的唯一标识 |
| Price | 浮点数 | 委托价格(单位:元) |
| Volume | 整数 | 委托数量(单位:股/手) |
| Side | 字符 | 买卖方向:'B' 买,'S' 卖 |
| Time | 时间戳 | 委托进入系统的时间(精确到毫秒) |
嗯,就这五个字段。别小看它们,订单流分析的所有秘密都藏在这里。
OrderID:每笔订单的身份证
OrderID 是唯一的。同一个订单如果被部分成交,剩下的部分会保留同一个 OrderID。我在项目中遇到过一个问题:有些交易所的 OrderID 是递增的,有些则是随机生成的。如果你要做订单生命周期追踪,这个细节很关键。
Price 和 Volume:最直观的博弈数据
Price 和 Volume 组合起来,就是「有人在某个价位挂了多少钱」。举个例子,如果看到连续多个买单在 10.00 元挂了 1000 手,而卖单在 10.01 元只有 200 手,那说明买方在死守 10.00 这个价位。
关键点:Volume 的单位要搞清楚。A股是「股」,但有些数据商给的是「手」(1手=100股)。我吃过这个亏,算出来的资金流差了一个数量级。
Side:买卖方向,但没那么简单
Side 字段只有 'B' 和 'S' 两种值。但这里有个坑:有些数据源把主动买入标记为 'B',被动卖出标记为 'S';有些则反过来。你想想看,如果方向搞反了,你的订单流分析就全错了。
我个人习惯是:拿到数据后先做一个小样本验证。找一笔已知的主动买入,看看 Side 字段是不是 'B'。这一步花不了几分钟,但能避免后面的大麻烦。
Time:毫秒级的战争
Time 字段精确到毫秒。为什么这么重要?因为高频交易就是在毫秒级别抢单的。我曾经分析过一只股票的逐笔数据,发现某个大单在 09:30:00.000 到 09:30:00.500 这 500 毫秒内,被拆成了 20 笔小单。这就是典型的「冰山订单」——大资金在隐藏自己的真实意图。
2.2 数据清洗与预处理实战
原始数据拿到手,第一件事不是分析,而是清洗。我见过太多人直接拿原始数据跑模型,结果跑出来一堆垃圾。
下面是我总结的清洗流程,每一步都有血的教训。
第一步:处理缺失值
逐笔数据中,缺失值通常出现在 Volume 或 Price 字段。比如撤单记录,有些数据源会把 Volume 设为 0 或 null。
# 示例:处理缺失值
import pandas as pd
df = pd.read_csv('tick_data.csv')
# 检查缺失值
print(df.isnull().sum())
# 处理方式:删除或填充
# 我个人建议删除,因为逐笔数据量很大,缺失几条不影响统计
df = df.dropna(subset=['Price', 'Volume'])
注意:不要直接 dropna() 全部删除。先看看缺失值占比。如果超过 5%,说明数据源有问题,需要排查。
第二步:去重
同一个 OrderID 可能因为网络重传出现多次。去重时要注意:保留时间戳最早的那条记录。
# 去重:按 OrderID 去重,保留最早的时间
df = df.sort_values('Time').drop_duplicates(subset='OrderID', keep='first')
我曾经遇到过一个极端情况:某只股票在开盘瞬间,同一个 OrderID 出现了 3 次,时间戳相差不到 1 毫秒。如果不做去重,你的订单流统计会多出 2 倍的虚假数据。
第三步:时间戳对齐
不同数据源的时间戳格式可能不一样。有的是 '2024-01-01 09:30:00.123',有的是 1704061800123(毫秒级时间戳)。统一转换成 datetime 格式,方便后续分析。
# 时间戳转换
df['Time'] = pd.to_datetime(df['Time'], unit='ms') # 如果是毫秒级时间戳
第四步:异常值过滤
Price 为负数、Volume 为 0、Side 不是 'B' 或 'S'——这些都属于异常值。直接过滤掉。
# 过滤异常值
df = df[(df['Price'] > 0) & (df['Volume'] > 0)]
df = df[df['Side'].isin(['B', 'S'])]
小技巧:过滤完后,打印一下数据量变化。如果过滤掉了超过 10% 的数据,说明原始数据质量堪忧,建议换数据源。
2.3 知识体系结构图
下面这张图是我自己整理的逐笔委托数据解析的知识结构。你可以把它当作一个检查清单,看看自己有没有遗漏。
2.4 实战:从原始数据到可用数据
光说不练假把式。咱们用一段真实数据走一遍流程。
假设原始数据长这样(CSV格式):
OrderID,Price,Volume,Side,Time
ORD001,10.00,1000,B,2024-01-01 09:30:00.123
ORD002,10.01,500,S,2024-01-01 09:30:00.456
ORD003,10.00,0,B,2024-01-01 09:30:00.789 # Volume为0,异常
ORD001,10.00,1000,B,2024-01-01 09:30:00.123 # 重复记录
ORD004,-1.00,200,S,2024-01-01 09:30:01.000 # 价格为负,异常
清洗后的结果:
OrderID,Price,Volume,Side,Time
ORD001,10.00,1000,B,2024-01-01 09:30:00.123
ORD002,10.01,500,S,2024-01-01 09:30:00.456
你看,5条原始数据,清洗后只剩2条。这就是现实——数据质量从来不会完美。
我的建议:把清洗流程写成函数,每次拿到新数据直接调用。别每次都手动处理,效率太低。
好了,这一章的内容就到这。逐笔委托数据的结构其实不复杂,但细节决定成败。下一章咱们会基于清洗后的数据,开始做真正的订单流分析。