第3章:订单生命周期管理

订单生命周期,说白了就是一笔订单从出生到消亡的全过程。我做了这么多年量化系统,见过太多因为状态机没设计好导致的惨案——资金对不上、成交记录丢失、甚至整个系统崩溃。今天咱们就来聊聊这个核心话题。

3.1 订单状态机:订单的“人生轨迹”

每个订单都有它的“人生”。从提交到交易所开始,它就会经历一系列状态变化。我个人习惯把状态机画在纸上,再动手写代码。你想想看,如果连状态图都画不清楚,代码怎么可能写对?

核心状态定义:

  • New:订单刚提交,还没成交
  • PartiallyFilled:部分成交了,还剩一些没成交
  • Filled:全部成交,订单使命完成
  • Cancelled:被用户主动撤销
  • Rejected:被交易所驳回,比如价格不对、资金不足

这里有个坑——RejectedCancelled 是两码事。Rejected 是交易所不认这个订单,Cancelled 是用户自己不想玩了。我曾经见过一个新手把这两个状态混在一起处理,结果撤单失败还以为是系统bug,排查了整整两天。

订单状态机流转图 New PartiallyFilled Filled Cancelled Rejected 部分成交 全部成交 剩余成交 继续部分成交 用户撤单 撤单 风控/参数错误 图例 正常流转 异常流转 循环状态

避坑指南:我曾经在状态机里漏掉了 PartiallyFilled 到 Cancelled 的路径。结果用户撤单时,系统直接报错。嗯,从那以后我写状态机都会画一张完整的图,逐个路径检查。

3.2 订单类型:不同的“武器”

订单类型就是你的交易武器。选对了,事半功倍;选错了,可能血本无归。我建议每个交易员都像熟悉自己的手指一样熟悉这些订单类型。

3.2.1 限价单(Limit Order)

限价单是最基础的订单类型。你指定一个价格,只有市场价达到这个价格时才会成交。说白了,就是“我只愿意在这个价格买/卖”。

限价单的特点:

  • 价格可控:你说了算
  • 成交不确定:可能等很久,也可能永远不成交
  • 适合做市商:挂单赚差价

3.2.2 市价单(Market Order)

市价单就是“不管什么价格,立刻成交”。我见过有人用市价单在流动性差的品种上吃大亏——滑点能让你怀疑人生。

注意:市价单在极端行情下可能成交在非常差的价格。我曾经在2015年股灾时看到有人用市价单卖出,结果成交价比预期低了5%。所以,流动性差的时候慎用市价单。

3.2.3 冰山订单(Iceberg Order)

冰山订单是个好东西。它把大单拆成小单,只露出“冰山一角”。你想想看,如果你直接挂一个100万股的买单,市场会怎么反应?价格肯定瞬间被拉高。

// 冰山订单示例
class IcebergOrder {
    int totalQuantity;    // 总数量:100000股
    int displayQuantity;  // 显示数量:5000股
    int filledQuantity;   // 已成交数量
    
    void onFill(int fillQty) {
        filledQuantity += fillQty;
        if (filledQuantity % displayQuantity == 0) {
            // 露出下一层冰山
            placeNewOrder();
        }
    }
}

实战经验:我个人习惯把冰山订单的显示量设为总量的5%-10%。太小了容易被交易所风控拦截,太大了又起不到隐藏效果。这个比例是我踩过坑之后总结出来的。

3.2.4 止损单(Stop Order)

止损单是用来控制风险的。当价格达到你设定的止损价时,它会变成市价单或限价单。说白了,就是“亏到一定程度我就跑”。

订单类型 触发条件 成交方式 适用场景
限价单 价格达到指定价 限价成交 做市、低买高卖
市价单 立即 市价成交 快速建仓/平仓
冰山订单 价格达到指定价 分批限价成交 大单交易
止损单 价格触发止损价 转为市价/限价 风险控制

3.3 状态机与订单类型的结合

不同类型的订单,状态机的流转路径其实是一样的。区别在于触发条件和成交逻辑。比如止损单在触发前是“休眠”状态,触发后才进入正常的 New 状态。

核心原则:状态机是骨架,订单类型是血肉。骨架不变,血肉可以灵活替换。我建议把状态机做成通用模块,订单类型做成插件。这样加新订单类型时,只需要写插件,不用动核心逻辑。

嗯,这里要注意一点:冰山订单的状态机里,PartiallyFilled 状态会反复出现。每次露出新的一层,其实都是一个新的子订单在流转。我个人习惯用“父订单-子订单”的模式来管理,父订单记录总量,子订单负责实际成交。

曾经踩过的坑:我在做冰山订单时,一开始把子订单的状态直接映射到父订单上。结果父订单显示 PartiallyFilled,但子订单已经 Filled 了,数据完全对不上。后来改成父订单只做统计,子订单独立管理状态,问题才解决。

最后说一句:订单生命周期管理是量化系统的地基。地基不稳,上面盖再漂亮的房子也是白搭。我建议你在写代码之前,先把状态图画清楚,把订单类型的逻辑理明白。磨刀不误砍柴工,这句话在量化交易里尤其适用。

蓝海数据掘金营,专注资料整理