4、撮合引擎原理:价格优先、时间优先原则

做量化交易这些年,我见过不少新手一上来就研究各种高大上的策略模型,结果连订单是怎么成交的都没搞明白。说实话,这就像你连方向盘都没摸熟就想去开F1。今天咱们就来聊聊撮合引擎的核心——价格优先、时间优先。这个原则,说白了就是交易所的「交通规则」。

4.1 为什么需要撮合规则?

想象一下,你站在菜市场里,有人喊「我出5块买白菜」,有人喊「我4块卖白菜」。如果没有规则,这交易根本没法做。交易所也是一样,成千上万的买卖单同时涌进来,总得有个先来后到、价高价低的说法。

我个人习惯把撮合引擎比作一个「自动红绿灯」。它不关心你是谁,也不关心你的单子有多大,它只认两条铁律:

  • 价格优先:出价高的买家优先成交,出价低的卖家优先成交
  • 时间优先:同样价格,先来的先成交

嗯,就这么简单。但简单背后,藏着不少坑。

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

先看价格优先。这个好理解——买方这边,谁出的价高谁先成交;卖方那边,谁要的价低谁先成交。你想想看,如果你是卖家,肯定愿意把货卖给愿意出更高价的人,对吧?

举个例子:

方向 价格 数量 时间
10.02 100 09:30:01
10.01 200 09:30:02
10.00 150 09:30:03

这时候来了一笔卖单,价格10.01,数量100股。谁会成交?答案是10.02那个买单先成交,因为他出价最高。哪怕他来得晚一点,只要价格好,他就优先。

核心要点:价格优先是「第一优先级」。只要价格有优势,时间再晚也能插队。

4.3 时间优先:价格一样,先来后到

那如果价格一样呢?这时候就看谁手快了。时间优先原则就是:谁先挂单,谁先成交。

我在项目中遇到过这样一个场景:某只股票突然拉升,买一价上堆了上千手的买单,价格都是10.00。这时候一个大卖单砸下来,先成交的是最早挂在那里的那笔买单,而不是最新的那笔。很多散户不理解,觉得「我明明也挂了10.00,为什么没成交?」——其实就是因为你来晚了。

避坑指南:我曾经帮一个客户排查过问题,他以为自己的限价单没成交是因为交易所「黑箱操作」。结果一查日志,发现他的单子比前面那笔晚了0.003秒。在量化交易里,毫秒级的差距就能决定生死。

4.4 撮合流程:一张图看懂

下面我用一张流程图来展示完整的撮合逻辑。这张图我画了很多遍,每次给团队新人培训都用它。

撮合引擎核心流程 新订单到达 判断买卖方向 是买单? 匹配卖单队列 匹配买单队列 价格优先:最高买价 vs 最低卖价 价格优先:最低卖价 vs 最高买价 时间优先:先到先得 时间优先:先到先得 成交 / 部分成交

这张图的核心逻辑其实就三步:先看方向,再比价格,最后比时间。每一步都过滤掉一批对手单,剩下的就是能成交的。

4.5 代码实现:一个极简撮合引擎

光说不练假把式。下面我写一个极简的撮合引擎,帮你理解这个过程。这个版本去掉了各种边界条件,只保留核心逻辑。

class Order:
    def __init__(self, order_id, side, price, quantity, timestamp):
        self.order_id = order_id
        self.side = side        # 'buy' 或 'sell'
        self.price = price
        self.quantity = quantity
        self.timestamp = timestamp

class MatchingEngine:
    def __init__(self):
        self.buy_orders = []    # 买单队列,按价格降序、时间升序
        self.sell_orders = []   # 卖单队列,按价格升序、时间升序
    
    def add_order(self, order):
        if order.side == 'buy':
            # 尝试匹配卖单
            while self.sell_orders and order.quantity > 0:
                best_sell = self.sell_orders[0]
                if order.price >= best_sell.price:
                    # 价格匹配成功
                    trade_qty = min(order.quantity, best_sell.quantity)
                    print(f"成交: {trade_qty}股 @ {best_sell.price}")
                    order.quantity -= trade_qty
                    best_sell.quantity -= trade_qty
                    if best_sell.quantity == 0:
                        self.sell_orders.pop(0)
                else:
                    break
            # 剩余部分入队列
            if order.quantity > 0:
                self.buy_orders.append(order)
                self.buy_orders.sort(key=lambda x: (-x.price, x.timestamp))
        else:
            # 卖单逻辑类似,略
            pass

注意:上面的代码只是一个教学示例。实际生产环境的撮合引擎要考虑锁竞争、内存屏障、GC停顿等问题。我曾经在一个高频场景里,因为用了Python的list做队列,导致撮合延迟从微秒级飙升到毫秒级——那叫一个惨。

4.6 实际应用中的几个坑

讲完了原理,说说我踩过的坑。这些经验都是用真金白银换来的。

  1. 价格相等时的处理:有些交易所对「价格相等」的定义有细微差别。比如A股是「价格优先、时间优先」,但期货市场可能略有不同。一定要看具体的交易规则。
  2. 冰山订单的挑战:大单往往隐藏真实数量。如果你只看到表面挂单就去撮合,可能会被「冰山」砸到。我见过一个策略因为没处理冰山订单,瞬间被吃掉了全部流动性。
  3. 撤单与改单的时序:撤单指令和成交指令谁先到?这涉及到「先撤后成」还是「先成后撤」的问题。不同交易所的处理方式不一样,一定要搞清楚。

小技巧:我个人习惯在测试环境里模拟各种极端情况——比如同时来1000笔撤单和1000笔新单,看看撮合引擎会不会乱掉。这种压力测试能帮你发现很多隐藏问题。

4.7 总结一下

价格优先、时间优先,这八个字看起来简单,但背后是整个市场公平交易的基石。做量化交易,你不需要自己写撮合引擎(除非你在交易所工作),但你一定要理解它的运作方式。否则,你的策略可能在不知不觉中就吃了亏。

嗯,今天就聊到这里。记住:在交易的世界里,价格是王道,时间是命脉。


专注资料整理