3. 订单簿结构与价格优先时间优先

集合竞价的撮合结果,最终由订单簿这一数据结构决定。理解订单簿的组成与排序规则,是读懂虚拟成交价、判断撮合顺序、乃至自行解析行情快照的前提。本节从订单簿的静态结构出发,讲清买卖盘的排序逻辑,再落到价格优先与时间优先的具体实现,最后给出订单簿快照的读取与解析方法。

订单簿的组成:两个方向、两个维度

订单簿(Order Book)本质上是按价格分档组织的委托集合。它有两个方向:买方(Bid)与卖方(Ask);每个方向又有两个维度:价格档位与档位内的委托队列。任一时刻,订单簿可以抽象为:

  • 买盘:若干价格档,价格从高到低排列,每档记录该价位的委托总量与委托笔数;
  • 卖盘:若干价格档,价格从低到高排列,每档同样记录总量与笔数;
  • 档内队列:同一价格上的多笔委托,按进入撮合系统的时间先后排成队列。

需要强调的是,订单簿里存的是“尚未成交的剩余委托”,而不是原始下单流水。一笔委托部分成交后,剩余数量仍留在原档位、原队列位置,只有全部成交或撤单才从订单簿中移除。这一点在集合竞价阶段尤其重要:竞价期间不产生连续成交,订单簿只增不减(除撤单外),因此快照反映的是“累积的意向”。

买卖盘的排序规则

两个方向的排序方向相反,但遵循同一条原则——对撮合越有利的委托排在越前面。买盘价格越高,越愿意成交,排在前;卖盘价格越低,越愿意成交,排在前。于是买盘按价格降序、卖盘按价格升序,最优买价(买一)与最优卖价(卖一)分别位于两侧的队首。

档内队列则按时间排序:先进入撮合系统的委托排在队首。这里有一个容易被忽略的细节——时间优先比较的是“进入撮合系统的时间”,而不是投资者点击下单的时间。经过经纪商前置、交易所网关后,委托到达撮合引擎的先后才是有效的时间戳。同一毫秒内到达的委托,通常按网关接收顺序或系统分配的序号进一步区分。

集合竞价阶段,价格优先与时间优先依然生效,但由于不即时成交,其作用体现在“竞价结束时的撮合顺序”上:先按价格排序确定可成交范围,再在边界价格上按时间先后逐笔配对。

价格优先与时间优先的具体实现

把规则落到实现上,可以拆成“排序键”和“匹配循环”两部分。每笔委托携带两个排序键:价格键与时间键。价格优先意味着价格键是主键,时间优先意味着时间键是次键。以买盘为例,排序比较可写成:价格高者优先;价格相同时,时间早者优先。卖盘则价格低者优先,其余相同。

撮合时的匹配循环遵循“取两侧队首、比较价格、能成交则配对”的逻辑:取买一与卖一,若买一价 ≥ 卖一价则成交,成交量取两者剩余量的较小值,成交后更新队首剩余量,耗尽则弹出该委托、露出下一笔;若买一价 < 卖一价则停止。这个循环在连续竞价中逐笔触发,在集合竞价中则在竞价结束时统一执行一次,从而产生单一成交价。

// 撮合匹配循环(示意,买盘降序、卖盘升序)
while (bestBid.price >= bestAsk.price) {
    qty = min(bestBid.remaining, bestAsk.remaining);
    recordTrade(bestBid, bestAsk, qty);      // 记录成交
    bestBid.remaining -= qty;
    bestAsk.remaining -= qty;
    if (bestBid.remaining == 0) popBid();    // 队首耗尽,露出下一笔
    if (bestAsk.remaining == 0) popAsk();
}

这段伪代码体现了两个优先级的协作方式:外层 while 条件由价格优先决定“能否成交”,内层弹出顺序由时间优先决定“谁先成交”。理解这一点,就能解释为什么同一价位上大单可能排在后面却先成交——它进入得早。

订单簿快照的读取与解析

实盘中我们拿不到完整订单簿,只能拿到行情快照。快照通常包含:快照时间、买一至买N的价格与数量、卖一至卖N的价格与数量,部分市场还提供档位委托笔数。解析快照时要注意三件事:

  1. 校验排序方向:买盘价格应严格递减,卖盘应严格递增,若出现乱序说明数据异常或字段错位;
  2. 识别空档与零量:某档数量为 0 表示该价位已无委托,不能当作有效档位参与计算;
  3. 区分快照与逐笔:快照是某一时刻的切片,两次快照之间的队列变化需靠逐笔委托或成交数据还原。

以一份示例快照为例(数据为教学假设):买盘 10.02 元 500 手、10.01 元 800 手、10.00 元 1200 手;卖盘 10.03 元 600 手、10.04 元 400 手、10.05 元 900 手。解析后可判断:最优买价 10.02 低于最优卖价 10.03,当前无连续成交可能;若这是集合竞价末段快照,则虚拟成交价将落在 10.02 与 10.03 之间,具体取决于两侧累积量的交叉点。这正是下一节虚拟成交价计算要处理的问题。

订单簿结构:买盘降序 · 卖盘升序 · 档内时间优先 买盘 Bid(价格降序) 买一 10.02 500 手 · 队列:A→B→C 买二 10.01 800 手 · 队列:D→E 买三 10.00 1200 手 · 队列:F 价格越高越靠前(越优) 同价按进入撮合系统时间先后 卖盘 Ask(价格升序) 卖一 10.03 600 手 · 队列:G→H 卖二 10.04 400 手 · 队列:I 卖三 10.05 900 手 · 队列:J→K 价格越低越靠前(越优) 同价按进入撮合系统时间先后 撮合 引擎 买一≥卖一 才成交 示例数据为教学假设:最优买价 10.02 < 最优卖价 10.03,当前无连续成交

把上述结构记牢,后续计算虚拟成交价时,只需在买盘降序、卖盘升序的序列上做累积量比较,就能快速定位交叉点。订单簿不是静态表格,而是带时间维度的队列集合——价格决定“在哪一档”,时间决定“在档内第几位”,两者共同决定了撮合顺序。