2. 金融市场微观结构基础

在Tick事件回放与撮合仿真中,真正决定“谁先成交、以什么价格成交”的,不是K线,也不是指标,而是订单簿与撮合规则。理解微观结构,是后续实现回放引擎、复现交易所行为、验证策略滑点的前提。本节围绕订单簿、买卖盘、限价单、市价单以及价格优先、时间优先原则展开,建立可落地的撮合认知模型。

一、核心原理:订单簿是市场的“实时供需账本”

订单簿(Order Book)是交易所维护的、按价格与时间组织的未成交订单集合。它分为买方(Bid)与卖方(Ask)两侧,每一侧都是按价格排序的队列。买盘最高价称为最优买价(Best Bid),卖盘最低价称为最优卖价(Best Ask),两者之差为买卖价差(Spread)。

订单簿的核心结构可以概括为:

  • 价格档位(Price Level):同一价格的订单聚合为一个档位,记录该价位的总数量与订单队列。
  • 队列顺序:同一价格内,订单按到达时间先后排列,先到者优先成交。
  • 深度(Depth):档位数量越多,代表该价位流动性越厚,价格冲击越小。

买卖盘并非静态快照,而是随每笔新增、撤单、成交事件动态变化。Tick级回放的本质,就是按时间顺序重放这些事件,并让本地订单簿与交易所状态保持一致。

订单簿结构与撮合方向示意 买方 Bid(买盘) 价格 数量 100.50 300 100.49 500 100.48 800 100.47 1200 最优买价 = 100.50 卖方 Ask(卖盘) 价格 数量 100.51 200 100.52 400 100.53 600 100.54 900 最优卖价 = 100.51 价差 0.01 买盘价格从高到低排列,卖盘价格从低到高排列;同价位按时间先后排队

二、限价单与市价单:两种基本订单类型

限价单(Limit Order)指定价格与数量,只有市场价格达到或优于该价格时才成交。它进入订单簿后成为“挂单”,为市场提供流动性。市价单(Market Order)只指定数量,不指定价格,要求立即以当前最优对手价成交,消耗订单簿流动性。

对比维度 限价单 市价单
是否指定价格 是 否
成交确定性 价格确定,成交不确定 成交确定,价格不确定
对订单簿影响 增加深度,提供流动性 消耗深度,提取流动性
典型风险 未成交、排队靠后 滑点、部分成交

在撮合仿真中,限价单需要插入对应价格档位的队列尾部;市价单则从对手方最优档位开始逐档吃单,直到数量耗尽或对手盘清空。

三、成交规则:价格优先、时间优先

绝大多数交易所采用“价格优先、时间优先”原则:

  1. 价格优先:买方出价越高越优先,卖方出价越低越优先。
  2. 时间优先:同价格下,先到达的订单先成交。

当新订单与对手方订单价格交叉时,立即触发撮合。例如,新买入限价单价格 ≥ 最优卖价,则以最优卖价成交;若数量大于该档位,则继续吃下一档,直到价格不再满足或数量耗尽。

撮合核心逻辑(伪代码):

function match(order):
    if order.side == BUY:
        while order.qty > 0 and asks not empty:
            best = asks.best()          # 最低卖价
            if order.type == LIMIT and order.price < best.price:
                break                   # 限价单价格不满足,停止
            trade_qty = min(order.qty, best.qty)
            execute(trade_qty, best.price)
            order.qty -= trade_qty
            best.qty -= trade_qty
            if best.qty == 0:
                asks.pop()
        if order.type == LIMIT and order.qty > 0:
            bids.insert(order)          # 剩余挂入买盘
    # 卖方向对称处理

这段逻辑是后续Tick回放引擎的核心。回放时,每一笔新增订单、撤单、成交事件都要按时间戳顺序驱动该状态机,才能复现真实盘口变化。

四、案例与场景:一笔市价买单如何吃掉卖盘

假设当前卖盘为:100.51×200、100.52×400、100.53×600。现在来了一笔市价买入 800 手:

  • 先吃 100.51 的 200 手,剩余 600 手;
  • 再吃 100.52 的 400 手,剩余 200 手;
  • 最后吃 100.53 的 200 手,剩余 0 手,成交结束。

成交均价约为 (200×100.51 + 400×100.52 + 200×100.53) / 800 ≈ 100.52。若该订单是限价 100.52 买入 800 手,则在 100.53 档位前停止,剩余 200 手挂入买盘 100.52 排队。

回放要点:真实Tick数据中,成交事件往往只给出价格与数量,不直接给出订单ID。仿真时需要用“价格优先、时间优先”反推队列消耗顺序,才能让本地订单簿与行情快照对齐。

五、常见误区与复盘

  • 误区一:把盘口一档当作全部流动性。市价单可能吃穿多档,滑点来自深度不足,而非单一价格。
  • 误区二:忽略时间优先,认为同价随机成交。同价位队列顺序决定成交先后,回放时必须维护队列。
  • 误区三:用K线收盘价模拟成交。Tick级撮合必须使用订单簿最优价与逐档消耗,否则回测结果失真。
  • 误区四:撤单事件处理滞后。撤单会改变队列与深度,若未按时间戳处理,盘口会与真实快照偏离。

复盘建议:每完成一段回放,将本地订单簿的最优买卖价与行情快照对比。若持续偏离,优先检查:事件时间戳排序、同价位队列插入位置、市价单吃单深度、撤单是否及时移除。

掌握订单簿、买卖盘、限价单、市价单与价格优先时间优先原则后,下一阶段即可将这些规则编码为撮合状态机,并在Tick事件流中逐笔驱动,实现可验证的撮合仿真。