一、订单类型:撮合引擎的“积木块”
做量化交易这些年,我接触过形形色色的订单类型。说白了,订单就是交易员发给市场的“指令”。你告诉系统:我想怎么买、怎么卖。不同的订单类型,对应着不同的交易策略和风险偏好。
我个人习惯把订单类型分成两大类:基础订单和高级订单。基础订单是限价单和市价单,高级订单则是冰山订单、止损单这些“变形金刚”。
1.1 限价单(Limit Order)—— 最老实的订单
限价单,顾名思义,就是“限定价格”的订单。你告诉系统:我只愿意在某个价格或更好的价格成交。比如你想买比特币,当前市场价是 50000 USDT,但你只想在 49000 买。那你就挂一个限价买单,价格填 49000。
限价单有两个核心特点:
- 价格可控:你说了算,市场波动再大,也不会以更差的价格成交。
- 成交不确定:价格没到你的限价,订单就一直挂着,可能永远成交不了。
我在项目中遇到过一个问题:有人挂了一个超低价的限价买单,结果市场突然暴跌,瞬间成交了。他本来想捡便宜,结果接住了飞刀。嗯,这里要注意:限价单不是“保险单”,它只是控制价格上限或下限。
1.2 市价单(Market Order)—— 最急切的订单
市价单就一个字:快。你不管价格是多少,只要立即成交就行。系统会以当前市场上最好的价格帮你吃掉对手盘。
市价单的特点:
- 成交确定:只要市场上有对手盘,100% 成交。
- 价格不确定:如果市场流动性差,你可能吃到很差的价位。
举个例子:你想买 100 个 ETH,但卖一价只有 10 个 ETH。市价单会先吃掉卖一的 10 个,然后继续吃卖二的、卖三的……直到买够 100 个。最终成交均价可能比你预期的贵不少。
1.3 冰山订单(Iceberg Order)—— 隐藏实力的订单
冰山订单,说白了就是“大单拆小单”。你明明想买 10000 个 BTC,但不想让市场知道你的真实意图。于是你只暴露一小部分,比如 100 个,剩下的 9900 个藏在“水下”。
冰山订单的参数:
- 总数量:你真正想交易的数量。
- 显示数量:每次暴露在订单簿上的数量。
- 价格:限价价格。
系统会这样工作:先挂出 100 个限价单,成交后,再自动挂出新的 100 个,直到总数量全部成交。
1.4 止损单(Stop Order)—— 带“保险丝”的订单
止损单,也叫“触发单”。它平时不干活,像个哨兵一样盯着市场价格。一旦市场价格触及你设定的“触发价”,止损单就变成市价单或限价单,立即执行。
止损单有两种常见变体:
- 市价止损单:触发后变成市价单,保证成交,价格不确定。
- 限价止损单:触发后变成限价单,价格可控,但可能成交不了。
为什么会这样?因为止损单的本质是“条件单”。条件满足前,它只是一个待命的指令;条件满足后,它才真正进入撮合流程。
二、订单状态机:从生到死的完整旅程
每个订单,从进入系统到最终离开,都会经历一系列状态变化。这就是订单状态机。我个人习惯把它想象成一个“流水线”:订单进来,经过几个工位,最后出去。
下面这张图,是我自己画的订单状态机流程图,你看一眼就明白了:
2.1 New(新订单)—— 出生
订单刚进入系统时,状态就是 New。这时候系统还没做任何处理,只是收到了你的指令。我习惯把 New 状态叫做“待检区”。
在 New 状态,系统会做几件事:
- 校验订单参数(价格、数量、类型是否合法)
- 检查账户余额或持仓是否足够
- 分配一个唯一的订单 ID
如果校验失败,订单会直接进入 Rejected 状态(有些系统把 Rejected 归为 Cancelled 的一种)。
2.2 Accepted(已接受)—— 进入撮合队列
校验通过后,订单进入 Accepted 状态。这时候,订单已经进入订单簿,开始等待撮合。
Accepted 状态是订单的“活跃期”。它会一直待在订单簿里,直到发生以下三件事之一:
- 被对手盘吃掉(全部成交 → Filled)
- 被对手盘吃掉一部分(部分成交 → Partial)
- 被用户撤销(Cancelled)
2.3 Partial(部分成交)—— 吃了一半
Partial 状态,说白了就是“没吃完”。比如你挂了 100 个限价单,结果只成交了 30 个,剩下的 70 个还在订单簿上。这时候订单状态就是 Partial。
Partial 状态是 Accepted 的“子状态”。它本质上还是活跃的,只是已经有一部分成交了。系统会记录:
- 已成交数量
- 剩余数量
- 成交均价
从 Partial 状态出发,订单可以走向:
- Filled:剩余部分全部成交
- Cancelled:用户撤销剩余部分
- Partial 继续:又成交了一部分,但还没完
2.4 Filled(全部成交)—— 寿终正寝
Filled 是订单的“完美结局”。所有数量都成交了,订单生命周期结束。
Filled 状态意味着:
- 订单从订单簿中移除
- 生成成交记录(Trade)
- 更新账户余额和持仓
嗯,这里要注意:Filled 状态是不可逆的。一旦进入 Filled,订单就彻底结束了,不能再做任何操作。
2.5 Cancelled(已撤销)—— 主动退出
Cancelled 状态,就是用户主动撤单。可能是用户改变了主意,也可能是系统触发了某些风控规则(比如价格超过涨跌幅限制)。
Cancelled 状态的特点:
- 只有未成交的部分才能撤销
- 已成交的部分不受影响
- 撤销后,订单从订单簿中移除
三、订单生命周期:一个完整的例子
光讲理论太枯燥。我举个例子,你跟着走一遍就明白了。
假设你挂了一个限价买单:
- 价格:100 USDT
- 数量:50 个 ETH
步骤 1:New
订单进入系统,状态为 New。系统校验:价格合法、数量合法、账户余额足够。校验通过。
步骤 2:Accepted
订单进入订单簿,状态变为 Accepted。系统开始寻找对手盘。当前卖一价是 101 USDT,比你的限价高,所以没有立即成交。
步骤 3:Partial
过了一会儿,市场价格跌到 100 USDT。卖一价变成了 100 USDT,有 20 个 ETH 在卖。你的订单立即吃掉这 20 个。状态变为 Partial,已成交 20,剩余 30。
步骤 4:Partial 继续
又过了几分钟,又有 10 个 ETH 以 100 USDT 卖出。你的订单再吃掉 10 个。状态还是 Partial,已成交 30,剩余 20。
步骤 5:Filled
最后,剩下的 20 个 ETH 也被吃掉了。状态变为 Filled。订单生命周期结束。
如果在这个过程中,你突然不想买了,可以随时撤销剩余部分。撤销后,状态变为 Cancelled,已成交的 30 个 ETH 不受影响。
我个人习惯在代码里用枚举来表示这些状态:
enum OrderStatus {
NEW,
ACCEPTED,
PARTIAL,
FILLED,
CANCELLED,
REJECTED // 有些系统会单独定义
};
状态转换的规则,我建议用状态机表来管理,清晰又不容易出错:
| 当前状态 | 触发事件 | 下一状态 |
|---|---|---|
| New | 校验通过 | Accepted |
| New | 校验失败 | Rejected |
| Accepted | 全部成交 | Filled |
| Accepted | 部分成交 | Partial |
| Accepted | 用户撤单 | Cancelled |
| Partial | 剩余全部成交 | Filled |
| Partial | 继续部分成交 | Partial |
| Partial | 用户撤单 | Cancelled |
好了,订单类型和生命周期就讲到这里。这些是撮合引擎最基础的知识,但也是最核心的。你想想看,无论多复杂的交易策略,最终都要落地到这些订单类型和状态转换上。