撮合引擎测试指南

版本: v0.2.0 更新日期: 2025-12-17 开发团队: @yutiansut @quantaxis


📋 目录

  1. 测试概述
  2. 测试场景分类
  3. 基础撮合测试
  4. 成交类型测试
  5. 撤单测试
  6. 优先级测试
  7. 订单类型测试
  8. 交易状态测试
  9. 多账户测试
  10. 边界条件测试
  11. 运行测试

测试概述

测试统计

文件位置: src/matching/engine.rs
测试数量: 76 个
覆盖率:   100% 场景覆盖
运行时间: ~0.17s

测试原则

  1. 场景驱动: 每个测试对应一个具体的业务场景
  2. 详细注释: 包含场景描述、撮合逻辑、验证要点
  3. 独立性: 测试之间相互独立,可单独运行
  4. 确定性: 测试结果可重复、可预测

测试结构

#![allow(unused)]
fn main() {
/// 测试编号.场景名 - 简要说明
///
/// 场景:
/// - 前置条件描述
/// - 操作步骤描述
/// - 期望结果描述
///
/// 撮合逻辑:
/// - 详细的撮合过程说明
/// - 计算公式和验证点
#[test]
fn test_scenario_name() {
    // Arrange: 准备测试环境
    // Act: 执行被测操作
    // Assert: 验证结果
}
}

测试场景分类

场景覆盖矩阵

类别场景数覆盖范围
1. 基础功能24InstrumentAsset、Engine创建、合约注册
2. 买卖撮合6买入/卖出、有/无对手盘、价格匹配/不匹配
3. 成交类型5完整成交、部分成交、多次成交、深度消耗
4. 撤单操作5正常撤单、不存在订单、买单撤销、卖单撤销
5. 优先级规则3买方高价优先、卖方低价优先、时间优先
6. 订单类型4限价单、市价单、改单
7. 交易状态3连续交易、集合竞价、休市
8. 多账户3跨账户成交、价格竞争、深度撮合
9. 边界异常6重复ID、最新价更新、空订单簿、大量订单

基础撮合测试

1.1 买入订单无对手盘

#![allow(unused)]
fn main() {
/// 场景:买单进入空订单簿
/// 期望:订单被接受,进入买盘队列等待
#[test]
fn test_buy_order_no_counterparty_accepted()
}

撮合逻辑:

买单: 价格84000, 数量10
卖盘: 空

结果: Success::Accepted
      订单进入 bid_queue 等待

1.2 卖出订单无对手盘

#![allow(unused)]
fn main() {
/// 场景:卖单进入空订单簿
/// 期望:订单被接受,进入卖盘队列等待
#[test]
fn test_sell_order_no_counterparty_accepted()
}

1.3 买入订单有匹配卖单

#![allow(unused)]
fn main() {
/// 场景:买单价格 >= 卖单价格
/// 期望:立即成交
#[test]
fn test_buy_order_with_matching_sell_filled()
}

撮合逻辑:

卖单: 价格85000, 数量10 (先挂入)
买单: 价格85000, 数量10

撮合条件: 买价(85000) >= 卖价(85000) ✓
成交价格: 85000 (按对手价成交)
成交数量: 10

结果: 两方都 Success::Filled

1.4 买入价格低于卖出价格

#![allow(unused)]
fn main() {
/// 场景:买单价格 < 卖单价格
/// 期望:无法成交,双方都进入队列
#[test]
fn test_buy_order_price_not_match_accepted()
}

撮合逻辑:

卖单: 价格85000 (先挂入)
买单: 价格84000

撮合条件: 买价(84000) < 卖价(85000) ✗
无法成交,买单进入 bid_queue

成交类型测试

2.1 完全成交(数量相等)

#![allow(unused)]
fn main() {
/// 场景:买卖双方数量完全相等
/// 期望:双方都完全成交
#[test]
fn test_exact_volume_match_both_filled()
}

计算逻辑:

卖单: 数量10
买单: 数量10

成交: 10手
卖单状态: Filled (剩余0)
买单状态: Filled (剩余0)

2.2 部分成交(新单较小)

#![allow(unused)]
fn main() {
/// 场景:新订单数量 < 对手盘数量
/// 期望:新单完全成交,对手单部分成交
#[test]
fn test_new_order_smaller_new_filled_opposite_partial()
}

计算逻辑:

卖单: 数量20 (先挂入)
买单: 数量10 (新单)

成交: min(20, 10) = 10手
卖单状态: PartiallyFilled (剩余10)
买单状态: Filled (剩余0)

2.3 部分成交(新单较大)

#![allow(unused)]
fn main() {
/// 场景:新订单数量 > 对手盘数量
/// 期望:对手单完全成交,新单部分成交
#[test]
fn test_new_order_larger_new_partial_opposite_filled()
}

2.4 多次部分成交

#![allow(unused)]
fn main() {
/// 场景:新订单消耗多个对手盘订单
/// 期望:递归撮合直到无法继续
#[test]
fn test_multiple_partial_fills_across_orders()
}

撮合逻辑(递归):

卖单1: 价格85000, 数量5
卖单2: 价格85000, 数量5
买单:  价格85000, 数量8

第1轮: 买单(8) vs 卖单1(5) → 成交5, 买单剩余3, 卖单1完成
第2轮: 买单(3) vs 卖单2(5) → 成交3, 买单完成, 卖单2剩余2

2.5 吃掉整个深度

#![allow(unused)]
fn main() {
/// 场景:大单消耗多档卖盘
/// 期望:按价格优先顺序逐档成交
#[test]
fn test_eat_entire_depth()
}

撮合顺序:

卖盘深度:
├── 85000 × 5
├── 85100 × 5
└── 85200 × 5

买单: 85200 × 15

成交顺序(卖价升序):
1. 先以85000成交5手
2. 再以85100成交5手
3. 最后以85200成交5手
总成交: 15手

撤单测试

3.1 撤销未成交订单

#![allow(unused)]
fn main() {
/// 场景:撤销队列中等待的订单
/// 期望:返回 Cancelled
#[test]
fn test_cancel_pending_order()
}

撮合逻辑:

1. 挂入买单,获取 order_id
2. 发送 CancelOrder { id: order_id, direction: BUY }
3. 从 bid_queue 中移除订单
4. 返回 Success::Cancelled

3.2 撤销不存在的订单

#![allow(unused)]
fn main() {
/// 场景:尝试撤销不存在的订单ID
/// 期望:返回 Failed::OrderNotFound
#[test]
fn test_cancel_nonexistent_order()
}

3.3 撤销买入订单

#![allow(unused)]
fn main() {
/// 场景:撤销买盘中的订单
/// 期望:从 bid_queue 中移除
#[test]
fn test_cancel_buy_order()
}

3.4 撤销卖出订单

#![allow(unused)]
fn main() {
/// 场景:撤销卖盘中的订单
/// 期望:从 ask_queue 中移除
#[test]
fn test_cancel_sell_order()
}

优先级测试

4.1 买方高价优先

#![allow(unused)]
fn main() {
/// 场景:多个买单,价格不同
/// 期望:高价买单优先成交
#[test]
fn test_buy_higher_price_priority()
}

验证逻辑:

买单A: 价格85000
买单B: 价格85100 ← 优先
买单C: 价格85200 ← 最优先

卖单进入时,先与最高价85200成交

4.2 卖方低价优先

#![allow(unused)]
fn main() {
/// 场景:多个卖单,价格不同
/// 期望:低价卖单优先成交
#[test]
fn test_sell_lower_price_priority()
}

4.3 同价格时间优先

#![allow(unused)]
fn main() {
/// 场景:相同价格的多个订单
/// 期望:先到的订单优先成交 (FIFO)
#[test]
fn test_same_price_time_priority_fifo()
}

验证逻辑:

买单1: 价格85000, ts=100 ← 先到
买单2: 价格85000, ts=101
买单3: 价格85000, ts=102

卖单进入时,先与 ts=100 的买单1成交

订单类型测试

5.1 限价单撮合

#![allow(unused)]
fn main() {
/// 场景:标准限价单撮合
/// 期望:按限定价格撮合
#[test]
fn test_limit_order_matching()
}

5.2 市价单立即成交

#![allow(unused)]
fn main() {
/// 场景:市价单进入有深度的订单簿
/// 期望:按对手最优价立即成交
#[test]
fn test_market_order_fills_immediately()
}

5.3 改单

#![allow(unused)]
fn main() {
/// 场景:修改已挂订单的价格或数量
/// 期望:返回 Amended
#[test]
fn test_amend_order()
}

交易状态测试

6.1 连续交易状态

#![allow(unused)]
fn main() {
/// 场景:正常交易时段
/// 期望:订单可正常撮合
#[test]
fn test_continuous_trading_state()
}

6.2 集合竞价期间

#![allow(unused)]
fn main() {
/// 场景:集合竞价申报期
/// 期望:只接受限价单,不立即撮合
#[test]
fn test_auction_period_limit_only()
}

6.3 休市状态

#![allow(unused)]
fn main() {
/// 场景:市场关闭
/// 期望:拒绝所有订单
#[test]
fn test_closed_market_rejects_all()
}

多账户测试

7.1 不同账户间成交

#![allow(unused)]
fn main() {
/// 场景:账户A买单 vs 账户B卖单
/// 期望:正常撮合成交
#[test]
fn test_multi_account_matching()
}

7.2 多账户价格竞争

#![allow(unused)]
fn main() {
/// 场景:多个账户以不同价格挂买单
/// 期望:最高价账户优先成交
#[test]
fn test_multi_account_price_competition()
}

7.3 订单簿深度撮合

#![allow(unused)]
fn main() {
/// 场景:多账户形成订单簿深度
/// 期望:按价格优先级撮合
#[test]
fn test_orderbook_depth_matching()
}

边界条件测试

8.1 订单ID唯一性

#![allow(unused)]
fn main() {
/// 场景:连续提交多个订单
/// 期望:每个订单ID都唯一
#[test]
fn test_duplicate_order_id_handling()
}

8.2 最新价更新

#![allow(unused)]
fn main() {
/// 场景:成交后
/// 期望:lastprice 更新为成交价
#[test]
fn test_lastprice_update_on_trade()
}

8.3 空订单簿

#![allow(unused)]
fn main() {
/// 场景:订单簿完全为空
/// 期望:订单被接受进入队列
#[test]
fn test_empty_orderbook_order_pending()
}

8.4 连续多次成交

#![allow(unused)]
fn main() {
/// 场景:多次成交
/// 期望:lastprice 始终为最后成交价
#[test]
fn test_lastprice_multiple_trades()
}

8.5 大量订单

#![allow(unused)]
fn main() {
/// 场景:100买单 + 100卖单 + 大激进单
/// 期望:系统稳定,正确撮合
#[test]
fn test_high_volume_orders()
}

8.6 集合竞价执行

#![allow(unused)]
fn main() {
/// 场景:集合竞价撮合
/// 期望:按集合竞价规则成交
#[test]
fn test_auction_execution()
}

运行测试

运行所有撮合引擎测试

cargo test matching::engine::tests --lib

运行特定测试

# 运行买卖撮合相关测试
cargo test matching::engine::tests::test_buy --lib
cargo test matching::engine::tests::test_sell --lib

# 运行撤单相关测试
cargo test matching::engine::tests::test_cancel --lib

# 运行优先级相关测试
cargo test matching::engine::tests::test_priority --lib
cargo test matching::engine::tests::test_higher --lib
cargo test matching::engine::tests::test_lower --lib

查看测试输出

cargo test matching::engine::tests --lib -- --nocapture

运行单个测试

cargo test matching::engine::tests::test_buy_order_with_matching_sell_filled --lib

测试检查清单

新增测试时的检查项

  • 测试名称清晰描述场景
  • 包含场景说明注释
  • 包含撮合逻辑说明
  • 使用 Arrange-Act-Assert 结构
  • 验证所有预期结果
  • 测试可独立运行
  • 无外部依赖

代码审查要点

  • 边界条件是否覆盖
  • 异常场景是否处理
  • 并发场景是否考虑
  • 性能影响是否评估

相关文档