撮合引擎:交易所是怎么把买单卖单凑成一笔交易的 书架

第 10 章 / 共 12 章

第十章 · 动手写一个撮合引擎

前九章都是设计文档

回到第二章那个画面:横向一格一格的价格台阶,纵向每格里一支FIFO队伍。现在把它变成代码。你会惊讶于这一步有多顺理成章——不是因为我们聪明,而是因为规则讲了九章,数据结构已经被规则决定好了。

三张表撑起一本簿

一个交易品种的订单簿,内存里就三样东西:

价格表用什么实现?教科书答案是红黑树(一种自平衡的二叉搜索树):插入、删除、取最大/最小值都是O(log n)。工程上还有两个变体值得知道:如果价格档位稀疏,用跳表也行;如果tick很小、价格区间有限(比如国债期货价格总在95-105之间跳动),可以直接开一个数组,下标就是价格格数——取买一卖一变成数组扫描,快到极致。很多顶级撮合引擎就是这么干的:把"价格"从连续的量变成离散的格,是tick size规则送我们的免费性能。

每个价位的订单队列,用双向链表。为什么不用数组?因为高频操作是"从中间摘掉一个"(撤单)——链表O(1),数组O(n)。订单对象里带prev/next指针,订单索引的哈希表直接存节点地址,撤单就是两步:哈希表查到节点,链表摘除。

大白话:三张表各管一件事——价格表管"最好的价在哪"(有序),链表管"同价谁先来的"(FIFO),哈希表管"我这单在哪"(快查)。撮合引擎的全部性能秘密就是:每个高频操作都落在其中一张表的O(1)或O(log n)上。

核心循环:五十行伪代码

把第五章的循环翻译成更接近代码的样子(以买单为例,卖单对称):

function match(order):
  while order.remaining > 0:
    level = 卖侧价格表.最低价位  // 卖一
    if level不存在: break      // 对面没人了
    if order是限价单 and level.price > order.price: break  // 碰不上
    maker = level.队首订单
    if maker.账户 == order.账户: 按STP处理; continue
    qty = min(order.remaining, maker.remaining)
    产生成交(price=level.price, qty, taker=order, maker=maker)
    order.remaining -= qty; maker.remaining -= qty
    if maker.remaining == 0:
      level.出队(); 订单索引.删除(maker.id)
      if level空了: 卖侧价格表.删除(level.price)
  处理剩余(order)  // GTC入簿 / IOC丢弃 / FOK已前置校验
  返回成交列表

对照第五章的算例走一遍,每一行都对得上。注意几个被故意写对的地方:成交价取level.price(被动方的价);成交输出里标了谁是taker谁是maker(第八章的计费靠它);STP检查在成交之前;价位空了才从价格表删——价格表只增删"档位",不碰档位内的顺序。

复杂度:为什么这样就是快

数一下每个操作的开销:新限价单撮合,每圈循环是O(1)(取最优价、队首、min都是常数),总共跑K圈,K=成交笔数——撮合的成本和成交量成正比,不和簿子大小成正比。这是好性质:墙再厚,只成交一格就只花一格的时间。剩余入簿O(log n),撤单O(1),查买一卖一O(1)(缓存队首价位)。

这就是主流撮合引擎单品种每秒几十万单、延迟个位数微秒的底气:没有任何一步需要遍历整个簿子。慢的系统多半是在某个地方偷偷做了全表扫描——比如每次成交后重新排序,或者用数组存队列导致撤单是O(n)。

整数,整数,整数

代码里有一个没画出来但写错就出人命的决定:价格和数量必须用整数,禁止浮点。10.01元存成1001(分),0.001个币存成1000(最小单位)。浮点数的0.1+0.2≠0.3,在别的系统是误差,在交易所是事故——两边对不上账,对账(第九章那张网)立刻报警。所有交易所内部都用定点整数,显示给用户时才除以缩放因子。这条没有例外。

先单线程,别急着并发

你可能已经手痒想加锁、加线程了。忍住。下一章整章讲为什么:撮合引擎的正确性完全建立在"事件严格按序处理"上,而多线程的第一作用就是破坏顺序。业界的主流答案——包括LMAX这样做到每秒六百万笔的系统——是单线程。我们现在先把单线程版本写对,下一章再把它做快、做皮实。

到这里,你已经有了一个能撮合的撮合引擎:三张表加一个循环,五十行伪代码。但它还只是个"实验室玩具"——断电就没了,重启就失忆。下一章把它变成一台可以托付真金白银的机器。

撮合引擎:交易所是怎么把买单卖单凑成一笔交易的 · 王建硕
费曼式写法 · 由多个 AI 代理撰写与互相审校