Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ch04 Pool、Book 与撮合源码精读

本章目标

  • 读懂 Pool 下单入口、Book 撮合循环、Order maker 状态、Fill 成交结果和 OrderInfo taker 生命周期。
  • 能解释 bid、ask、price level、queue priority 如何编码到 order_idBigVector<Order> 中。
  • 能推演限价单、市价单、IOC、FOK、post-only、自成交策略、取消和修改订单的状态变化。
  • 能把链上事件和源码中的状态字段对应起来,服务前端订单查询和 indexer 落库。

本章学习阶梯

  • L2 先用三档订单簿手工推演 bid、ask、price level 和 fill。
  • L3 再读订单 ID、撮合循环、剩余挂单和事件生成。
  • L4 能解释 post-only、IOC、FOK、自成交策略的失败边界。
  • L5 能把撮合结果还原成前端订单状态和 Indexer 数据。

关键定义卡片

撮合章先分清 OrderOrderInfoFill

public struct Order has drop, store {
    balance_manager_id: ID,
    order_id: u128,
    client_order_id: u64,
    quantity: u64,
    filled_quantity: u64,
    fee_is_deep: bool,
    order_deep_price: OrderDeepPrice,
    epoch: u64,
    status: u8,
    expire_timestamp: u64,
}

Order 是可以留在订单簿里的 maker 状态。它有 filled_quantitystatusexpire_timestamp,所以取消、过期、部分成交都能从这里更新。

public struct OrderInfo has copy, drop, store {
    pool_id: ID,
    order_id: u128,
    balance_manager_id: ID,
    client_order_id: u64,
    trader: address,
    order_type: u8,
    self_matching_option: u8,
    price: u64,
    is_bid: bool,
    original_quantity: u64,
    executed_quantity: u64,
    cumulative_quote_quantity: u64,
    fills: vector<Fill>,
    status: u8,
    market_order: bool,
    order_inserted: bool,
    timestamp: u64,
}

OrderInfo 是一次新订单的生命周期结果。完全吃单的市价单可能只返回 OrderInfo,不会插入簿成为 Order。这也是前端和 Indexer 容易写错的地方。

public struct Fill has copy, drop, store {
    maker_order_id: u128,
    execution_price: u64,
    balance_manager_id: ID,
    base_quantity: u64,
    quote_quantity: u64,
    taker_is_bid: bool,
    taker_fee: u64,
    maker_fee: u64,
}

Fill 是一笔 taker 与一个 maker 的成交切片,不是一整笔订单。一个 taker 订单可能产生多个 Fill

源码地图

小节目录

本章代码

  • code/s01-orderbook-simulator/:用 TypeScript 实现最小订单簿模拟器。
  • code/s02-match-trace/:输入订单列表,输出逐步撮合日志。
  • code/s03-order-query/:查询链上订单并与本地模拟排序规则对比。

Move 高阶穿插点

  • 撮合源码要按“输入校验、订单构造、撮合循环、剩余处理、事件输出、资金结算”六段读。
  • 订单簿里的整数不要急着换成小数显示;先保留链上原始单位,最后一层 UI 再格式化。
  • OrderInfoOrder 要分开理解:前者描述本次交易结果,后者描述可留在簿上的状态。

常见错误

  • 错误:把市价单当成没有价格。源码中市价买单使用 max price,市价卖单使用 min price,并强制 IOC。
  • 错误:用 JavaScript number 保存 u128 order id。必须使用字符串或 BigInt。
  • 错误:认为部分成交普通限价单的状态一定是 live。OrderInfo 可是 partially_filled,同时剩余部分以 Order 进入 Book。
  • 错误:取消订单只删 Book。实际还要更新 account open orders、计算 refund、通过 Vault 结算。
  • 错误:只监听 OrderFilled。订单生命周期还需要 OrderInfoOrderPlacedOrderFullyFilledOrderCanceledOrderExpired

本章检查清单

  • 能解释 encoded order id 的 bit 布局。
  • 能说明 bid/ask 分别从哪一侧、哪个价格方向开始撮合。
  • 能写出限价单进入 Book 的完整函数链。
  • 能解释 Fill 中 expired/completed 的不同含义。
  • 能推演 IOC、FOK、post-only 和自成交策略的行为。
  • 能说明取消订单如何退还锁定资产。

进阶练习

  • 练习 1:用 BigInt 写一个 decodeOrderId,输入链上 order id 后输出 side、price、sequence。
  • 练习 2:构造一个 maker 过期场景,解释为什么 Fill.expired = true 但它仍会参与移除订单和释放锁定资产。
  • 练习 3:设计一个包含 4 个 maker 的订单簿,分别推演普通限价、IOC 和 FOK taker 的最终状态。