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 订单生命周期状态机
下图展示了订单从创建到终态的状态转换路径,包含限价单、市价单、撤单及成交事件的完整交互:
4.7 事件驱动流程示例
以下是一个完整的订单-成交事件驱动流程,展示限价单从创建到完全成交的步骤:
- 策略发出限价买入指令 → 生成
LimitOrderEvent,状态PENDING。 - 行情引擎推送新 tick → 卖一价 10.02,限价单价格 10.00,条件不满足,订单保持
PENDING。 - 下一 tick 卖一价降至 9.98 → 条件满足,撮合引擎执行。
- 盘口卖一量仅 200 股,而订单数量为 500 股 → 生成
FillEvent(部分成交,200 股 @ 9.98),订单状态变为PARTIAL_FILLED。 - 后续 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 | 是(自身即为成交事件) |