3. 价格优先与时间优先原则

好,咱们今天聊聊撮合引擎里最核心的两个原则——价格优先和时间优先。说实话,这两个词听起来简单,但实际落地时坑特别多。我在早期做交易系统时,就因为在时间优先的实现上偷了个懒,结果被测试团队追着改了三个版本……嗯,咱们一步步来。

3.1 价格优先原则详解

价格优先,说白了就是:谁出的价更优,谁先成交

  • 对于买单:出价高的优先
  • 对于卖单:出价低的优先

你想想看,这其实很符合直觉。你愿意花更多钱买,市场当然先满足你。你愿意便宜卖,买家自然抢着跟你成交。

核心规则:

  • 买单A:价格100.5元,排在前面
  • 买单B:价格100.3元,排在后面
  • 买单C:价格100.5元,排在A后面

成交顺序:A → C → B(价格优先于时间)

我在项目中遇到过一个问题:有些新手会把「价格优先」理解成「价格绝对值优先」。比如有人觉得卖单价格越低越好,买单价格越高越好——这没错。但有人会问:「那如果卖单价格是负数呢?」嗯,实际交易中不会出现这种情况,但理论上确实要处理边界值。我建议在代码里加一个价格范围校验,避免极端值导致撮合逻辑异常。

3.2 时间优先原则详解

时间优先就一句话:同等价格下,谁先到谁先成交

为什么需要这个原则?因为价格优先只能解决「谁出价更好」的问题。当两个人出价完全一样时,总得有个先后顺序吧?

我的习惯:时间戳精度至少到微秒级。我曾经用毫秒级时间戳,结果在高频场景下,两个订单的时间戳完全一样,导致撮合顺序随机——这绝对是个灾难。后来我统一改用纳秒级时间戳 + 自增序列号,双重保证。

时间优先的实现其实有个细节:是先到先得,还是先到先排队?

我解释一下。在订单簿里,每个价格档位其实是一个队列。新来的订单会追加到队列尾部。撮合时,从队列头部开始匹配。这就是典型的FIFO(先进先出)。

// 伪代码示例:时间优先队列
struct PriceLevel {
    double price;
    std::deque<Order> orders;  // 队列头部是最早的订单
};

void match(Order& taker) {
    auto& level = book[taker.price];
    while (!level.orders.empty() && taker.remaining > 0) {
        Order& maker = level.orders.front();
        // 撮合逻辑...
        if (maker.filled()) {
            level.orders.pop_front();  // 先到的先走
        }
    }
}

你看,代码其实不复杂。但要注意:队列的插入和删除操作必须加锁。我曾经见过一个系统,因为忘记加锁,导致两个线程同时修改队列,结果订单顺序全乱了……嗯,那场面,挺尴尬的。

3.3 两者结合的实际效果

价格优先和时间优先不是独立存在的。它们是一套组合拳。实际撮合时,流程是这样的:

  1. 先按价格排序:买单从高到低,卖单从低到高
  2. 同价格下,按时间排序:先到先得
  3. 逐层匹配,直到一方订单全部成交或价格不满足条件

避坑指南:我曾经犯过一个错误——在价格优先排序时,用了浮点数直接比较。结果因为精度问题,两个明明应该相等的价格(比如100.1和100.1000000001)被判定为不同价格,导致撮合顺序出错。后来我统一改用整数表示价格,比如把价格乘以10000,用整数比较。

咱们来看一个实际例子:

订单ID 方向 价格 数量 时间
1 100.5 10 09:30:01.000
2 100.3 5 09:30:00.500
3 100.5 8 09:30:00.800
4 100.4 12 09:30:01.200

假设现在进来一个卖单,价格100.4,数量15。撮合过程是这样的:

  • 先看买单队列:价格从高到低排序
  • 价格100.5的买单有:订单1(时间09:30:01.000)和订单3(时间09:30:00.800)
  • 同价格下,时间优先:订单3先成交(8手)
  • 订单3成交后,卖单还剩7手
  • 继续匹配订单1(10手),成交7手,卖单全部成交
  • 订单1还剩3手,留在订单簿里

你看,订单2虽然时间最早,但因为价格只有100.3,低于卖单的100.4,所以根本没机会成交。这就是价格优先的威力——价格差一点,顺序差很多

实际效果总结:

  • 价格优先保证了市场效率:最优价格最先成交
  • 时间优先保证了公平性:同等价格下先到先得
  • 两者结合,既防止了价格操纵,又避免了排队插队

我个人习惯在实现时,把价格优先和时间优先的逻辑封装成一个独立的匹配引擎模块。这样如果以后要改成「价格优先 + 数量优先」或者「价格优先 + 随机优先」,只需要替换这个模块就行。嗯,设计模式里的策略模式,你懂的。

最后说一句:这两个原则看起来简单,但实际在高并发场景下,时间戳的精度、队列的并发安全、价格的精度处理,每一个细节都可能成为系统的瓶颈。我建议你在写代码之前,先把这些边界情况想清楚——别像我当年那样,上线前一天才发现问题。