为了账号安全,请及时绑定邮箱和手机立即绑定

Level2数据接口如何获取订单簿深度信息

标签:
人工智能

一、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 前置准备

  1. 注册AllTick账号并获取API Token
  2. 确认目标市场对应的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 关键注意事项

  1. 心跳维持:订阅成功后,需每10秒发送一次心跳包。若30秒内未收到心跳,服务端将断开连接。心跳通常通过WebSocket协议层的Ping/Pong机制实现。

  2. 订阅覆盖机制:对于同一个WebSocket连接,每次发送订阅请求会覆盖之前的订阅。如需追加订阅新产品,需在symbol_list中包含所有需要订阅的产品。

  3. 自动重连:生产环境中务必实现断线自动重连逻辑,并在重连后重新订阅。

  4. 深度限制:不同产品的最大深度不同,港股和美股最大10档,A股最大5档。不活跃产品可能存在深度不足的情况。

四、盘口数据实时更新处理

Level2数据的实时性要求较高,WebSocket推送的消息可能以每秒数十至数百次的频率到达。有效处理这些数据需注意以下几点:

4.1 快照 + 增量更新模式

大多数WebSocket订单簿接口采用"快照+增量"模式:

  1. 首次订阅:服务器推送完整的订单簿快照
  2. 后续推送:仅推送发生变化的价格档位(增量更新)

增量更新中,当某个价格档位的数量变为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压测

在实际开发中,建议重点关注心跳维持、自动重连、本地订单簿维护与序列号校验等关键环节,以确保数据链路的稳定与订单簿状态的一致性。

参考文档:https://apis.alltick.co/

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消