TL;DR
- 组合策略单(Strategy Order)最大的敌人是腿部风险(Legging Risk):如果先成交腿 A、腿 B 却因为盘口跳价/流动性消失而失败,用户会瞬间暴露在“裸腿头寸”的巨大风险里。
- 撮合引擎要提供“事务语义”:多腿原子性(要么全成、要么全不成)+ 净价差约束(Implied Net Price 必须满足限价)+ 公平/确定性(同一输入流得到同一输出)。
- 实现上通常有两条路:SOB vs SOB(策略簿对策略簿撮合,简单但流动性薄)与 SOB vs CLOB(策略簿拆腿去两条腿的独立订单簿找流动性,复杂但更贴近真实市场)。
- 工程关键不是“写撮合逻辑”,而是把多腿操作做成可回滚/可验证的状态变更序列:延迟对外发布成交回报、暂存状态,失败则恢复;并用 Sequencer/输入日志/快照恢复把一致性闭环。
1. 先把问题讲清楚:为什么“组合下单”比你想的难
以牛市看涨价差(Bull Call Spread)为例:买入低行权价 Call(腿 A)+ 卖出高行权价 Call(腿 B)。交易者关心的是净价差(例如最多付 1.50 美元),而不是两条腿各自成交价。
如果系统只把它拆成两笔普通订单顺序执行,就会出现经典灾难:腿 A 成了、腿 B 失败——策略没了,只剩裸腿风险。
- 原子性:两条腿必须“同时”成交或同时失败。
- 价格约束:腿的组合价格必须满足策略单的限价(净价差/净信用)。
- 流动性来源:对手方可能是另一个反向策略单,也可能来自两条腿各自的订单簿(更常见)。
- 性能/公平:在微秒级系统里做到这些,还要守住价格-时间优先与确定性。
2. 两种撮合模式:SOB vs SOB / SOB vs CLOB
模式 A:SOB vs SOB(策略簿内部撮合)
对每类策略(垂直价差、跨式、蝶式…)维护一个策略订单簿(SOB),策略单之间直接按“净价”撮合。
好处是逻辑简单、天然原子;坏处是现实里策略簿流动性往往不足。
模式 B:SOB vs CLOB(拆腿去腿市场找流动性)
引擎从两条腿各自的 CLOB 探测最优价(例如腿 A 看 Ask、腿 B 看 Bid),计算隐含净价(Implied Spread),满足限价则尝试执行。
难点在于:你必须把“探测 → 锁定/预留 → 执行 → 发布回报”做成一个原子序列,否则就会在中途被行情/撤单打断。
3. 关键要点/坑:你需要一个“内存事务”的工程等价物
- 延迟发布(defer publish):撮合过程中不要立刻对外发送成交回报;先在内存里完成所有腿的状态变更,全部成功后再一次性发布。
- 状态暂存与恢复:失败路径必须能把订单簿与订单对象恢复到撮合前的状态(否则就是“孤儿腿”事故)。这通常意味着在一次撮合函数调用内完成所有变更,失败就回滚本地修改。
- 同时间戳/同事件序列的确定性:策略撮合和普通撮合共存时,要定义清晰的处理顺序;否则同一输入在不同机器/不同运行中结果可能漂移。
- 别把数据库 2PC 硬搬进来:经典分布式事务的网络/锁开销对低延迟撮合是灾难。更常见的做法是把“强一致的变更范围”限制在单进程/单撮合分片内,用确定性状态机 + 输入日志解决恢复一致性。
4. 架构层面的落地路线(从能跑到跑得稳)
- MVP:只做 SOB vs SOB,先跑通策略单生命周期与风控/清算/行情发布链路。
- 半自动拆腿服务:用独立 Legging Engine 订阅策略簿深度,发现机会后同时发两笔 IOC 去腿市场,复杂性与风险先隔离在边上。
- 内置高性能拆腿:把拆腿逻辑下沉到撮合核心,配合状态暂存/延迟发布实现原子;再做 CPU/缓存/内存池等性能工程。
- 更高阶的合成流动性:用桥接合约/图算法把 A-vs-C 与 C-vs-B 合成 A-vs-B(技术与风险模型都很重,属于“卷王阶段”)。
5. 适用场景:什么时候你真的需要“策略撮合”
- 你做的是期权/衍生品交易,需要对用户提供“组合下单”的执行保证;
- 你希望把“执行风险”从客户端/交易员身上,收敛回交易所/平台的系统能力;
- 你已经有成熟的定序、回放、快照恢复体系,能为复杂撮合提供确定性与可审计性。