3、挂单引擎设计:订单匹配引擎原理、FIFO与Pro-Rata算法、订单队列管理、撮合流程实现
挂单引擎,说白了就是交易所的「心脏」。你下的每一笔限价单,最终都要交给它来处理。我做了这么多年量化,见过太多因为挂单引擎设计不合理导致的惨案——滑点失控、成交延迟、甚至系统崩溃。今天咱们就把这块硬骨头啃下来。
3.1 订单匹配引擎的核心原理
匹配引擎要解决的核心问题就一个:当买方和卖方的价格条件满足时,如何高效地完成配对。我个人习惯把它拆成三个步骤:
- 价格优先:买单出价高的排前面,卖单出价低的排前面
- 时间优先:同等价格下,先来的先成交
- 数量匹配:按规则把买卖双方的订单撮合到一起
你想想看,如果价格优先和时间优先这两个原则没定好,市场会乱成什么样?我在项目中遇到过一家小交易所,他们为了「提高流动性」改了时间优先规则,结果大户直接写脚本刷单抢跑,普通用户根本吃不到单子。
3.2 FIFO与Pro-Rata算法
这两个算法,是挂单引擎里最经典的「双胞胎」。一个简单粗暴,一个公平但复杂。咱们一个一个说。
3.2.1 FIFO(先进先出)
FIFO 的逻辑特别直白:谁先挂单,谁先成交。就像排队买奶茶,先来的先拿到。
// 伪代码示例:FIFO匹配逻辑
class FIFOMatcher:
def match(self, buy_orders, sell_orders):
# 按价格排序后,再按时间排序
buy_orders.sort(key=lambda o: (-o.price, o.time))
sell_orders.sort(key=lambda o: (o.price, o.time))
while buy_orders and sell_orders:
buy = buy_orders[0]
sell = sell_orders[0]
if buy.price < sell.price:
break # 价格不匹配,停止
trade_qty = min(buy.qty, sell.qty)
# 执行成交...
buy.qty -= trade_qty
sell.qty -= trade_qty
if buy.qty == 0: buy_orders.pop(0)
if sell.qty == 0: sell_orders.pop(0)
FIFO 的好处是简单、可预测。但有个问题——大户可以拆单插队。我曾经见过一个做市商,把100个比特币的卖单拆成1000个0.1比特币的小单,全部挂在最优价格上,直接把后面的小散户堵死。
3.2.2 Pro-Rata(按比例分配)
Pro-Rata 就温和多了。它不按时间排队,而是按订单数量比例分配成交。比如盘口有10个比特币的卖单,你挂了5个,别人挂了5个,那新来的买单就会一人一半。
// 伪代码示例:Pro-Rata匹配逻辑
class ProRataMatcher:
def match(self, buy_orders, sell_orders):
# 按价格分组,同一价格下按比例分配
price_level = sell_orders[0].price
total_qty = sum(o.qty for o in sell_orders if o.price == price_level)
for order in sell_orders:
if order.price != price_level:
break
ratio = order.qty / total_qty
# 按比例分配成交
trade_qty = incoming_qty * ratio
# 执行成交...
Pro-Rata 的好处是公平,大户没法通过拆单来抢跑。但坏处也很明显——计算量大。每次匹配都要算比例,订单量一上来,CPU 直接拉满。我记得有一次帮客户优化引擎,Pro-Rata 的匹配延迟比 FIFO 高了整整3倍。
| 特性 | FIFO | Pro-Rata |
|---|---|---|
| 公平性 | 时间优先,易被拆单攻击 | 按比例分配,更公平 |
| 性能 | O(1) 匹配,极快 | O(n) 计算,较慢 |
| 适用场景 | 高吞吐、低延迟场景 | 流动性好、大户多的市场 |
| 实现复杂度 | 简单 | 中等 |
3.3 订单队列管理
订单队列,就是挂单引擎的「内存数据库」。所有未成交的限价单都排在这里。管理不好,轻则成交慢,重则内存溢出。
我个人习惯用价格-时间双索引的结构。说白了就是:
- 按价格建一个红黑树或跳表(快速定位价格档位)
- 每个价格档位下,再按时间建一个链表(FIFO 队列)
这样查询和插入都是 O(log n) 级别,性能杠杠的。
3.4 撮合流程实现
好了,理论说完了,咱们看看实际撮合流程怎么走。我画了一张图,帮你理清思路。
流程其实不复杂,核心就三步:
- 接收订单:先做基础校验,比如价格不能为负、数量不能为零
- 价格检查:看能不能和盘口对上的单子匹配
- 匹配判断:能匹配就执行撮合,不能匹配就丢进队列
嗯,这里要注意一个细节——部分成交。比如你挂了10个比特币的买单,盘口只有3个卖单,那先成交3个,剩下的7个继续排队。这个逻辑在代码里要处理好,不然容易出bug。
好了,挂单引擎的核心内容就这些。FIFO 和 Pro-Rata 各有优劣,选哪个取决于你的业务场景。订单队列管理要注重性能,撮合流程要严谨。把这些搞明白了,你的量化交易系统就稳了一半。
公众号:蓝海资料掘金营,微信deep3321