4. 订单与成交事件模型:限价单、市价单、撤单、部分成交、完全成交的事件定义

在事件驱动回测框架中,订单与成交是连接策略逻辑与市场模拟的核心纽带。本章明确定义五种基本事件类型:限价单、市价单、撤单、部分成交、完全成交,并给出它们在回测引擎中的数据结构、状态转换与触发条件。

4.1 订单事件的基本结构

所有订单事件共享一个基础数据模型,包含以下关键字段:

字段 类型 说明
order_id string 全局唯一订单标识
symbol string 交易标的代码
direction enum BUY / SELL
order_type enum LIMIT / MARKET
quantity int 原始委托数量
price float 限价单指定价格;市价单为 None
status enum PENDING / PARTIAL_FILLED / FILLED / CANCELLED / REJECTED
timestamp datetime 订单创建时间
关键设计原则: 订单事件一旦生成即不可变(immutable),状态变更通过生成新的成交事件或撤单事件来驱动,避免直接修改原始订单对象。

4.2 限价单事件(Limit Order Event)

限价单指定了明确的委托价格,只有当市场行情达到或优于该价格时才有可能成交。在回测引擎中,限价单的触发逻辑如下:

  • 买入限价单: 当最新卖一价(ask price) ≤ 限价单价格时,触发撮合。
  • 卖出限价单: 当最新买一价(bid price) ≥ 限价单价格时,触发撮合。
  • 未触发前: 订单状态保持为 PENDING,等待后续行情数据。
# 限价单事件示例(Python 数据类)
@dataclass
class LimitOrderEvent:
    order_id: str
    symbol: str
    direction: str  # 'BUY' or 'SELL'
    quantity: int
    price: float
    status: str = 'PENDING'
    timestamp: datetime = None

    def is_triggered(self, bid: float, ask: float) -> bool:
        if self.direction == 'BUY':
            return ask <= self.price
        else:  # SELL
            return bid >= self.price

4.3 市价单事件(Market Order Event)

市价单不指定价格,以当前市场最优价格立即成交。在回测中,市价单的成交价格由当前盘口深度决定:

  • 买入市价单: 按卖一价、卖二价……依次吃单,直到数量满足。
  • 卖出市价单: 按买一价、买二价……依次吃单,直到数量满足。
  • 滑点模型: 可配置固定滑点(如 0.01%)或基于盘口深度的动态滑点。
回测建议: 市价单在实盘中存在成交价格不确定性,回测时应至少记录成交均价和滑点成本,避免过度理想化。
# 市价单事件示例
@dataclass
class MarketOrderEvent:
    order_id: str
    symbol: str
    direction: str
    quantity: int
    status: str = 'PENDING'
    timestamp: datetime = None

    def execute(self, order_book: OrderBook) -> FillResult:
        # 根据盘口深度计算成交价格与数量
        fills = []
        remaining = self.quantity
        if self.direction == 'BUY':
            for level in order_book.asks:
                if remaining <= 0:
                    break
                trade_qty = min(remaining, level.quantity)
                fills.append(Trade(price=level.price, quantity=trade_qty))
                remaining -= trade_qty
        # ... 类似处理 SELL
        return FillResult(fills=fills, unfilled=remaining)

4.4 撤单事件(Cancel Order Event)

撤单事件用于主动撤销尚未完全成交的订单。撤单事件的处理规则:

  • 可撤单状态: 仅 PENDING 或 PARTIAL_FILLED 的订单允许撤销。
  • 已完全成交订单: 撤销请求被拒绝(REJECTED)。
  • 部分成交后撤单: 已成交部分不可撤销,未成交部分被取消。
订单当前状态 撤单结果 新状态
PENDING 全部撤销 CANCELLED
PARTIAL_FILLED 未成交部分撤销 CANCELLED(剩余部分)
FILLED 拒绝撤单 FILLED(不变)
CANCELLED 重复撤单,拒绝 CANCELLED(不变)

4.5 成交事件:部分成交与完全成交

成交事件(Fill Event)是订单撮合的结果,分为部分成交和完全成交两种子类型。每次撮合产生一个或多个成交事件,并更新对应订单的已成交数量。

4.5.1 部分成交(Partial Fill)

  • 定义: 订单的委托数量在一次撮合中只成交了一部分。
  • 触发条件: 盘口可用流动性不足,或限价单价格只匹配到部分对手单。
  • 后续处理: 订单状态变为 PARTIAL_FILLED,剩余数量继续等待后续行情。

4.5.2 完全成交(Full Fill)

  • 定义: 订单的委托数量全部被成交。
  • 触发条件: 盘口流动性充足,且价格条件满足。
  • 后续处理: 订单状态变为 FILLED,从活跃订单队列中移除。
# 成交事件数据模型
@dataclass
class FillEvent:
    fill_id: str
    order_id: str
    symbol: str
    direction: str
    fill_price: float
    fill_quantity: int
    fill_time: datetime
    is_partial: bool  # True 表示部分成交,False 表示完全成交

    @property
    def turnover(self) -> float:
        return self.fill_price * self.fill_quantity

4.6 订单生命周期状态机

下图展示了订单从创建到终态的状态转换路径,包含限价单、市价单、撤单及成交事件的完整交互:

订单生命周期状态机 PENDING(待成交) CANCELLED(已撤销) PARTIAL_FILLED(部分成交) FILLED(完全成交) REJECTED(已拒绝) 部分成交 完全成交 撤单 风控拒绝 剩余成交 撤单 说明:市价单创建后直接进入撮合流程,若流动性不足则可能变为 PARTIAL_FILLED 或 REJECTED。

4.7 事件驱动流程示例

以下是一个完整的订单-成交事件驱动流程,展示限价单从创建到完全成交的步骤:

  1. 策略发出限价买入指令 → 生成 LimitOrderEvent,状态 PENDING。
  2. 行情引擎推送新 tick → 卖一价 10.02,限价单价格 10.00,条件不满足,订单保持 PENDING。
  3. 下一 tick 卖一价降至 9.98 → 条件满足,撮合引擎执行。
  4. 盘口卖一量仅 200 股,而订单数量为 500 股 → 生成 FillEvent(部分成交,200 股 @ 9.98),订单状态变为 PARTIAL_FILLED。
  5. 后续 tick 卖一价继续为 9.98,剩余 300 股成交 → 生成第二个 FillEvent(完全成交,300 股 @ 9.98),订单状态变为 FILLED。
常见陷阱: 部分成交后,剩余订单的限价价格不变,但市场行情可能反向移动导致剩余部分无法成交。策略应监控订单状态,必要时主动撤单并重新发单。

4.8 事件定义总结表

事件类型 触发条件 状态变更 是否生成成交事件
限价单 行情价格达到或优于限价 PENDING → PARTIAL_FILLED / FILLED 是
市价单 立即执行(按盘口深度) PENDING → FILLED / PARTIAL_FILLED 是
撤单 策略主动调用 PENDING/PARTIAL_FILLED → CANCELLED 否
部分成交 盘口流动性不足或价格部分匹配 PENDING → PARTIAL_FILLED 是(自身即为成交事件)
完全成交 订单全部数量被撮合 PENDING/PARTIAL_FILLED → FILLED 是(自身即为成交事件)