Level2行情数据解析:逐笔成交、逐笔委托、十档行情、快照与增量
做市交易这行,数据就是你的眼睛。而Level2行情,说白了就是给你配了一副高清夜视仪。
普通行情只能看到五档买卖盘,每秒刷新一次。Level2呢?十档行情、逐笔成交、逐笔委托,还有快照和增量数据。我刚开始接触时也觉得眼花缭乱,但用顺手了就会发现——这些数据里藏着太多赚钱的信号。
Level2到底多了什么?
先看一张对比表,你就明白了:
| 数据维度 | 普通Level1 | Level2 |
|---|---|---|
| 买卖盘档位 | 5档 | 10档(部分市场支持更多) |
| 刷新频率 | 约3秒一次 | 约0.5秒一次,甚至实时推送 |
| 逐笔成交 | 无 | 有(每笔交易的详细记录) |
| 逐笔委托 | 无 | 有(每笔挂单的详细记录) |
| 数据推送方式 | 全量快照 | 快照 + 增量更新 |
嗯,这里要注意。Level2的核心优势不是「多五档」,而是「逐笔」和「增量」。这两个东西,才是做市策略的灵魂。
逐笔成交:每一笔交易都逃不过你的眼睛
逐笔成交数据,记录的是每一笔实际成交的订单。它包含什么?
- 成交时间:精确到毫秒甚至微秒
- 成交价格:这笔交易的实际成交价
- 成交数量:这笔交易成交了多少手
- 成交方向:主动买还是主动卖(也就是常说的「主买」和「主卖」)
- 成交编号:交易所给这笔交易分配的唯一ID
我在项目中遇到过一件事。有一次做回测,发现某个策略在模拟环境里跑得挺好,一上实盘就亏。后来查了半天,问题出在「成交方向」的判断上。模拟环境里我用的是Tick数据的买卖标识,但实盘里有些交易所的逐笔成交数据,方向标识是反的。你想想看,方向都搞反了,策略能赚钱才怪。
逐笔委托:看透订单簿的「前世今生」
逐笔委托比逐笔成交更底层。它记录的是每一笔挂单、撤单、成交的完整生命周期。
举个例子。你在盘口看到卖一价10.00元挂了1000手,下一秒变成了800手。普通行情只告诉你「卖一数量变了」,但逐笔委托会告诉你:
- 有人撤单了200手
- 或者有人成交了200手
- 甚至可能是有人修改了订单(先撤再挂)
逐笔委托的数据结构大概长这样:
{
"order_id": "2024010109300012345", // 委托单编号
"time": 1704061800000, // 委托时间(毫秒时间戳)
"price": 10.00, // 委托价格
"volume": 1000, // 委托数量
"side": "sell", // 买卖方向
"type": "limit", // 委托类型(限价/市价)
"action": "new" // 操作类型:new/cancel/trade
}
为什么这个数据重要?因为你可以通过逐笔委托,还原出订单簿的「微观结构」。比如,某个价位突然出现大量挂单,然后又迅速撤单——这很可能是「虚假挂单」,用来诱导其他交易者跟风。做市商如果看不懂这个,很容易被「钓鱼」。
十档行情:看得更远,才能跑得更快
十档行情,就是订单簿上买卖各10档的价格和数量。普通行情只有5档,Level2给你10档。
多这5档有什么用?
- 判断支撑和压力:如果卖10档到卖6档全是密密麻麻的挂单,说明上方压力很大,价格很难突破。
- 识别大单动向:某个价位突然出现巨量挂单,可能是机构在「筑墙」。
- 计算市场深度:你可以算出,要推动价格变动1个tick,需要吃掉多少对手盘。
我记得有一次做ETH的做市策略,发现卖5档到卖8档之间有个「缺口」——中间几档几乎没有挂单。这意味着什么?一旦价格突破卖4档,会瞬间跳到卖9档,中间几乎没有阻力。我立刻调整了策略,在卖4档附近加大了挂单量,果然吃到了这波快速拉升的利润。
快照与增量:两种数据,两种用法
Level2行情推送有两种方式:快照和增量。
快照:每隔一段时间(比如500ms),交易所给你发一份完整的订单簿数据。包含所有10档的价格和数量。
增量:每次订单簿发生变化时,只推送变化的部分。比如「卖一价从10.00变成10.01,数量从1000变成800」。
为什么要有两种?
快照数据完整,但频率低。增量数据实时,但需要你自己维护订单簿状态。
我建议的做法是:
- 用快照数据做「基准」——每收到一次快照,就重置本地的订单簿状态。
- 用增量数据做「更新」——在两次快照之间,用增量数据实时更新订单簿。
- 定期校验——比如每10秒检查一次本地订单簿和快照是否一致,如果不一致,以快照为准重新同步。
一张图看懂Level2数据体系
下面这张图,是我自己整理的数据流转逻辑。你看一遍就能明白各个数据之间的关系:
实战:如何用Python解析Level2数据
假设你从交易所拿到了Level2的增量数据,格式是JSON。下面这段代码,是我常用的解析方式:
import json
from collections import defaultdict
class OrderBook:
def __init__(self):
# 用字典存储买卖盘,key是价格,value是数量
self.bids = defaultdict(int) # 买盘
self.asks = defaultdict(int) # 卖盘
self.last_snapshot_time = 0
def apply_snapshot(self, snapshot_data):
"""应用快照数据,重置订单簿"""
self.bids.clear()
self.asks.clear()
for bid in snapshot_data['bids']:
self.bids[bid['price']] = bid['volume']
for ask in snapshot_data['asks']:
self.asks[ask['price']] = ask['volume']
self.last_snapshot_time = snapshot_data['timestamp']
print(f"快照更新完成,时间戳: {self.last_snapshot_time}")
def apply_increment(self, inc_data):
"""应用增量数据,更新订单簿"""
for change in inc_data['changes']:
price = change['price']
volume = change['volume']
side = change['side'] # 'bid' 或 'ask'
if side == 'bid':
if volume == 0:
# 数量为0表示该价位已撤单
self.bids.pop(price, None)
else:
self.bids[price] = volume
elif side == 'ask':
if volume == 0:
self.asks.pop(price, None)
else:
self.asks[price] = volume
# 每次增量更新后,可以触发策略逻辑
self.check_strategy()
def check_strategy(self):
"""简单的策略检查:如果买卖价差小于阈值,提示机会"""
if not self.bids or not self.asks:
return
best_bid = max(self.bids.keys())
best_ask = min(self.asks.keys())
spread = best_ask - best_bid
if spread < 0.01: # 假设阈值是0.01
print(f"价差收窄!买一: {best_bid}, 卖一: {best_ask}")
def get_top_n(self, n=10):
"""获取前n档买卖盘"""
sorted_bids = sorted(self.bids.items(), reverse=True)[:n]
sorted_asks = sorted(self.asks.items())[:n]
return sorted_bids, sorted_asks
# 使用示例
ob = OrderBook()
# 模拟收到快照
snapshot = {
"timestamp": 1704061800000,
"bids": [{"price": 10.00, "volume": 1000}, {"price": 9.99, "volume": 2000}],
"asks": [{"price": 10.01, "volume": 1500}, {"price": 10.02, "volume": 800}]
}
ob.apply_snapshot(snapshot)
# 模拟收到增量更新
increment = {
"changes": [
{"price": 10.00, "volume": 800, "side": "bid"}, # 买一数量减少
{"price": 10.01, "volume": 0, "side": "ask"} # 卖一被吃掉
]
}
ob.apply_increment(increment)
数据质量:最容易忽略的坑
Level2数据虽然好,但质量参差不齐。我踩过的坑包括:
- 数据延迟:有些交易所的增量数据比实际成交慢了100毫秒以上。做高频策略时,这100毫秒足以让你亏钱。
- 数据缺失:网络波动可能导致某些增量更新没收到。如果不做校验,你的本地订单簿会越来越「歪」。
- 时间戳问题:有些交易所给的是「交易所时间」,有些给的是「本地接收时间」。做回测时一定要统一时间基准。
我曾经因为没处理好数据延迟,在实盘里连续吃了三天的亏。后来我加了一个「延迟监控」模块——每次收到增量数据,都计算一下当前时间和数据时间戳的差值。如果延迟超过200毫秒,就暂时停止策略,等数据恢复正常再启动。
嗯,数据这东西,说白了就是「垃圾进,垃圾出」。Level2行情再高级,如果解析和处理的环节出了问题,策略照样白搭。