第12章:订单管理模块:订单生命周期管理、订单取消与重发策略、订单路由逻辑、FIFO与Pro-Rata撮合规则适配

做市商系统里,订单管理模块就像人的心脏。它跳得好,整个策略就活着;它出问题,再好的定价模型也是白搭。我这些年踩过的坑,有一半都跟订单管理有关。今天咱们就把这块掰开揉碎了讲清楚。

12.1 订单生命周期管理

一个订单从出生到死亡,经历哪些状态?说白了就这几个阶段:

  • 创建(Created):订单刚生成,还没发出去
  • 已发送(Sent):发到交易所了,等确认
  • 已确认(Acknowledged):交易所收下了,挂单成功
  • 部分成交(Partially Filled):吃了一半,另一半还在
  • 完全成交(Filled):全吃完了,订单结束
  • 已取消(Cancelled):主动撤单
  • 已拒绝(Rejected):交易所不给过

嗯,这里要注意。很多新手只盯着"成交"和"未成交"两个状态。我建议你至少维护上面这7个状态。为什么?

我在项目中遇到过这么个事:某次行情剧烈波动,我们发了一堆限价单。结果交易所返回了部分成交,但我们的系统还傻傻地以为订单全活着,继续发新的。最后仓位直接爆了。从那以后,我强制团队必须把每个订单的状态机画清楚。

核心原则:订单状态机必须是确定性的。每个状态只能有唯一的入口和出口。别搞什么"既可以是A又可以是B"的模糊状态。

来看一个简单的状态机实现:

class OrderStateMachine:
    def __init__(self):
        self.state = 'CREATED'
        self.valid_transitions = {
            'CREATED': ['SENT'],
            'SENT': ['ACKNOWLEDGED', 'REJECTED'],
            'ACKNOWLEDGED': ['PARTIALLY_FILLED', 'FILLED', 'CANCELLED'],
            'PARTIALLY_FILLED': ['FILLED', 'CANCELLED'],
            'FILLED': [],
            'CANCELLED': [],
            'REJECTED': []
        }
    
    def transition(self, new_state):
        if new_state in self.valid_transitions[self.state]:
            self.state = new_state
            return True
        return False

12.2 订单取消与重发策略

做市商最怕什么?挂单挂在那里,行情跑了,你还傻等着。这时候就需要取消重发策略。

我个人习惯用这么几种策略:

策略名称 触发条件 适用场景
价格偏移取消 最新成交价偏离挂单价超过阈值 快速行情
超时取消 订单挂单超过设定时间(如500ms) 流动性差的市场
队列位置取消 订单在队列中排名靠后 FIFO撮合市场
批量取消 策略信号变化,需要整体调整 多腿策略

我曾经犯过一个低级错误:取消订单后立刻重发,结果交易所还没处理完取消,新订单就被拒绝了。后来我加了一个取消确认等待机制

class CancelReplaceManager:
    def __init__(self, exchange_client):
        self.client = exchange_client
        self.pending_cancels = {}
    
    async def cancel_and_replace(self, old_order_id, new_order):
        # 先发取消
        cancel_req = await self.client.cancel_order(old_order_id)
        self.pending_cancels[old_order_id] = {
            'new_order': new_order,
            'timestamp': time.time()
        }
        
        # 等待确认,超时则放弃
        while time.time() - self.pending_cancels[old_order_id]['timestamp'] < 0.1:
            status = await self.client.get_order_status(old_order_id)
            if status == 'CANCELLED':
                # 确认取消成功,再发新单
                return await self.client.place_order(new_order)
            await asyncio.sleep(0.001)
        
        # 超时了,记录告警
        logger.warning(f"取消订单 {old_order_id} 超时")
        return None

避坑指南:我曾经在某个交易所遇到取消请求返回成功,但订单实际还在挂单的情况。原因是交易所的取消确认是异步的。解决方案:不要相信一次返回,要轮询确认状态。

12.3 订单路由逻辑

如果你只在一个交易所做市,路由逻辑很简单。但现实是,我们往往要同时对接多个交易所。这时候路由就变得关键了。

订单路由的核心问题就一个:这个单子该发到哪去?

我总结了几种常见的路由策略:

  • 最优价格路由:哪个交易所价格好,发哪个。简单粗暴,但容易忽略流动性深度。
  • 流动性加权路由:根据各交易所的挂单深度,按比例分配订单。适合大单。
  • 延迟优先路由:哪个交易所延迟低,优先发。适合高频做市。
  • 智能路由:综合价格、深度、延迟、手续费等因素,动态决策。

你想想看,如果只按价格路由,你可能会遇到这种情况:A交易所价格好但只有1手深度,B交易所价格差一点但有100手深度。你把大单全发到A,结果只成交了一点点,剩下的全被市场吃掉了。亏不亏?

我个人比较喜欢用智能路由,但实现起来确实复杂。这里给一个简化版的权重计算:

def calculate_exchange_score(exchange_data):
    """
    计算交易所的综合评分
    exchange_data: {
        'price': 100.5,
        'depth': 10000,
        'latency': 0.005,
        'fee': 0.0001
    }
    """
    price_score = 1 / (1 + abs(exchange_data['price'] - best_price))
    depth_score = min(exchange_data['depth'] / target_size, 1.0)
    latency_score = 1 / (1 + exchange_data['latency'] * 100)
    fee_score = 1 / (1 + exchange_data['fee'] * 1000)
    
    # 权重可以动态调整
    weights = {'price': 0.4, 'depth': 0.3, 'latency': 0.2, 'fee': 0.1}
    
    return (price_score * weights['price'] + 
            depth_score * weights['depth'] + 
            latency_score * weights['latency'] + 
            fee_score * weights['fee'])

12.4 FIFO与Pro-Rata撮合规则适配

这是很多做市商容易忽略的点。不同交易所的撮合规则不一样,你的订单管理策略必须适配。

FIFO(先进先出):谁先挂单,谁先成交。说白了就是排队。在这种规则下,队列位置就是一切。

Pro-Rata(按比例分配):按挂单量比例分配成交。你挂得多,成交就多。在这种规则下,挂单量才是关键。

我画了一张图,帮你理解这两种规则的区别:

FIFO 撮合 单A 单B 单C 先到先成交 队列位置决定一切 Pro-Rata 撮合 单A(10手) 单B(5手) 单C(2手) 按比例分配成交 挂单量决定一切

那么,做市商该怎么适配?

实战建议

  • 在FIFO市场,你的订单管理要关注队列位置监控。如果发现订单排在后面,果断取消重发,往前挤。
  • 在Pro-Rata市场,你的订单管理要关注挂单量调整。如果发现成交比例太低,适当增加挂单量。
  • 混合市场(比如某些交易所同时用两种规则),那就更复杂了。我建议你根据历史数据,统计出哪种规则占主导,再针对性优化。

我曾经在某个FIFO市场做市,一开始没注意队列位置,结果挂单经常排到第100位以后,一天下来成交率不到5%。后来我加了一个队列位置监控,一旦发现排名掉出前10,立刻取消重发。成交率直接提升到40%。

适配代码示例:

class MatchingRuleAdapter:
    def __init__(self, exchange_type):
        self.exchange_type = exchange_type
        
    def should_cancel_and_replace(self, order):
        if self.exchange_type == 'FIFO':
            # FIFO市场:关注队列位置
            if order.queue_position > 10:
                return True
        elif self.exchange_type == 'PRO_RATA':
            # Pro-Rata市场:关注成交比例
            if order.fill_ratio < 0.3:
                return True
        return False
    
    def calculate_optimal_order_size(self, base_size, current_depth):
        if self.exchange_type == 'FIFO':
            # FIFO市场:小单更容易排到前面
            return min(base_size, current_depth * 0.01)
        elif self.exchange_type == 'PRO_RATA':
            # Pro-Rata市场:大单才能分到更多
            return max(base_size, current_depth * 0.05)
        return base_size

嗯,最后说一句。订单管理模块是做市系统的最后一道防线。定价模型可以错,信号可以延迟,但订单管理绝对不能出bug。我建议你在上线前,至少做三轮压力测试:

  1. 模拟极端行情,看订单取消重发是否正常
  2. 模拟网络延迟,看超时处理是否健壮
  3. 模拟交易所异常返回,看状态机是否兜得住

这三轮测试跑下来,你的订单管理模块基本就稳了。

交易系统化学习资料 微信Strategy888888