做量化交易的人,只要把深交所和上交所的Level-2行情同时接入过一遍,都会有一个很直观的感受:同样是二级行情,这两个所的数据规则差异不是“多几档少几档”这么简单,而是整个数据组织的逻辑都不一样。同一个盘口因子,在深交所的实时快照上算出来是0.3,切到上交所的行情上重新算,可能就变成-0.1,甚至方向都反了。这不是策略逻辑问题,而是两所Level-2推送规则在设计上就有本质区别。
这篇文章我会把自己在实盘接入和策略适配过程中踩过的坑、验证过的细节,以及两所Level-2数据规则的核心差异逐个拆开讲。内容适合量化开发、行情系统工程师,以及想用Level-2数据做盘口分析和订单流的交易研究人员。看完你至少能回答三个问题:两所Level-2到底哪里不一样?为什么同一个策略必须分所适配?搭建一套兼容两所的行情系统时,哪些雷区必须提前避开。
1. 两所Level-2数据体系全貌对比
1.1 数据通道与文件组成:不是“多几路”的差别
深交所的Level-2行情,从数据通道上看大体分为三块:行情快照、逐笔委托、逐笔成交。其中逐笔委托和逐笔成交是两条独立的流,但业务上又有关联——每一笔成交基本都能在委托流里找到对应的委托号。这意味着用深交所数据做订单流分析时,你可以把“谁在什么时候挂了什么方向的单子”和“这笔单子最终有没有成交”完整串起来。
上交所的Level-2则完全不同。上交所提供的是行情快照加逐笔成交,逐笔委托数据是不对外提供的。也就是说,你只能看到每一笔成交的价格、数量、方向,但看不到促成这笔成交的挂单和撤单过程。这个差别在量化策略里的影响非常大,尤其是做撤单识别、订单簿重建和微观结构分析的人,必须清楚自己在哪个所“缺少哪只眼睛”。
我做了一个简单对比表,方便你快速建立框架:
| 对比项 | 深交所Level-2 | 上交所Level-2 |
|---|---|---|
| 快照频率 | 基础3秒一帧,另有增量快照优化(实测约20-30ms级别) | 固定3秒一帧 |
| 逐笔委托 | 有,独立数据流 | 无 |
| 逐笔成交 | 有,独立数据流,且委托流中可关联委托号 | 有,独立数据流 |
| 行情档位 | 十档,但不一定是标准价差 | 十档,标准0.01元价格梯度 |
| 委托队列 | 买一卖一价位前50笔委托明细 | 买一卖一价位前50笔委托明细 |
| 推送协议 | 二进制流,通过深证通SDK等接收 | 快照为二进制流,逐笔成交使用STEP协议 |
| 盘口数据重建能力 | 强,可还原挂撤单过程 | 弱,只能从快照委托队列变化推断撤单 |
这个表格里的每一行,落到系统实现上都对应着不同的解码逻辑和存储结构。尤其是“逐笔委托”这一行,直接决定了一个团队能不能做真正的订单流重构策略,还是只能退而求其次做成交驱动的统计策略。
1.2 快照行情:深交所“虚拟档位”与上交所“标准十档”
很多第一次接触深交所Level-2快照的人都会懵:为什么买一和买二之间的价差是0.03元,买二和买三之间突然变成0.07元?这不是解析错误,而是深交所的快照档位本身就是“聚合”出来的。
深交所Level-2快照中的十档价格,并不是按0.01元的固定梯度排列的,而是按照委托量的分布做了一定程度的合并,产生所谓的“虚拟档位”。某些行情活跃的瞬间,你看到的可能只有六七个不同价格的档位,某些档位委托笔数为0或者跳空很大。这个设计和上交所有本质区别,上交所的快照就是标准的从买一到买十、卖一到卖十,每个档位间隔0.01元,价格梯度清清楚楚。
这个差异从数据源头上看,是两所为了平衡数据量、带宽和展示效果做的取舍。但反映到策略端,问题就来了:所有依赖“档位价差”或者“盘口深度斜率”的因子,在深交所都需要先做一步原始档位的还原或修正,否则算出来的盘口形态是失真的。这一点放在后面策略适配部分再展开,这里先记下结论。
1.3 逐笔数据:一个能看“过程”,一个只能看“结果”
从逐笔数据上讲,两所完全不是一个量级。深交所Level-2有逐笔委托,意味着你可以看到每一笔委托的录入时间、委托价、委托量、买卖方向,也能看到这笔委托后续是部分成交、全部成交还是被撤掉。你在数据里看到的是“一笔订单从生到死的完整生命周期”。
上交所只有逐笔成交,你能看到的是“成交了这一笔,价格是多少、量是多少、主买还是主卖”。至于这笔成交背后是谁挂的单、谁的主动单去吃的、挂单是不是早已存在,这些信息都拿不到。做高频微观结构研究的人应该深有体会,这相当于一局棋只给你看最终落子的结果,却不给你看每一步思考过程。
有一个现实的推论:如果策略需要识别“大单撤单后重新挂单”这类行为,在深交所可以用逐笔委托流直接判断,在上交所则只能观察3秒快照之间某个价位委托量的减少来间接推测,精度和时效性差得很远。
2. 核心字段与协议细节拆解
2.1 深交所快照的档位聚合逻辑与处理方式
深交所Level-2快照里比较关键的字段包括:当前快照时间、行情序号、十档价位数据、各档位委托笔数和委托量,以及买一卖一的50笔委托队列。如果你去对比盘中某只票的快照和你自己从逐笔委托流里算出来的真实盘口,会发现快照的十档价格和真实挂单价格并不完全一致——这正是虚拟档位的合并效果。
怎么理解这个“合并”呢?我用一个近似例子说明。假设当前买一真实挂单在10.00元,有200手;买二真实挂单在10.01元,有100手;买三真实挂单在10.02元,但只有5手。上交所的快照会原样展示10.00、10.01、10.02三档。深交所的快照则可能把10.00元和10.01元合并成同一档显示,因为这两个价位的委托量相对集中且相邻,而10.02元那档由于委托量太少也可能被合并或单独保留。于是你看到的价格序列里,档位与档位之间的价差不再是均匀的0.01元。
处理这个问题的常规做法是:在策略计算前,不要直接使用快照档位做“逐档价差”,而是优先使用买一卖一真实价位和委托队列,对深度数据做“按价位还原”。如果你有逐笔委托流,甚至可以自己重新聚合一个标准盘口,把虚拟档位的影响彻底消除。如果没有逐笔委托流,就只能对因子做稳健性处理,比如使用“各档位之间的累计委托量”而不是“档位价差”来描述盘口压力。
2.2 上交所逐笔成交与STEP协议解析要点
上交所在Level-2行情里推送逐笔成交,底层用的是STEP协议。STEP是证券交易所以FIX协议为基础做的一套二进制压缩版,虽然底层封装和解决方案各家厂商可能不太一样,但核心思路是一致的:用尽可能紧凑的字段编码,在高吞吐场景下保证逐笔成交消息的及时送达。
解析上交所逐笔成交消息时,通常要关注这几个关键信息:成交编号、成交时间、证券代码、成交价格、成交数量、成交金额、买方委托号、卖方委托号。其中买方委托号和卖方委托号虽然不直接对外面披露完整委托链,但可以用来判断一笔成交是主动买入还是主动卖出,也可以用来计算大单拆单的行为特征。
有意思的是,上交所逐笔成交里的“买卖方向”并不是单纯给出一个Buy/Sell字段,很多时候需要结合成交价格相对当前快照买卖价位的位置来判断主动方向。例如成交价等于卖一价时,大概率是主动买;成交价等于买一价时,大概率是主动卖。如果你直接拿接口里的某个方向字段去统计主买主卖,遇到跨档成交或者价格跳变时,统计出来的数据和盘口感受会对不上,这是实际接入中经常遇到的坑。
2.3 时间戳、序号与网络延迟的监控要点
两所Level-2推送数据里都有交易所侧的时间戳,但用途需要区分。深交所的快照带的是交易所行情生成时间,逐笔委托和逐笔成交带的是业务发生时间;上交所逐笔成交里带的时间戳也是业务发生时间,用于描述这笔成交在交易所撮合系统里的时间。
对高频策略来说,除了看时间戳,更要监控的是两所行情的序号连续性。深交所快照和逐笔数据都有序号字段,上交所逐笔成交也有消息序号,它的作用是判断本地接收是否存在漏包。如果序号出现跳变或重复,必须马上触发重新订阅或快照对齐流程,否则后续所有基于“连续数据”的策略计算都会慢慢跑偏,而且很难定位。
我自己在搭建行情监控时,会单独维护一个指标:本地接收时间减去交易所业务时间戳。这个值代表了行情从交易所撮合到策略进程的端到端延迟。在正常机房托管环境下,这个值应该相对稳定;如果某一天突然从5ms跳到了50ms,那大概率不是行情源的问题,而是本机CPU抢占、网卡中断或数据落盘的锁竞争回来了。
3. 策略适配:同一种策略在两所落地时的关键调整
3.1 盘口不平衡策略:深交所高频快照和上交所快照加逐笔补偿
盘口不平衡是Level-2数据最常见的策略应用,核心思路是用买盘力量和卖盘力量的对比预测短期价格方向。常见的因子包括买卖委差、委比、深度加权不平衡等。这个因子在深交所和上交所的实现细节完全不同。
深交所有增量快照优化,实际接收到的快照频率远高于3秒一帧。这意味着你可以在很短的时间窗口内观察委买委卖的变化,做相对高频的盘口不平衡信号。但要注意,深交所快照的档位是虚拟档位,你在计算各档位深度时,要先把快照里的档位映射到“价格区间”上,否则会出现一个档位的委托量突然“暴涨”,其实只是两个真实档位被合并到了一起。
上交所没有增量快照,3秒一帧总让人觉得不够用。所以在做上交所盘口不平衡时,我通常会把逐笔成交作为辅助信号叠加进去:逐笔成交持续以主动买方向成交,但快照里买一委量没有明显下降,说明有人在买一价位不断挂单托盘,这比单纯看3秒快照的委差更真实。换句话说,深交所的策略可以直接消费“高频率快照”,上交所的策略则要学会用“低频快照加高频成交”来重建盘口微观动态。
3.2 基于逐笔委托和逐笔成交的订单流策略:两所的可实现性差异
如果策略的核心逻辑是“识别大单入场、跟踪主力的挂单和撤单行为”,那么我只能说这是深交所Level-2的主场,上交所Level-2做这类策略先天受限。
深交所因为有逐笔委托流,你可以实时跟踪每一笔委托的完整状态:录入、部分成交、全部成交、撤单。基于这个数据,可以构建所谓的“委托流因子”,例如某价位大单挂出后是否持续存在、是否在价格上涨前悄悄撤单等。
上交所没有逐笔委托流,意味着你无法直接看到撤单行为。替代方案是:用每一帧快照之间的“委托队列快照”差异来判断撤单。比如上一帧买一价位有100手委托,这一帧变成了60手,期间逐笔成交只吃掉了10手,那另外30手大概率是撤单了。这个方法的缺点是时效性差(3秒一帧),而且队列快照只有买一卖一前50笔,当队列长度超过50笔时,你看到的只是一个截断视图,撤单推断就更不可靠了。所以做上交所订单流策略,要有预期管理:你能做的是“成交流统计”,很难做“委托流还原”。
3.3 主买主卖、大单拆单策略的适配思路
主买主卖是Level-2数据里最常用的基础统计之一。很多人以为直接看逐笔成交里的方向字段就行,实际两所都要做二次判断。
深交所的逐笔成交虽然可以从成交回报里看到主动方向,但更稳妥的做法是把成交回报和对应的逐笔委托关联起来,确认这笔成交的“主买主卖”是否与价格变动方向一致。上交所在没有逐笔委托的情况下,一般用“成交价与快照买卖价比较”的算法来修正逐笔成交的方向字段。当成交价等于卖一或者高于卖一时判定为主买,等于买一或低于买一时判定为主卖。这个算法在连续竞价阶段基本可靠,但在开盘集合竞价、尾盘集合竞价和涨跌停附近会出现失效,需要额外处理。
大单拆单策略也是一个典型场景。深交所的逐笔委托流可以让你看到一个资金账户可能通过多个子账号连续挂出同方向同价格的委托;上交所则只能从逐笔成交里看到连续的多笔方向一致、价格相近的成交,推测背后可能是同一机构在拆单。前者是“看到原形”,后者是“猜影子”,两种数据颗粒度导致策略置信度完全不同。
4. 行情系统架构设计:如何同时兼容两所规则
4.1 接入层:把两套解码放进一个网关服务里
做兼容两所的行情系统,我强烈建议不要在策略端直接散落地处理两所协议,而是在接入层做一个统一的行情网关。网关对策略进程提供一个标准化的行情消息接口,把深交所和上交所的差异在网关内部消化掉。
这个标准消息接口至少要包含三类消息:快照消息、逐笔成交消息、逐笔委托消息。当接入的是上交所时,逐笔委托消息为空,但接口仍然保留,策略层只要做好“无委托数据”的容错就行。这样做的好处是,你在策略端可以写一套盘口逻辑,通过配置切换数据源,不需要为每一套行情源单独实现一套策略代码。
我个人的实践经验是,不要试图把两所的快照结构完全强行统一到同一个结构体里,否则必然会出现大量冗余字段。更好的方式是快照结构体里保留一个通用的十档价格和十档量,同时增加一个“档位类型”字段,标明这十个档位是标准价差档位还是虚拟聚合档位。策略层根据这个字段自行决定是否要触发修正逻辑。
4.2 深交所增量快照的合成与对齐
深交所增量快照的引入把所有只见过“整包快照”的开发者都折磨了一遍。原因很简单,增量快照不是完整的十档数据,它只包含相对于上一帧发生变化的部分字段。如果你只拿了增量包但没有维护一个正确的“基础快照”上下文,那后续每一帧重建出来的盘口都是错的。
实际工程里,我推荐的做法是:网关层维护一个“当前完整快照缓存”,每收到一帧增量快照,就把变化字段合并到缓存中,再把完整的合成快照广播给策略进程。这里有一个关键点:增量快照依赖上一帧的正确性,如果中途发生序号跳变或者丢包,缓存就“脏”了。所以订阅逻辑里必须有一个强制刷新机制——检测到序号不连续时,立即触发一次基础快照的重新订阅,等基础快照到位后再继续拼接后续增量。
这个机制听起来简单,但很多事故恰恰出在这里。有人为了省事,在序号跳变后不重新拉基础快照,而是继续用脏缓存拼增量,结果连续十几帧的盘口数据都是错的,而策略可能已经把错误数据送进了交易信号模型。请务必重视增量快照的对账和自愈逻辑。
4.3 监控指标与告警:延迟、丢包、序号连续性
一套能长期稳定运行的行情系统,监控指标设计比解码本身更重要。我通常会为每套行情源建立四类监控指标:
- 延迟类指标:本地接收时间减去交易所业务时间戳的差值,观察中位数、90分位、99分位的变化,任何一个分位突然恶化都要能定位到是网络问题还是进程问题。
- 序号连续性指标:监控收到的消息序号是否连续,序号跳变次数一旦超过阈值,立即告警,由人工或自动机制触发快照重订阅。
- 数据完整性指标:以分钟为单位统计快照帧数和逐笔笔数,如果某只深交所股票一分钟内收到的逐笔委托笔数与历史均值相差数倍,多半是解码或订阅过滤逻辑有问题。
- 性能指标:网关进程的CPU、内存、GC次数、网络带宽使用率,尤其是逐笔成交量极大时网络带宽会瞬间冲高,带宽打满是很多问题的根源。
这四类指标的告警阈值,需要根据你的托管环境和策略频率去标定,没有统一答案,但建议设置得“宁可多报不可漏报”。行情数据是策略的原料,原料出问题,下游再好的模型都是空中楼阁。
5. 实盘踩坑与排错实录
5.1 深交所档位跳变导致策略误判
这个坑我记忆很深。当时我们在深交所回测里发现一个盘口不平衡因子净值表现很好,但上实盘后连续两天出现信号抖动,最后定位到问题出在虚拟档位上。某个瞬间,快照的买二价位从10.02元变成10.05元,买二委托量大幅增加,导致系统以为有大买单在托底,实际上只是10.02元到10.04元之间的真实挂单被聚合到了同一个虚拟档位里。
排查时我们把深交所快照和逐笔委托流做了逐笔对比,才发现虚拟档位的合并逻辑会让“档位价差”这个特征在行情活跃期时大时小。修正方法也很直接:所有基于深交所快照的因子,先把虚拟档位按真实价格区间重新映射,或者干脆用逐笔委托自行聚合成标准盘口再计算因子。从那以后,我们对深交所的盘口因子统一多挂了一道“档位归一化”流程。
5.2 上交所逐笔成交方向与实际主买统计的偏差
另一个印象深刻的坑出现在上交所主买主卖统计上。某只股票盘中突然出现一笔高价成交,逐笔成交里面的方向字段标成了主动买,但如果你对照当时的快照,会发现成交价远高于卖五价,明显是异常撮合或错单。如果我们直接把这笔成交计入主买量,当天的资金流向统计就会发生偏差,基于主买主卖构建的量价因子也会被带偏。
后来我们加了一个清洗步骤:对每一笔逐笔成交,先计算成交价与当前快照买一卖一价的距离,如果偏离度过大,标记为“异常成交”,不参与主买主卖统计。这个方法会漏掉一些真实的大宗扫单行为,但它避免了异常值对因子体系的整体污染。从收益风险比看,清洗优先于真相还原。
5.3 断线重连与数据补齐:两所都要有自愈机制
行情链路在实盘中一定会出现断线,问题只在于断线之后你的系统多久能恢复、能不能自动对账。深交所和上交所的断线恢复逻辑不太一样,但核心原则相同:优先恢复基础快照,再重放增量或逐笔数据。
碰到过一次比较惨的情况,上交所逐笔成交进程断线十分钟,但因为通知系统没配告警,直到策略端发现成交数据和盘口完全对不上才察觉。恢复时,我们没有简单地从断点继续接,而是丢弃断线期间的所有逐笔数据,先订阅最新快照,再让策略在“无逐笔成交”的空窗期对做市类策略降低交易频率。这个保守策略虽然损失了十分钟的高频数据,但避免了系统在数据不一致的状态下继续下注。
5.4 两所Level-2接入常见问题速查
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 深交所快照档位价差忽大忽小 | 虚拟档位聚合导致 | 使用逐笔委托自建标准盘口,或对因子做档位归一化 |
| 上交所主买主卖统计和盘口感受不一致 | 未对异常成交做清洗 | 增加成交价与买一卖一价偏离度过滤 |
| 增量快照合成后盘口错误 | 增量序号跳变后未重新订阅基础快照 | 检测序号不连续,强制触发基础快照重新订阅 |
| 上交所逐笔成交延迟突然升高 | 网卡中断或进程CPU抢占 | 监控端到端延迟分位数,定位到具体进程或网络链路 |
| 两所数据在相同时间基准下对不齐 | 时间戳精度或时区处理不一致 | 统一使用纳秒级时间戳,并明确“交易所时间”和“本地接收时间”两个字段 |
| 委托队列超过50笔后无法获取更深度信息 | Level-2只提供前50笔委托明细 | 接受限制,用快照档位统计量辅助推断 |
最后再分享一点个人心得
在两所Level-2规则差异这件事上,别指望交易所短期内会主动统一,策略开发更务实的态度是“因所适配”。我现在的标准流程是:任何新策略先明确目标交易所,再倒推需要哪类Level-2数据字段,最后才写因子和交易逻辑。深交所能做的事,不要想当然地搬到上交所;上交所的逐笔成交优势,也不要在深交所的逐笔委托体系里重复造轮子。
另外一个小技巧是,建议团队把两所的历史行情各存一份,做成离线回放器。回放器不仅能用来验证策略逻辑,还能在接入新数据源或改动解析代码时快速做回归对比。毕竟行情数据是策略的原料,原料加工规则不同,再好的配方也要调整火候。