交易所行情源
SSE LDDS/MDGW · SZSE MDGW · HKEX OMD-C · 各自独立的二进制协议与传输通道
同时支持 BINARY · STEP+FAST · OMD-C 等多种协议族
将多种外部接口统一为符合内部协议的数据流
具备重传机制的交易所需妥善利用机制,确保数据不丢
行情源之间建设多路冗余
按成分券清单精准订阅,支持运行时动态增删频道
交易所发原始行情 → 行情接入层标准化 → 指数计算 → 对外发布
SSE LDDS/MDGW · SZSE MDGW · HKEX OMD-C · 各自独立的二进制协议与传输通道
协议解析 · 数据标准化 · 质量过滤 · 多源适配 · 99.995% 高可用
匹配成分券 · 统一标准化行情输入 · 实时指数计算 · 99.995% 高可用
⚠ 行情数据不是"接了就行",数据质量直接决定指数准确性
订阅数据分组 · 流式/文件发布 · 跨机房数据择优 · 99.995% 高可用
会话生命周期管理
SSE Level-1: S001/S002
SSE Level-2: 发送 Logon(35=A),接收并判断 Logout(35=5)
SZSE: Logon/Logout
HKEX: 加入组播即可
链路状态监测
SSE: S003/UA1202
HKEX: 3×间隔断连
丢包检测与补传
SZSE: 390094
HKEX: RTS / Refresh
证券定义/公告/文件
SSE: mktdt00.txt
HKEX: News/Security Def
定期全量刷新
股票/指数/基金
3s周期 / 实时推送
实时逐笔推送
成交/委托
L2 专属 / HKex Live 频道
每端维护 (NxtIn, NxtOut),从 1 连续递增
仅用于会话完整性校验,不用于重传定位
对比:L2 按 tag10142 产品类别编号 · SZSE 按频道号编号
"MDGW目前发布的行情应用消息不支持重传"
由于Level1丢包只能等下一快照周期(3s),考虑业务层补偿
→ 可能涉及主备切换
HeartBtInt 登录时协商间隔
2× 间隔无消息 → 判定异常 → 重建会话
对比:L2 UA1202 每 10s 单向推送,VSS 无需应答
uint8 逐字节累加,结果放入 uint32
仅检测基本字节错误
SZSE: uint32 累加 %256 (需注意Big Endian的影响) · L2 STEP: 字节累加 %256 · HKEX : 依赖 UDP 层校验
超时未登录:TCP 连接后几秒内未发送登录请求
登录异常: 收到MDGW发送的LOGOUT消息
消费积压:VSS 处理太慢,待发送消息超阈值,导致套接字被断开等
⚠ 需确保消费速度匹配 MDGW 推送速度
⚠ 重连或触发主备切换
⚠ 重连时机:登录失败 / 网络异常 / 连续收到无效包
无全量/增量区分:每条快照都是独立的最新完整状态
直接覆盖更新:收到新数据即覆盖旧值,无需增量合并
覆盖范围:指数 / 股票 / 债券 / 基金等所有快照
⚠ 收盘产品不应再被新快照数据更新, 除非重新放开锁定
原始备份:MDGW 收到的有效消息在处理前先原样写盘
转发备份:全量快照与变化数据持久化至数据库
写入模式:缓冲模式(攒批写)vs 直接模式(逐条写)
⚠ 原始数据是纯二进制数据,不适合持久化至数据库⚠ 生产环境推荐缓冲模式,减少磁盘 I/O 提升性能
Level 2 不是直连交易所撮合主机,而是通过 VDE 中间件接入 — VSS(我方行情应用)只能通过 VDE 获取数据
VDE 是上交所提供的 Java 接入程序(JRE 1.8),我方 VSS 只能通过 VDE 获取数据,不能绕过
实时端口 = 长连接推送
重建端口 = 短连接请求
每端口仅允许唯一 TCP 连接
VSS 必须与 VDE 部署在同一局域网内
数据安全由信息商自行保证
每次重建请求完毕后 VDE 发 Logout
VSS 须断开,下次重建需重新连接
保留 FIX 会话层结构(Tag 8/9/10、SOH 分隔)
裁剪通用金融字段,扩展 10xxx 自定义 tag
支持 Tag 96 内嵌 FAST 二进制载荷
⚠ STEP 是基于 FIX 协议框架的本地化定制版:它保留了 FIX 的会话层信封结构(Tag 8/9/10、SOH 分隔、校验和机制),裁剪了 FIX 中不适用的通用金融字段,同时扩展了大量 10xxx 范围的自定义 tag,以适配上交所 Level-2 行情的业务需求(包括嵌套 FAST 二进制载荷)。⚠ 消息边界识别:一条完整的 STEP 消息以 8=STEP.1.0.0
会话字段:35=UA3202 · 49=VDE · 56=VSS
路由字段:10142=6(分类ID) · 10072=1(序号)
FAST 载荷:95=长度 · 96=二进制数据
⚠ SOH 分隔符(ASCII 0x01):每个 Tag=Value 对之间用 SOH 分隔,不是换行符也不是逗号,解析器必须正确处理这个不可见字符。
Tag 95 = FAST 载荷的字节长度(纯文本数字)
Tag 96 = FAST 压缩行情数据(二进制流)
两者都是 Body 内的普通 tag=value 字段
⚠ 不能简单地按 SOH 切分整条消息,需要先读 tag95 获取 FAST 数据长度,再精确截取 tag96 的二进制载荷
Tag 9 = Body 长度(从 Body 起始到 Tag 10 含)
Tag 10 = 字节累加 %256,覆盖整个 Body
STEP_HEAD_LEN=15 · STEP_TAIL_LEN=7
校验范围含 95/96,即 FAST 二进制也参与校验
RcvStepMsg() 收到帧
GetMsgType() 提取 Tag 35
35=A → 登录
35=5 → Logout break
35=UAxxxx → 业务路由
⚠ 模板文件 template.xml:FAST 解码器需要加载与当前行情匹配的模板文件,模板定义了每种消息(UA3202、UA5803 等)的字段结构和操作符(copy、default、increment、constant、none)⚠ 操作符语义:FAST 操作符决定了解码行为——copy 表示同值省略、default 表示缺省值、increment 表示自增、constant 表示固定值、none 表示每次都传。如果操作符理解错误,解码出来的数据会完全不对⚠ 接口文档升级可能导致模板变更(如新增字段、调整操作符),必须确保本地模板文件与交易所发布的版本严格一致
白名单匹配:
UA3202 竞价快照
UA3113 指数行情
UA5803 逐笔合并
UA5815 通道序号
UA3802 债券快照
UA3901 债券逐笔
UA3815 债券市场总览
DisposeDataMsg():
GetTagValue("95") → FAST 长度
GetFastValue("96") → 二进制指针
MsgQueSend() → 消息队列
→ 送入 FAST 解码线程
SynchronousDecoder:
加载 template.xml 模板
按模板 ID 匹配解码器
操作符:constant/copy/default
→ consumeMessage()
→ parseSnap/parseIndex
Level 2 快照比 Level 1 更精细 — 分为"变化全量"与"周期全量"两层,下游需容忍最长 30s 数据空窗
每 3 秒发布一次快照,但只发有变化的数据
若某只股票长时间无交易,其快照会顺延不发
⚠ 不能假设每 3s 一定能收到某产品的快照
⚠ 债券(UA3802)仅 3s 单层策略,无双层发布
每 30 秒无差别发布所有产品快照
无论数据有无变化,均会发布一次全量
保证所有产品至少 30s 刷新一次数据
⚠ 30s 周期全量为竞价专用,债券无此机制
盘后固定价格交易快照(UA3108)周期全量频率为 6s
ImageStatus=1 表示全量快照,直接覆盖即可
盘后频率比盘中快 5 倍(6s vs 30s)
⚠ 债券不参与盘后固定价格交易
• 不能假设每 3s 一定能收到某产品快照
• 需容忍最长 30s 数据空窗
• 全量快照直接覆盖,无需增量合并
停牌期间仍会发快照(状态=SUSP)
SecurityType=1(股票/基金/指数/债券分销)
Level 2 无此机制 — 需自行从逐产品状态推断全市场阶段
MD002 股票 · MD003 债券分销 · MD004 基金(含REITs)
⚠ M / N 为 Level 1 独有熔断状态,Level 2 无对应枚举
▸ 竞价 Level-2(UA3202)
▎股票(含收盘集合竞价 CCALL)
▎基金 / 债券分销产品(无 CCALL)
▸ 债券 Level-2(UA3802)
▎可转债(15:00 收盘,无 CCALL)
▎债券现券 / 回购(除可转债,15:30 收盘)
▎停牌
| 状态码 | 含义 | 股票 | 基金/债券分销 | 可转债 | 现券/回购 |
| ADD | 产品未上市 | — | — | ✓ 债券独有 | ✓ 债券独有 |
| START | 启动 | 8:45~9:15 | 8:45~9:15 | 8:45~9:15 | 8:45~9:15 |
| OCALL | 开盘集合竞价 | 9:15~9:25 | 9:15~9:25 | 9:15~9:25 | 9:15~9:25 |
| TRADE | 连续自动撮合 | 9:25~14:57 | 9:25~15:00 | 9:25~15:00 | 9:25~15:30 |
| CCALL | 收盘集合竞价 | 14:57~15:00 | — 无 | — 无 | — 无 |
| CLOSE | 闭市(计算收盘价) | 15:00 | 15:00 | 15:00 | 15:30 |
| ENDTR | 交易结束 | 随后发 | 随后发 | 随后发 | 随后发 |
OCALL/CCALL 期间:申买价一/申卖价一 = 虚拟参考价
申买量一/申卖量一 = 虚拟匹配量 · 量二 = 未匹配量
消息头:MsgType(uInt32) + BodyLength(uInt32)
消息体:业务字段,按 MsgType 分发解析
消息尾:CheckSum(uInt32)
参见接口文档§4.2
所有整数类型:高字节序(Big Endian)
字符串:UTF-8 编码,不足最大长度补空格
Price=Int64 N13(4) · Qty=Int64 N15(2)
CheckSum: 逐字节累加 %256,覆盖消息头+消息体
实时会话:端口 9129,接收全量行情
重传会话:端口 9130,仅网络版提供
Logon → 登录确认 → Heartbeat 保活
心跳间隔 Logon 时协商 · 2×间隔超时断连
1=市场/证券状态 · 2=公告
10/11=指数/国证指数 · 12=基金净值
101x=股票快照 · 201x=股票逐笔 ……
每频道独立心跳 · 记录号频道内从 1 递增
快照行情:定时发布截面,不可重传
含股票/基金/指数/债券快照(300111/309011等)
逐笔行情:逐条记录,支持重传
频道内 ApplSeqNum 从 1 递增,跳变即丢包
参见接口文档§3.2/§3.3
ResendType: 1=逐笔行情 · 2=公告信息
ChannelNo + ApplBegSeqNum/ApplEndSeqNum 定位
EndSeqNum=0 → 自动设为内存最大值
重传会话独立 TCP 连接(端口 9130)
多个重传请求按到达顺序依次处理
会话心跳:Logon 时 HeartBtInt 协商间隔
空闲时发送,2×间隔超时 → 判定断连
频道心跳(390095):每频道独立 3 秒
含 ApplLastSeqNum + EndOfChannel 标志
频道心跳无记录号,不参与序号连续性检测
每个频道 = 两条独立组播流:
① Realtime 实时增量流(A/B 双路 + RTS 重传)
② Refresh 定期全量快照流(A/B 双路,无 RTS)
刷新频道用专用组播地址,周期发送市场快照
Refresh Complete 标记快照周期边界,携带 LastSeqNum
Line A 不保证比 B 快,两者同等优先级
A/B 打包方式可能不同(同一消息在不同包)
Gap 检测模块不应关心包来源
⚠ "先 A 后 B" 的思路是错误的
丢弃重复:(seqNum+msgCount) < nextSeqNum → 跳过
重排序:缓存乱序包,按序号处理
Gap 检测:seqNum > nextSeqNum → 触发恢复
统一通过 nextSeqNum 变量实现
Ch1 Reference Data(参考数据)
Ch2 Trading Session Status(交易状态)
Ch10 Conflated 聚合行情(AggOB/价格/成交)
Ch3 Value Add · Ch4 News · Ch9 DR Signal
每个频道均有 Realtime(A/B) + Refresh(A/B) + RTS
⚠ 仅用于实时补传,不可用于日内全量恢复
⚠ 先刷新参考数据频道,再刷新行情频道
收到消息 100
→ 重置 nextSeqNum = 1
→ 清除该频道所有缓存
→ 订阅 Refresh 获取最新快照
→ 恢复实时处理
开盘前可能收到多次 Reset
OMD-C 盘中重启
→ 所有 RTS 断开
→ 所有组播停止(含心跳)
→ 收到通知后清除全部缓存
→ 按迟连接流程重连
重启间隔可达 1 小时,不可依赖 Reset 消息
DR Signal (105) 专用频道
DR Status 1 → 故障切换中
→ 清除所有缓存数据
DR Status 2 → 切换完成
→ 通过 RFS 重建市场镜像
目标 5 分钟内完成切换
心跳 = 包头 + MsgCount=0,不携带序号
SeqNum 设为上一条消息的序号
必须跳过,否则 Gap 检测会误报
仅在线路空闲时发送(有行情时不发)
心跳间隔 2s · 超时阈值可配置
所有二进制值均为 Little Endian
最低有效字节在低地址
C/C++ 可直接 cast 结构体指针
SSE/SZSE 使用 Big Endian,需注意切换
多数价格字段:固定小数位(如 3 位)
部分字段:可变小数位由同消息另一字段定义
例:CallPrice=1234567, Decimals=5 → $12.34567
必须按消息内 Decimals 字段动态还原
MsgSeqNum 序号跳变或频道记录号不连续时触发重传,不同交易所的重传粒度与通道各异