问题描述
版主好:
构建订单簿时,如果出现逐笔委托和逐笔成交丢数据的现象就无法正确还原订单簿。以今天 600686 为例:正确的十档如下:
解决方案
在量化交易中,利用 Level2 (L2) 数据(逐笔委托和逐笔成交)构建高精度的订单簿是许多高频和微观结构策略的基础。然而,由于网络延迟、系统负载或数据源本身的问题,L2 数据丢包是一个常见且棘手的问题。当发生丢包时,基于增量更新的订单簿就会出现偏差,导致策略做出错误的交易决策。
针对 QMT 平台上 L2 数据丢包导致订单簿构建失败的问题,可以采取以下几种策略来应对和修复:
1. 定期使用快照数据进行校准 (Snapshot Calibration)
这是最常用且最有效的解决方案。虽然逐笔数据(Tick-by-Tick)用于实时更新订单簿,但交易所也会定期发送行情快照(Snapshot,例如每 3 秒一次的十档行情)。
- 机制:在接收逐笔数据的同时,订阅并接收快照数据。
- 校准逻辑:每当收到一个新的快照时,将其与当前通过逐笔数据构建的内部订单簿进行比对。如果发现不一致(例如价格档位缺失、数量不匹配),则以快照数据为准,强制覆盖和重置内部订单簿。
- QMT 实现:在 QMT 中,你可以同时订阅
tick(分笔数据,包含快照信息)和 L2 的逐笔数据。利用ContextInfo.get_full_tick()或订阅回调中的快照信息来定期修正你的订单簿状态。
2. 序列号检测与断线重连 (Sequence Number Tracking)
高质量的 L2 数据流通常会为每条逐笔委托和逐笔成交分配一个连续的序列号(Sequence Number)。
- 机制:在处理每条逐笔数据时,检查其序列号是否与上一条连续。
- 处理丢包:如果发现序列号跳跃(例如收到了 100,下一条是 105),则明确知道发生了丢包。
- 应对措施:
- 短时间容忍:如果丢包数量很少,且策略对极短期精度要求不高,可以等待下一个快照到来进行自动修复。
- 主动重置:一旦检测到丢包,立即将内部订单簿标记为“不可信”状态,暂停基于订单簿的交易信号生成,直到下一个完整的快照数据到来并完成重置。
- 请求重传:某些高级数据接口支持根据序列号请求重传丢失的数据包(QMT 平台具体是否支持需参考其底层数据接口文档,通常普通 API 不提供此功能)。
3. 订单簿合理性校验 (Sanity Checks)
即使没有序列号,也可以通过一些逻辑规则来判断订单簿是否出现了严重错误。
- 价格交叉:买一价(Bid 1)大于或等于卖一价(Ask 1)。在正常的连续竞价阶段,这是不可能发生的(除非是特殊的撮合机制)。如果出现,说明订单簿已损坏。
- 负数量:某个价格档位的委托数量变为负数。这通常是因为丢失了该档位的初始委托单,但收到了后续的撤单或成交单。
- 极端价格偏离:订单簿中出现了偏离当前市场价格极大的异常委托,且长时间未被清理。
- 应对:一旦触发这些合理性校验失败,立即清空当前订单簿,并等待下一个快照数据进行重建。
4. 优化网络和系统环境
减少丢包的根本方法是优化接收端的基础设施。
- 网络连接:确保运行 QMT 的服务器或本地机器拥有稳定、低延迟的网络连接。尽量使用有线网络而非无线网络。
- 系统负载:L2 数据量巨大,处理逐笔数据需要消耗大量的 CPU 和内存资源。确保你的 Python 策略代码足够高效(例如使用
Numpy或Cython优化核心计算逻辑),避免因处理不过来导致接收缓冲区溢出而丢包。 - 独立进程:如果可能,将数据接收和订单簿构建逻辑放在一个独立的线程或进程中,与策略计算和下单逻辑分离,以提高数据处理的实时性。
总结建议
在 QMT 平台中,最务实的做法是结合逐笔数据和快照数据。以逐笔数据驱动订单簿的实时微小变动,以快照数据作为“锚点”进行定期强制校准。当检测到订单簿异常(如价格交叉或负数量)时,立即停止交易并等待快照重置。这样可以在保证一定实时性的同时,最大程度地降低丢包带来的风险。