第二章:撮合引擎原理——价格优先、时间优先,市价单与限价单的交互逻辑
大家好,我是老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。
撮合引擎会怎么做?
- 先看最优卖单:价格10.00,数量100。全部吃掉。剩余需求:300 - 100 = 200。
- 再看下一个卖单:价格10.05,数量200。全部吃掉。剩余需求:200 - 200 = 0。
- 成交完成。市价买单最终以10.00和10.05两个价格成交,平均成交价 = (100*10.00 + 200*10.05) / 300 ≈ 10.033。
你看,市价单就像一头饿狼,从最优价格开始一路吃下去,直到吃饱为止。
2.5 核心流程图:撮合引擎的完整逻辑
下面我用一张SVG图来展示整个撮合流程。这张图我画了很多遍,算是我的“压箱底”了。
这张图我解释一下:
- 收到订单后,先判断是市价单还是限价单。
- 市价单直接按价格顺序“吃”对手盘,直到完全成交或没有对手盘。
- 如果市价单没有完全成交(比如流动性不足),剩余部分会自动转为限价单,挂到订单簿上。
- 限价单则直接插入订单簿,等待被吃。
一个小技巧:
在实际开发中,市价单转限价单的“价格”怎么定?我一般用当前最优对手盘价格。比如市价买单没吃完,剩余部分就按当前最优卖单价格挂成限价买单。这样既保证了剩余部分能尽快成交,又不会因为价格太离谱而影响市场。
2.6 实战中的坑与优化
最后,分享几个我在实战中踩过的坑:
- 并发问题:撮合引擎是高频操作,多个订单同时到达时,必须加锁或使用无锁队列。我曾经用Python的threading.Lock,结果性能惨不忍睹。后来改用Redis的原子操作,才勉强撑住。
- 浮点数精度:价格和数量用浮点数?千万别。我见过因为浮点数精度导致成交价差0.0001的案例,客户差点起诉。用整数(比如最小价格单位)或Decimal类型。
- 订单簿重建:系统重启后,订单簿怎么恢复?我建议用WAL(Write-Ahead Logging)机制,每次订单操作都先写日志,再更新内存。这样即使崩溃,也能从日志重建。
总结一下:
撮合引擎的核心就是“价格优先、时间优先”这八个字。市价单和限价单的交互,说白了就是“主动吃”和“被动等”的关系。理解了这个,你就掌握了交易系统的灵魂。
公众号:蓝海资料掘金营,微信deep3321