上交所逐笔数据“隐藏”了什么?一文讲清沪深口径差异与还原方案
导语:在高频交易和因子研究中,逐笔数据的完整性直接影响策略结果的准确性。但在沪深两市中,上交所逐笔委托与深交所的披露方式并不一致,尤其是即时全部成交或部分成交的委托,往往会在原始数据中出现“缺失”或“缩量”现象,导致同一套统计逻辑在不同交易所上算出不同结果。针对这一问题,DolphinDB 提供了委托订单还原引擎(createOrderReconstituteEngine),通过还原上交所缺失的原始委托,统一沪深逐笔数据口径,让快照合成、订单流因子计算等上层应用可以复用同一套代码。
一个让人困惑的现象
做高频策略的团队也许会遇到一件怪事:同一套统计逻辑,比如“过去 60 秒新增了多少买单”放到深交所的数据上算出来的结果是对的,放到上交所却总是偏小。
排查了半天,代码没有问题,问题出在数据本身——两个交易所对外发布逐笔数据的方式不一样。
沪深逐笔的差异从哪来
深交所的逐笔委托是完整披露的:只要一笔委托进入撮合系统,无论最终是否成交,都会先发一条委托记录。上交所则做了精简,对能够立即撮合的委托区别处理:
结果就是:只要统计口径是“市场究竟来了多少委托”,而不是“最终成交了多少”,上交所的原始数据就会系统性漏记一部分。
于是开发者不得不为沪深两市各维护一套预处理逻辑——上交所这边还要额外关联逐笔成交、反推缺失的委托量——策略迁移和维护成本因此上升。
委托订单还原引擎:把缺的那条委托补回来
针对上述场景,DolphinDB 开发了委托订单还原引擎(createOrderReconstituteEngine)专门解决这个问题:根据逐笔成交与逐笔委托之间的委托号关联关系,实时还原上交所缺失的原始委托,让输出数据和深交所逐笔委托具有一致的语义。
还原逻辑对应前面两种情形:
为了便于后续做因子计算、快照重建等场景,引擎在输出结果里维护了两个标记:一个用来说明这条记录是原始数据还是引擎补出来的,另一个记录了还原后的正确先后顺序。
一套接口适配实盘和回测两种计算场景
无论是拿历史数据回测因子,还是接入实时行情跑线上策略,都可以通过委托订单还原引擎实现委托补齐。
具体使用逻辑只需要三步:
Step1. 准备数据。
把逐笔委托和逐笔成交合并成一张统一格式的表,核心信息包括证券代码、时间、价格、数量、买卖方向和委托编号。合并表通过 SourceType 字段区分消息来源:0 表示逐笔委托,1 表示逐笔成交。
Hint:为了便于统一表结构,可以在委托数据时,将其 BuyNo 和 SellNo 均置为 0。
批计算场景下,这一步可以通过 SQL 直接处理合并;流计算场景下,则有两种方式处理:
合并后的 OrderTrans 表结构如下图所示:

Step2. 创建引擎
DolphinDB 以 createOrderReconstituteEngine 函数接口的形式提供委托订单还原引擎。引擎的输入表为标准化后的逐笔委托与逐笔成交合并表,输出表在原始字段基础上增加 orderMark 和 orderIndex 两列,用于标识还原结果来源以及还原后在通道内的顺序。
参考代码如下:
1 engine = createOrderReconstituteEngine( 2 name="OrderReconstitute", 3 dummyTable=objByName(inputTbName), 4 outputTable=objByName(inputTbName+"OrderReconstitute"), 5 inputColMap=inputColMap)
其中,inputColMap 是一个字典,负责把合并表里的列,对应到引擎能识别的几类信息:
| 说明 | |
|---|---|
| codeColumn | 标的股票代码 |
| typeColumn | 区分市价、限价、撤单等委托类型,以及成交、撤单等成交类型 |
| priceColumn / qtyColumn | 委托或成交对应的价格和股数 |
| sideColumn | 买卖方向:买单还是卖单 |
| buyOrderColumn / sellOrderColumn | 用于把一笔成交关联回它对应的委托,是还原逻辑的核心依据 |
| msgTypeColumn | 区分这条记录是委托、成交,还是市场状态消息 |
Step3. 注入数据
引擎创建好之后,只需要把数据喂进去就能拿到还原结果。
需要注意:委托还原引擎会维护每个通道内的订单状态,因此同一个引擎一次只能处理同一天、同一个行情通道(ChannelNo)的数据,且必须按原始发布顺序(ApplSeqNum 顺序)接入,这是引擎能正确判断“这笔委托是否被还原过”的前提。
历史计算场景下,可以直接通过 append! 接口向引擎注入数据:
// 选取一天一个通道的数据 testData = select * from SHOrderTrans where TradeDate=2026.04.29 and ChannelNo=1 order by ApplSeqNum // 将历史数据追加到委托还原引擎 getStreamEngine("OrderReconstitute").append!(testData)
流计算场景下,则通过订阅函数的 handler 接口注入引擎:
1 subscribeTable(tableName="OrderTrans", actionName="orderTrans_to_reconstitute", offset=-1, handler=getStreamEngine("OrderReconstitute"), msgAsTable=true)
注意:若多通道并行接入,建议按 ChannelNo 拆分输入流或为每个通道创建独立任务。
案例实战
场景一:对接快照合成引擎
在实际的高频数据处理链路里,逐笔委托和逐笔成交通常不是终点,而是中间产物:拿到干净、口径一致的逐笔数据后,下一步往往是合成 Orderbook 快照,把某个时间点的完整盘口(各档位的买卖价格、数量、委托笔数)重建出来,供后续因子计算、策略回测直接使用。
这一步对数据的要求很高——快照合成天然依赖“先有委托、后有成交”的真实时间顺序,任何一笔委托的缺失或顺序错乱,都会直接反映在盘口的挂单量和委托笔数上。
委托订单还原引擎的输出,可以在流程上直接对接 DolphinDB 快照合成引擎——只需要把用于指定数据顺序的字段,从原始委托编号切换为还原引擎生成的顺序标记(orderIndex)即可。
1 inputColMap = dict(`codeColumn`timeColumn`typeColumn`priceColumn`qtyColumn 2 `buyOrderColumn`sellOrderColumn`sideColumn`msgTypeColumn`seqColumn, 3 `SecurityID`Time`Type`Price`Qty`BuyNo`SellNo`BSFlag`SourceType`orderIndex)
快照合成引擎的详细使用教程参考 基于逐笔数据合成高频 Orderbook:DolphinDB Orderbook 引擎
场景二:让订单流因子沪深两市共用一套代码
除了快照合成,高频因子研究里也有一类指标绕不开委托数据的完整性问题——订单流因子,比如过去 60 秒新增买/卖单委托笔数和委托量、过去 5 分钟买/卖单委托量的波动率。这些指标统计的是“市场上到达了多少委托”,而不是“最终成交了多少”,如果直接在上交所原始数据上计算,会因为漏掉立即成交的委托而系统性偏低,进而影响净买入委托量、委托量不平衡率等衍生因子的准确性。
在该场景下,上交所和深交所可以复用同一套因子计算逻辑。两市在计算逻辑上的差异只保留在过滤条件中:
SourceType == 0 筛选;SourceType == 0 and Type == 2 筛选。因子逻辑定义参考脚本如下:
1 /** 2 * A26/A27: 3 * 过去 5 分钟买/卖单单笔委托量的波动率(5 分钟窗口) 4 */ 5 def calcA26A27(orderTb, exchange){ 6 if(exchange == "SZ"){ 7 return select 8 std(iif(BSFlag==1, Qty, NULL)) as A26, 9 std(iif(BSFlag==2, Qty, NULL)) as A27 10 from orderTb 11 where SourceType == 0 12 group by TradeDate, SecurityID, bar(Time, 5m) as Time 13 } else if(exchange == "SH"){ 14 return select 15 std(iif(BSFlag==1, Qty, NULL)) as A26, 16 std(iif(BSFlag==2, Qty, NULL)) as A27 17 from orderTb 18 where SourceType == 0 and Type == 2 19 group by TradeDate, SecurityID, bar(Time, 5m) as Time 20 } else { 21 throw "Unsupported exchange: " + exchange 22 }
定义了计算所用到的所有因子后,将其封装在一个通用订单流因子计算函数 calcMarketFactors 中,最终的上交所和深交所因子计算脚本可以参考:
1 // 1. 上交所:使用委托还原后的逐笔数据 2 shOrderTb = select * from objByName("orderTrans2OrderReconstitute") 3 order by orderIndex 4 5 // 2. 深交所:直接使用标准化后的逐笔合并数据 6 szOrderTb = select * from SZOrderTrans 7 order by ApplSeqNum 8 9 // 3. 分别调用统一计算函数 10 shFactorRes = calcMarketFactors(shOrderTb, "SH") 11 szFactorRes = calcMarketFactors(szOrderTb, "SZ")
其中,上交所基于委托订单还原后的 orderTrans2OrderReconstitute 计算,深交所基于标准化后的逐笔合并表 SZOrderTrans 计算。
两市算出来的结果字段结构完全一致,可以直接合并进同一张因子表,不用再为两地行情各写一套代码、各查一次数——这也是委托订单还原引擎的核心价值所在:把交易所层面的数据差异解决在最底层,上层的因子逻辑、快照合成逻辑都可以按同一套框架复用。

上交所因子计算结果示例

深交所因子计算结果示例
结语
上交所逐笔数据的“缺失”看似是一个细节问题,实际却会让同一套统计逻辑在沪深两市算出不一致的结果,逼着开发者反复为交易所差异打补丁。与其在每个应用场景里各自处理,不如把这类问题一次性解决在数据基础设施层——委托订单还原引擎正是这个思路的体现:把交易所层面的口径差异消化在数据层,上层不管接入多少种计算逻辑,都可以直接复用,不必为每一次新需求重新踩一遍坑。
这也是 DolphinDB 在开发时始终坚持的方向——把复杂的数据治理工作沉淀成标准化的引擎能力,让上层的策略和研究工作可以专注于真正创造价值的部分。
完整实战代码可点击 委托订单还原引擎代码 获取~

同一套统计逻辑,放到深交所的数据上算出来的结果是对的,放到上交所却总是偏小?DolphinDB 一文讲清沪深口径差异与还原方案。
浙公网安备 33010602011771号