第二章:撮合引擎原理——价格优先、时间优先,市价单与限价单的交互逻辑

大家好,我是老K。

上一章我们聊了订单簿的数据结构,说白了就是一张大表,左边挂买单,右边挂卖单。但光有这张表还不够,你得有个“裁判”来决定谁先成交、怎么成交。这个裁判,就是撮合引擎。

今天我们就来拆解这个核心模块。我个人习惯把撮合引擎比作一个“红绿灯系统”——它不生产订单,它只是订单的搬运工。但搬运的规则,决定了整个市场的公平与效率。

2.1 价格优先:谁出价好,谁先走

先问一个问题:为什么要有价格优先?

你想想看,如果我想买比特币,我出价10万,另一个人出价9万。市场上有卖家愿意以9.5万卖出。你说,这枚比特币该卖给谁?

当然是出价10万的我。因为我出的价更高,卖家能多赚5000块。这就是价格优先的核心——买单价格高的优先成交,卖单价格低的优先成交

核心规则:

  • 买单:价格从高到低排序(谁出价高,谁排前面)
  • 卖单:价格从低到高排序(谁要价低,谁排前面)

我在项目中遇到过一个问题:有人问,如果两个买单价格一样怎么办?嗯,这就引出了第二个原则——时间优先。

2.2 时间优先:先来后到,公平竞争

价格相同的情况下,谁先挂单,谁先成交。这个逻辑很朴素,就像排队买奶茶,先来的先得。

但这里有个细节:时间优先只对同一价格有效。不同价格之间,永远是价格优先说了算。说白了,价格是“硬通货”,时间是“软约束”。

避坑指南:

我曾经在开发一个期货撮合系统时,忽略了时间戳的精度问题。两个订单在同一微秒到达,结果系统随机排序,导致客户投诉。后来我改成了纳秒级时间戳 + 订单到达序号的双重排序,才彻底解决。

记住:时间优先的前提是“时间可比较”。如果精度不够,就会变成“随机优先”。

2.3 市价单 vs 限价单:两种订单的“相爱相杀”

好了,有了规则,我们来看看两种最常见的订单类型。

2.3.1 限价单:指定价格,耐心等待

限价单就是“我只愿意以某个价格成交,高了我不买,低了我不卖”。比如我挂一个限价买单,价格10万,数量1个BTC。那么只有当卖单价格 ≤ 10万时,才会成交。

限价单的特点是:价格可控,但成交时间不确定。可能秒成交,也可能挂一天都没人理你。

2.3.2 市价单:立即成交,价格随行就市

市价单就是“我不在乎价格,我只要立刻成交”。比如我下一个市价买单,系统会立即以当前最优的卖单价格成交。如果卖单不够,就继续吃下一个价格的卖单,直到我的数量全部成交。

市价单的特点是:成交速度快,但价格不可控。遇到流动性差的时候,可能会滑点严重。

注意:

市价单在极端行情下非常危险。我见过有人用市价单在流动性枯竭时买入,结果成交价比预期高了5%。所以,我个人建议:大额交易尽量用限价单,或者用“冰山订单”拆分。

2.4 交互逻辑:当市价单遇到限价单

现在我们来模拟一个真实的撮合场景。

假设当前订单簿如下:

方向 价格 数量 类型
卖单 10.00 100 限价
卖单 10.05 200 限价
卖单 10.10 150 限价

现在,来了一个市价买单,数量300。

撮合引擎会怎么做?

  1. 先看最优卖单:价格10.00,数量100。全部吃掉。剩余需求:300 - 100 = 200。
  2. 再看下一个卖单:价格10.05,数量200。全部吃掉。剩余需求:200 - 200 = 0。
  3. 成交完成。市价买单最终以10.00和10.05两个价格成交,平均成交价 = (100*10.00 + 200*10.05) / 300 ≈ 10.033。

你看,市价单就像一头饿狼,从最优价格开始一路吃下去,直到吃饱为止。

2.5 核心流程图:撮合引擎的完整逻辑

下面我用一张SVG图来展示整个撮合流程。这张图我画了很多遍,算是我的“压箱底”了。

撮合引擎核心流程图 收到新订单 市价单? 按价格顺序吃单 插入订单簿 完全成交? 成交完成 转为限价单

这张图我解释一下:

  • 收到订单后,先判断是市价单还是限价单。
  • 市价单直接按价格顺序“吃”对手盘,直到完全成交或没有对手盘。
  • 如果市价单没有完全成交(比如流动性不足),剩余部分会自动转为限价单,挂到订单簿上。
  • 限价单则直接插入订单簿,等待被吃。

一个小技巧:

在实际开发中,市价单转限价单的“价格”怎么定?我一般用当前最优对手盘价格。比如市价买单没吃完,剩余部分就按当前最优卖单价格挂成限价买单。这样既保证了剩余部分能尽快成交,又不会因为价格太离谱而影响市场。

2.6 实战中的坑与优化

最后,分享几个我在实战中踩过的坑:

  1. 并发问题:撮合引擎是高频操作,多个订单同时到达时,必须加锁或使用无锁队列。我曾经用Python的threading.Lock,结果性能惨不忍睹。后来改用Redis的原子操作,才勉强撑住。
  2. 浮点数精度:价格和数量用浮点数?千万别。我见过因为浮点数精度导致成交价差0.0001的案例,客户差点起诉。用整数(比如最小价格单位)或Decimal类型。
  3. 订单簿重建:系统重启后,订单簿怎么恢复?我建议用WAL(Write-Ahead Logging)机制,每次订单操作都先写日志,再更新内存。这样即使崩溃,也能从日志重建。

总结一下:

撮合引擎的核心就是“价格优先、时间优先”这八个字。市价单和限价单的交互,说白了就是“主动吃”和“被动等”的关系。理解了这个,你就掌握了交易系统的灵魂。


公众号:蓝海资料掘金营,微信deep3321