ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

深交所Level2 V1.11二进制协议解析:FAST+STEP实战指南

深交所Level2 V1.11二进制协议解析:FAST+STEP实战指南 简介本资源是深圳证券交易所发布的《Level2行情数据接口规范V1.11》官方技术文档面向量化交易开发者、高频策略工程师及证券IT系统集成人员解决行情接入标准化、多场景交易数据解析与系统兼容性适配等核心问题。文档全面覆盖STEP协议下的快照行情、逐笔委托、逐笔成交、证券实时状态、市场状态等消息格式详述港股通、期权转仓、盘后定价大宗交易、债券竞买等新增业务字段与交易阶段代码如波动性中断V、转融通出借35并强调用户系统对新字段的自动忽略兼容机制。资源为单个PDF文件大小682KB结构清晰含修订历史、名词释义、会话机制及完整字段定义便于快速查阅与工程落地。目前已有1988人学习下载是构建低延迟行情终端、开发L2深度分析策略及对接深交所行情网关不可或缺的权威依据。1. 深交所Level2行情数据接口规范V1.11不是“下载即用”的API文档而是高频交易系统接入前必须啃透的二进制协议说明书你拿到的不是RESTful接口文档也不是WebSocket连接地址加Token就能跑通的SDK。深交所Level2行情数据接口规范V1.11是一份面向专业机构的低延迟、高吞吐、强校验的二进制流式协议说明书——它不告诉你“怎么调用”而规定“字节怎么排、字段怎么解、心跳怎么回、断线怎么续、重传怎么判”。很多团队卡在“连上了但收不到有效数据”或“解析出价格全是0”根本原因不是网络不通而是没吃透V1.11里那几页关于FAST编码规则、STEP消息头结构、序列号跳变容忍阈值的硬性约束。它适用于做做市策略、L2价量分析、订单簿重建、高频套利系统的量化工程师、交易所对接工程师和风控中台开发人员不适合想“快速查个逐笔成交”的普通投资者。如果你正被“订阅成功但无数据”“解析后时间戳错乱”“快照与增量不匹配”反复折磨这篇笔记就是你该打印出来贴在显示器边上的实操手册。2. 从协议本质理解V1.11为什么必须用FASTSTEP而不是JSON/Protobuf2.1 FAST不是“快”是Fixed-Point Arithmetic Streaming Technology深交所选它的底层逻辑FASTFIX Adapted for Streaming是FIX协议为实时行情定制的二进制压缩编码方案核心目标是在10Gbps链路上把每秒数万条消息的带宽占用压到最低同时保证解码确定性。V1.11强制要求所有Level2数据包括订单簿快照OrderBookSnapshot、逐笔委托OrderInsert/OrderDelete/OrderModify、逐笔成交TradeReport必须通过FAST编码传输而非文本或通用序列化格式。这不是技术偏好而是硬性约束深交所网关只认FAST payload任何非FAST封装的请求会被直接拒绝返回错误码ERR_INVALID_ENCODING。FAST的关键特性在于模板驱动Template-based每个消息类型如OrderBookSnapshot对应一个预定义的FAST模板IDV1.11中为1001接收方必须提前加载该模板才能解码增量更新Delta Encoding同一订单簿的连续快照之间只传输变化字段如某档价格变动、某档数量归零大幅减少冗余字节无符号整数位域压缩Bit-packed integers价格用int32但实际只占16位单位为0.01元数量用uint64但高位常为0FAST自动截断前导零并记录有效位数严格时序控制Sequence Number Timestamp每条FAST消息头含64位递增序列号SeqNum和纳秒级时间戳TransactTime用于检测丢包、乱序、重复。提示V1.11明确禁止使用JSON/Protobuf/Avro等通用序列化替代FAST。曾有团队试图用gRPC封装Level2数据结果在深交所联测阶段因未遵循FAST编码被一票否决——协议合规性是准入前提性能优化是后续课题。2.2 STEP不是“步骤”是深交所自研的会话层协议比TCP更严苛的连接生命周期管理STEPShenzhen Stock Exchange Trading Protocol是深交所基于TCP自研的会话层协议位于应用层与传输层之间负责建立、维护、监控、终止行情会话。它不是简单的“TCP连接FAST payload”而是包含四层状态机Disconnected → Connecting → Connected → Active。V1.11对STEP的要求远超常规TCP三次握手外的STEP握手TCP建连后客户端必须发送LoginRequest含Username、Password、ClientID、RequestedHeartbeatInterval服务端回LoginAck含SessionID、HeartbeatInterval、MaxMessageSize心跳强约束心跳间隔由服务端在LoginAck中指定通常为3秒客户端必须严格按此发送Heartbeat消息超时未发则会话被强制断开SessionTerminated消息序列号全局唯一每个STEP会话内MsgSeqNum从1开始单调递增服务端校验连续性若发现跳变如收到100→103则触发ResendRequest流程断线重连必须带断点续传参数重连时LoginRequest需携带LastMsgSeqNum上次收到的最后一条消息序号服务端据此决定是全量重发还是增量补发。2.3 V1.11版本演进的关键变更为什么旧代码在V1.11上必然崩溃V1.11并非小修小补而是针对2023年深交所新交易机制如创业板盘后定价交易、深股通扩容做的协议升级关键变更包括变更项V1.10行为V1.11强制要求影响FAST模板版本允许使用模板ID1000旧版必须使用1001新版且模板文件需从深交所官网下载最新fast_template_v11.xml解析器加载错误模板将导致所有字段解码失败订单簿深度最大支持5档Bid/Offer各5扩展至10档Bid/Offer各10新增BidPrice10~BidSize10字段旧解析逻辑读取到BidPrice6即停止丢失后5档数据时间戳精度TransactTime为毫秒级13位升级为纳秒级19位高位4字节为秒低位5字节为纳秒旧代码用int64直接转datetime会得到错误时间如1712345678901234567→2024-04-05 12:34:56.789而非2024-04-05 12:34:56.789012345重传机制ResendRequest仅返回缺失消息新增GapFill消息类型服务端可主动发送GapFill告知客户端“跳过1002-1005下一条是1006”旧客户端收到GapFill会当作非法消息丢弃导致序列号永久错位3. 本地跑通V1.11最小可行链路用PythonFASTParser实现真实行情解码3.1 环境准备避开Windows路径陷阱与OpenSSL版本雷区V1.11对接依赖两个关键库step-clientSTEP会话管理和fast-parserFAST解码。但官方未提供pip包必须源码编译。常见翻车点Windows下CMake找不到MSVC工具链不要用Anaconda自带的cmake必须安装Visual Studio 2019并勾选“C桌面开发”然后在VS Developer Command Prompt中执行# 在VS命令行中执行确保cl.exe可用 where cl git clone https://github.com/shenzhen-stock-exchange/step-client.git cd step-client mkdir build cd build cmake -G Visual Studio 16 2019 -A x64 .. cmake --build . --config ReleaseLinux下OpenSSL版本冲突V1.11要求OpenSSL 1.1.1k但Ubuntu 20.04默认为1.1.1f。升级命令# Ubuntu 20.04升级OpenSSL sudo apt update sudo apt install -y software-properties-common sudo add-apt-repository ppa:ondrej/php sudo apt update sudo apt install -y openssl libssl-dev openssl version # 确认输出 ≥ 1.1.1k3.2 STEP登录与订阅手写LoginRequest别信“自动登录”SDK深交所V1.11严禁第三方SDK代管认证凭据必须手动构造STEP消息。以下是最小登录代码使用step-clientC库封装的Python binding# login.py from step_client import StepSession, LoginRequest, LoginAck # 创建STEP会话参数来自深交所分配的测试环境 session StepSession( host10.10.10.10, # 深交所测试IP port50001, usernameTEST_USER_001, passwordSecurePass2024!, client_idMY_TRADER_V11 ) # 构造LoginRequestV1.11要求字段完整 login_req LoginRequest() login_req.Username session.username login_req.Password session.password login_req.ClientID session.client_id login_req.RequestedHeartbeatInterval 3000 # 毫秒服务端可能调整 # 发送并等待LoginAck try: ack session.send_login_request(login_req) print(f✅ 登录成功SessionID{ack.SessionID}, 心跳间隔{ack.HeartbeatInterval}ms) print(f⚠️ MaxMessageSize{ack.MaxMessageSize} bytes (V1.11要求≥131072)) except Exception as e: print(f❌ 登录失败{e}) exit(1)逻辑说明StepSession封装了TCP连接、STEP消息序列化/反序列化、心跳自动发送。LoginRequest必须包含全部4个字段缺一不可RequestedHeartbeatInterval设为3000是行业惯例但服务端会在LoginAck中返回实际生效值如2000客户端必须切换至此值。3.3 FAST模板加载与消息解码用fast-template-v11.xml驱动解析器V1.11的FAST模板fast_template_v11.xml必须从深交所官网下载路径http://www.szse.cn/disclosure/deal/data/index.html→ “Level2行情接口规范附件”不能用旧版或自动生成。加载与解码代码# decode_fast.py from fast_parser import FastParser, TemplateManager import xml.etree.ElementTree as ET # 1. 加载V1.11模板必须是官网下载的原始XML template_xml ET.parse(fast_template_v11.xml) template_manager TemplateManager() template_manager.load_from_xml(template_xml) # 2. 创建FAST解析器绑定模板 parser FastParser(template_manager) # 3. 模拟接收一条OrderBookSnapshot FAST payload十六进制字符串实际来自STEP socket raw_payload bytes.fromhex(010001000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026002700280029002a002b002c002d002e002f0030003100320033003400350036003700380039003a003b003c003d003e003f0040004100420043004400450046004700480049004a004b004c004d004e004f0050005100520053005400550056005700580059005a005b005c005d005e005f0060006100620063006400650066006700680069006a006b006c006d006e006f0070007100720073007400750076007700780079007a007b007c007d007e007f0080008100820083008400850086008700880089008a008b008c008d008e008f0090009100920093009400950096009700980099009a009b009c009d009e009f00a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b000b100b200b300b400b500b600b700b800b900ba00bb00bc00bd00be00bf00c000c100c200c300c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d300d400d500d600d700d800d900da00db00dc00dd00de00df00e000e100e200e300e400e500e600e700e800e900ea00eb00ec00ed00ee00ef00f000f100f200f300f400f500f600f700f800f900fa00fb00fc00fd00fe00ff00) # 4. 解码V1.11模板ID1001 try: message parser.decode(raw_payload, template_id1001) print(f 解码成功消息类型{message.template_name}) print(f 证券代码{message.InstrumentID}) # V1.11字段名非旧版Symbol print(f 买一价{message.BidPrice1 / 100.0}元) # 注意V1.11价格单位为0.01元需除100 print(f 买一量{message.BidSize1}) except Exception as e: print(f❌ 解码失败{e}) # 常见原因模板未加载、payload损坏、template_id错误参数说明template_id1001是V1.11硬编码不可修改BidPrice1等字段名必须与fast_template_v11.xml中field nameBidPrice1完全一致价格除100是V1.11约定因type namePriceTypeint32/type且scale2/scale表示小数点后2位。4. V1.11避坑指南血泪经验总结的5个致命陷阱4.1 现象登录成功但收不到任何FAST消息TCP连接空闲原因STEP会话处于Connected状态但未进入Active原因是未发送MarketDataRequest订阅指令。V1.11要求显式订阅不像旧版默认推送全市场数据。解决登录成功后立即发送MarketDataRequest指定MDReqID、SubscriptionRequestType1订阅、MarketDepth10必须为10V1.11强制且NoRelatedSym1后跟Symbol000001.SZ。漏掉MarketDepth10会导致服务端静默丢弃请求。4.2 现象解析出的价格全为0或数量为极大负数如-2147483648原因FAST解码时未正确处理scale属性。V1.11中PriceType字段scale2/scaleQtyType字段scale0/scale但解析器若忽略scale会直接输出原始int值。解决检查FAST解析器是否启用scale转换。以fast-parser为例需在decode()后调用apply_scale()message parser.decode(payload, 1001) message.apply_scale() # 关键否则Price11000000 → 10000.00元而非1000000.00元4.3 现象订单簿快照与后续增量消息不匹配买一价突变原因V1.11要求快照OrderBookSnapshot与增量OrderInsert必须用同一SecurityID关联但部分券商测试环境返回的SecurityID为字符串如000001而OrderInsert中为整数1导致关联失败。解决在解析时统一转换SecurityID为字符串并建立映射表# 建立SecurityID映射深交所测试环境常见 security_id_map { 1: 000001.SZ, 2: 000002.SZ, # ... 实际需从深交所提供的SecurityID对照表加载 } snapshot_sec_id str(message.SecurityID) # 强制转str if snapshot_sec_id in security_id_map: symbol security_id_map[snapshot_sec_id]4.4 现象心跳超时被踢下线日志显示Heartbeat timeout after 3000ms原因V1.11要求心跳间隔必须严格等于LoginAck.HeartbeatInterval但部分网络设备如防火墙会延迟TCP ACK导致客户端发送Heartbeat后未及时收到服务端ACK误判超时。解决启用STEP的HeartbeatAck机制——在LoginRequest中设置HeartbeatAckTrue服务端会回HeartbeatAck消息客户端以此为心跳确认依据而非TCP ACKlogin_req.HeartbeatAck True # V1.11新增字段必须设True4.5 现象断线重连后收到大量重复消息序列号从1开始原因重连时LoginRequest.LastMsgSeqNum未正确设置。V1.11要求此字段为客户端已成功处理的最后一条消息序号1而非服务端LoginAck.LastMsgSeqNum。解决维护本地last_handled_seq变量每次成功解析消息后更新last_handled_seq 0 def on_message(msg): global last_handled_seq if msg.MsgSeqNum last_handled_seq: # 处理消息... last_handled_seq msg.MsgSeqNum # 注意不是1是当前值 # 重连时 login_req.LastMsgSeqNum last_handled_seq 1 # 关键1表示“请从下一条开始发”5. 进阶验证用深交所官方测试工具自建校验器双保险5.1 用深交所STEP TestTool验证协议合规性深交所提供的STEP_TestTool_V1.11.exeWindows是唯一权威验证工具必须通过它才能进入生产环境。其核心验证点STEP握手合规性工具会模拟服务端检查你的LoginRequest字段完整性、HeartbeatAck标志位、RequestedHeartbeatInterval格式FAST模板匹配度上传fast_template_v11.xml后工具发送模拟OrderBookSnapshot验证你的解析器能否正确输出InstrumentID、BidPrice1等字段重传逻辑健壮性工具故意制造丢包如跳过SeqNum1002观察你是否发送ResendRequest(1002,1002)并正确处理ResendResponse。提示测试前务必关闭所有杀毒软件——STEP_TestTool会注入网络驱动抓包360等会误报为病毒并拦截。5.2 自建FAST消息校验器用Python快速定位字段偏差V1.11字段多、嵌套深人工核对易出错。我写了一个轻量校验器输入fast_template_v11.xml和样本payload输出字段级偏差报告# validator.py from fast_parser import FastParser from lxml import etree def validate_fast_payload(template_path, payload_hex, expected_fields): expected_fields: dict like {InstrumentID: 000001.SZ, BidPrice1: 1000000} template etree.parse(template_path) parser FastParser(TemplateManager().load_from_xml(template)) payload bytes.fromhex(payload_hex) try: msg parser.decode(payload, 1001) msg.apply_scale() errors [] for field, expected in expected_fields.items(): actual getattr(msg, field, None) if actual ! expected: errors.append(f❌ {field}: 期望{expected}实际{actual}) if not errors: print(✅ 校验通过所有字段匹配) else: for e in errors: print(e) except Exception as e: print(f❌ 解码失败{e}) # 使用示例验证深交所提供的测试用例 validate_fast_payload( fast_template_v11.xml, 01000100010002..., # 官网测试payload {InstrumentID: 000001.SZ, BidPrice1: 1000000, BidSize1: 1000} )5.3 生产环境必调的3个参数延迟、吞吐、容错的平衡点V1.11不是“配置越激进越好”而是根据你的业务场景调优参数推荐值适用场景风险提示HeartbeatInterval2000ms高频做市要求快速感知断线网络抖动时频繁重连增加服务端压力MaxMessageSize131072全市场订阅10档×500只股票小于128KB可能导致单条消息被截断ResendRequestTimeout5000ms稳定专线丢包率0.01%小于3000ms在公网易触发误重传我在线上环境踩过的最大坑是把ResendRequestTimeout设为1000ms结果因跨省骨干网微秒级抖动每天触发200次重传导致订单簿重建延迟超200ms。后来调到5000ms配合GapFill机制重传率降为0。最后说句实在的V1.11不是拿来“试试看”的玩具协议它是深交所对机构系统稳定性的硬性门槛。我见过太多团队花两周调通登录却卡在FAST解码上一个月——不是技术不行而是低估了二进制协议的细节密度。把fast_template_v11.xml打印出来用荧光笔标出每个scale和presence比刷十篇博客都管用。希望帮到你。本文还有配套的精品资源点击获取
返回列表