一、Level2数据基础知识
Level2行情数据(又称市场深度数据)是交易所提供的进阶行情服务,相比Level1(仅包含最新成交价、最高买价、最低卖价等表层数据),Level2揭示了订单簿的多档位挂单信息。
以A股为例,普通行情软件仅展示买一到买五、卖一到卖五共五档盘口,而Level2行情则将视野扩展至买一到买十、卖一到卖十。深交所提供的Level2数据主要包含十档买卖盘口行情快照、逐笔委托、逐笔成交等三类核心数据。其中,行情快照以约3秒一幅的频率对外输出,而逐笔委托和逐笔成交则持续实时输出。
港股Level2行情则提供买一或卖一位置的前10笔委托单明细,通过委托单的大小与规律可判断委托方属机构、大户或散户。不同市场对Level2深度的定义有所差异:港股通常提供10档盘口,美股提供10档,A股提供5档。
二、订单簿数据结构解析
订单簿(Order Book)本质上是某一交易品种当前全部有效买卖委托的集合,按价格档位组织。Level2订单簿的核心数据结构包含两个数组:
- Bids(买盘):按价格从高到低排列的买单委托列表,每个档位包含价格(Price)和委托量(Size)
- Asks(卖盘):按价格从低到高排列的卖单委托列表,每个档位同样包含价格和委托量
每个档位由三个核心要素构成——价格(price)、数量(size)和方向(side)。在聚合订单簿(Aggregated Order Book)中,同一价格的所有委托被合并为一个档位。
以某股票为例,其订单簿可能呈现如下结构:
{
"bids": [
["100.50", 2300], // 买一:价格100.50,挂单量2300股
["100.49", 1500], // 买二
["100.48", 3200] // 买三
],
"asks": [
["100.51", 1800], // 卖一:价格100.51,挂单量1800股
["100.52", 2100], // 卖二
["100.53", 950] // 卖三
]
}
订单簿数据通常通过两种方式获取:快照(Snapshot) 提供某一时刻的完整订单簿状态;增量更新(Delta/Incremental Update) 则实时推送订单簿的变化——新增、修改或删除某个价格档位。
三、实战接入:获取买卖盘深度数据
本部分以 AllTick API 为例,演示如何通过WebSocket协议实时获取Level2订单簿深度数据。AllTick提供7天全功能免费试用,免费期间支持全市场测试,支持WebSocket压测。
3.1 前置准备
- 注册AllTick账号并获取API Token
- 确认目标市场对应的WebSocket端点
AllTick按资产类别提供不同的WebSocket接入地址:
| 资产类别 | WebSocket地址 |
|---|---|
| 美股、港股、A股 | wss://quote.alltick.co/quote-stock-b-ws-api?token={您的token} |
| 外汇、贵金属、加密货币、原油 | wss://quote.alltick.co/quote-b-ws-api?token={您的token} |
3.2 WebSocket连接与订阅
步骤一:建立WebSocket连接
import websocket
import json
import threading
import time
TOKEN = "your_api_token_here" # 替换为您的实际Token
WS_URL = f"wss://quote.alltick.co/quote-stock-b-ws-api?token={TOKEN}"
def on_open(ws):
print("WebSocket连接已建立")
# 订阅订单簿深度数据
subscribe_msg = {
"cmd_id": 22002, # 最新盘口订阅协议号
"seq_id": 1, # 订阅请求标识(自定义)
"trace": "sub_001", # 可追溯ID(每次请求不可重复)
"data": {
"symbol_list": [
{"code": "US-AAPL", "depth_level": 10}, # 美股AAPL,10档深度
{"code": "HK-0700", "depth_level": 10}, # 港股腾讯,10档深度
{"code": "SH-600036", "depth_level": 5} # A股招商银行,5档深度
]
}
}
ws.send(json.dumps(subscribe_msg))
print("订阅请求已发送")
def on_message(ws, message):
data = json.loads(message)
print(f"收到数据: {json.dumps(data, indent=2)}")
def on_error(ws, error):
print(f"发生错误: {error}")
def on_close(ws, close_status_code, close_msg):
print("WebSocket连接已关闭")
# 创建WebSocket连接
ws = websocket.WebSocketApp(
WS_URL,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
# 启动连接(建议在独立线程中运行)
# ws.run_forever()
关键参数说明:
cmd_id: 22002:最新盘口订阅请求的固定协议号depth_level:指定需要获取的盘口深度档位数。若省略该字段,默认仅提供1档- 不同市场的最大深度限制:港股最大10档、美股最大10档、A股最大5档
步骤二:处理订阅确认
订阅成功后,服务器会返回cmd_id: 22003的确认消息:
{
"ret": 200,
"msg": "ok",
"cmd_id": 22003,
"seq_id": 1,
"trace": "sub_001",
"data": {}
}
ret: 200表示订阅成功。
3.3 解析订单簿数据
订阅成功后,服务器通过cmd_id: 22999持续推送订单簿数据。数据格式示例如下:
{
"cmd_id": 22999,
"data": {
"code": "US-AAPL",
"bids": [
["189.50", 1200],
["189.48", 850],
["189.45", 2300]
],
"asks": [
["189.52", 950],
["189.55", 1400],
["189.58", 1800]
],
"timestamp": 1723456789123
}
}
数据结构解析:
| 字段 | 类型 | 说明 |
|---|---|---|
cmd_id |
integer | 固定为22999,表示订单簿数据推送 |
data.code |
string | 产品代码 |
data.bids |
array | 买盘数组,每个元素为[价格, 数量] |
data.asks |
array | 卖盘数组,每个元素为[价格, 数量] |
data.timestamp |
integer | 毫秒级时间戳 |
3.4 完整可运行示例
import websocket
import json
import time
TOKEN = "your_api_token_here"
WS_URL = f"wss://quote.alltick.co/quote-stock-b-ws-api?token={TOKEN}"
class OrderBookSubscriber:
def __init__(self):
self.ws = None
self.order_books = {} # 存储各产品的订单簿
def on_open(self, ws):
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 连接已建立")
# 订阅多个产品的订单簿
subscribe_msg = {
"cmd_id": 22002,
"seq_id": int(time.time() * 1000),
"trace": f"trace_{int(time.time())}",
"data": {
"symbol_list": [
{"code": "US-AAPL", "depth_level": 10},
{"code": "US-TSLA", "depth_level": 10},
{"code": "HK-0700", "depth_level": 10}
]
}
}
ws.send(json.dumps(subscribe_msg))
print(f"订阅请求已发送: {subscribe_msg['data']['symbol_list']}")
def on_message(self, ws, message):
try:
data = json.loads(message)
cmd_id = data.get("cmd_id")
if cmd_id == 22003:
# 订阅确认
print(f"订阅确认: {data.get('msg')}")
elif cmd_id == 22999:
# 订单簿数据
book_data = data.get("data", {})
code = book_data.get("code")
bids = book_data.get("bids", [])
asks = book_data.get("asks", [])
ts = book_data.get("timestamp")
# 更新本地订单簿
self.order_books[code] = {
"bids": bids,
"asks": asks,
"timestamp": ts
}
# 计算一些关键指标
if bids and asks:
best_bid = float(bids[0][0])
best_ask = float(asks[0][0])
spread = best_ask - best_bid
bid_volume = sum(float(b[1]) for b in bids[:5])
ask_volume = sum(float(a[1]) for a in asks[:5])
print(f"[{code}] 买一: {best_bid} | 卖一: {best_ask} | 价差: {spread:.4f}")
print(f" 前5档买量: {bid_volume:.0f} | 前5档卖量: {ask_volume:.0f}")
else:
print(f"未知消息类型: {cmd_id}")
except json.JSONDecodeError as e:
print(f"JSON解析错误: {e}")
except Exception as e:
print(f"处理消息异常: {e}")
def on_error(self, ws, error):
print(f"WebSocket错误: {error}")
def on_close(self, ws, close_status_code, close_msg):
print(f"连接已关闭 [{close_status_code}]: {close_msg}")
# 自动重连逻辑
print("5秒后尝试重连...")
time.sleep(5)
self.connect()
def connect(self):
self.ws = websocket.WebSocketApp(
WS_URL,
on_open=self.on_open,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close
)
self.ws.run_forever()
if __name__ == "__main__":
subscriber = OrderBookSubscriber()
subscriber.connect()
3.5 关键注意事项
-
心跳维持:订阅成功后,需每10秒发送一次心跳包。若30秒内未收到心跳,服务端将断开连接。心跳通常通过WebSocket协议层的Ping/Pong机制实现。
-
订阅覆盖机制:对于同一个WebSocket连接,每次发送订阅请求会覆盖之前的订阅。如需追加订阅新产品,需在
symbol_list中包含所有需要订阅的产品。 -
自动重连:生产环境中务必实现断线自动重连逻辑,并在重连后重新订阅。
-
深度限制:不同产品的最大深度不同,港股和美股最大10档,A股最大5档。不活跃产品可能存在深度不足的情况。
四、盘口数据实时更新处理
Level2数据的实时性要求较高,WebSocket推送的消息可能以每秒数十至数百次的频率到达。有效处理这些数据需注意以下几点:
4.1 快照 + 增量更新模式
大多数WebSocket订单簿接口采用"快照+增量"模式:
- 首次订阅:服务器推送完整的订单簿快照
- 后续推送:仅推送发生变化的价格档位(增量更新)
增量更新中,当某个价格档位的数量变为0时,表示该档位应从本地订单簿中移除。本地需维护完整的订单簿状态,根据增量消息持续更新。
4.2 本地订单簿维护策略
class LocalOrderBook:
def __init__(self):
self.bids = {} # price -> size
self.asks = {} # price -> size
self.last_update_id = 0
def apply_snapshot(self, bids, asks, update_id):
"""应用全量快照"""
self.bids = {float(p): float(s) for p, s in bids}
self.asks = {float(p): float(s) for p, s in asks}
self.last_update_id = update_id
def apply_delta(self, bids_delta, asks_delta, update_id):
"""应用增量更新"""
if update_id <= self.last_update_id:
return # 忽略过期的更新
for price, size in bids_delta:
p = float(price)
s = float(size)
if s == 0:
self.bids.pop(p, None) # 数量为0则删除该档位
else:
self.bids[p] = s
for price, size in asks_delta:
p = float(price)
s = float(size)
if s == 0:
self.asks.pop(p, None)
else:
self.asks[p] = s
self.last_update_id = update_id
def get_best_bid(self):
return max(self.bids.keys()) if self.bids else None
def get_best_ask(self):
return min(self.asks.keys()) if self.asks else None
def get_sorted_bids(self, n=10):
"""获取前n档买盘(从高到低)"""
return sorted(self.bids.items(), key=lambda x: -x[0])[:n]
def get_sorted_asks(self, n=10):
"""获取前n档卖盘(从低到高)"""
return sorted(self.asks.items(), key=lambda x: x[0])[:n]
4.3 序列号校验
为防止数据丢失或乱序导致订单簿错乱,AllTick等专业API支持序列号追踪机制。每条消息携带递增的序列号(update_id),客户端应校验序列号的连续性。若发现序列号跳跃,应主动重新获取全量快照。
五、总结
Level2订单簿深度数据是量化交易与市场分析的核心数据源,通过买卖盘多档位的价格与挂单量信息,揭示了市场的真实流动性分布。
AllTick API 提供了标准化的WebSocket接入方案,支持美股、港股、A股等多市场的实时订单簿订阅:
- 接入简单:仅需Token认证,通过
cmd_id: 22002即可订阅多产品深度数据 - 深度灵活:港股/美股最大10档,A股最大5档,可按需指定
depth_level - 实时推送:通过
cmd_id: 22999持续推送订单簿更新,毫秒级响应 - 免费试用:提供7天全功能免费试用,支持全市场测试与WebSocket压测
在实际开发中,建议重点关注心跳维持、自动重连、本地订单簿维护与序列号校验等关键环节,以确保数据链路的稳定与订单簿状态的一致性。
共同学习,写下你的评论
评论加载中...
作者其他优质文章