NautilusTrader 追踪限价止损单(Trailing-Stop-Limit Order)完全指南:原理、参数与源码实现
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
导读
追踪限价止损单(Trailing-Stop-Limit)是 NautilusTrader 九种标准订单类型中两种"条件追踪"订单之一:它在市场价格朝有利方向移动时,始终让触发价与市场价保持固定偏移,并在触发后释放一张限价单(Limit order),且该限价单的价格同样随市场同步更新。本文以 NautilusTrader 官方概念文档为核心,结合crates/model、crates/execution与crates/common中的真实源码,系统讲解其订单模型、工厂 API、Rust/Python 双语言创建方法、触发与限价计算算法、校验规则以及实盘/模拟盘中的行为边界,帮助你在策略中正确使用这一订单类型并理解底层执行机制。
什么是 Trailing-Stop-Limit 订单
按照官方概念文档 orders/trailing_stop_limit.md 的定义,Trailing-Stop-Limit 订单具有以下核心特征:
- 它属于条件订单(Conditional order),在 FIX 协议中对应
OrdType <40>=4(Stop Limit)叠加 trailing peg 字段——FIX 没有为追踪止损定义独立的OrdType,因此 NautilusTrader 在 FIX 映射中将其建模为4(Stop Limit)+ trailing peg(参见 orders/index.md 的 FIX OrdType 映射表)。 - 在订单激活并开始追踪后,其触发价始终与"适用的市场价格"保持一个固定偏移(trailing offset)。当市场价格朝有利方向移动时,触发价随之更新(只朝有利方向收紧,绝不回退);当价格反向穿越触发价时,订单被触发。
- 触发后释放的不是市价单,而是一张限价单。这张限价单的价格同样由
limit_offset相对市场价格计算得出,并随市场移动而更新,直到触发那一刻为止。
与纯 Trailing-Stop-Market(释放市价单)不同,Trailing-Stop-Limit 把"出场执行"限定在指定的最差可接受成交价之上,代价是:在快速反转行情中,释放出的限价单可能无法成交,从而让仓位继续暴露在市场风险之下。这与 Stop-Limit 的固有限制一致——文档 Use cases 一节对此有明确说明。
适用场景:动态追踪保护 + 最差成交价约束
官方文档给出的适用场景非常聚焦:
为仓位提供动态追踪保护,同时限定一个"最差可接受成交价"。
典型用法是保护浮盈仓位:行情上涨时买入止损单(或行情下跌时卖出止损单)的触发价跟随市场抬高/压低,锁定部分利润;而一旦触发,限价单保证你不会在更差的价格成交。需要警惕的是,正如文档与源码都强调的,触发后的限价单在快速反转时可能不成交,这与 stop_limit.md 中 Stop-Limit 的风险一致。若你希望触发后必定成交,应选用 trailing_stop_market.md。
此外,如果目标交易场所(venue)不原生支持追踪止损,NautilusTrader 的OrderEmulator可以在本地模拟该订单类型:设置emulation_trigger后,本地模拟器会在触发条件满足时将其转换为LIMIT订单走正常风控与执行路径(详见 emulated.md 中"可模拟订单类型"表格,TRAILING_STOP_LIMIT的释放类型为LIMIT)。
使用 OrderFactory 创建 Trailing-Stop-Limit 订单
官方文档强调:所有示例订单都应在Strategy上下文中通过工厂创建——Python 端暴露为self.order_factory,Rust 端暴露为self.order()。工厂会自动分配 trader/strategy ID、生成 client order ID 与初始化 ID、记录初始时间戳,并为所选订单类型应用默认值(见 orders/index.md 的 Order factory 一节)。
官方示例:Currenex FX ECN 上的 AUD/USD 买入追踪限价止损
以下示例在 Currenex FX ECN 上以 0.71000 USD 的限价买入 1,250,000 AUD/USD,在 0.72000 USD 处激活,随后以距当前卖价 0.00100 USD 的偏移进行追踪,GTC 永久有效:
use nautilus_model::{ enums::{OrderSide, TimeInForce, TrailingOffsetType, TriggerType}, identifiers::InstrumentId, types::{Price, Quantity}, }; use rust_decimal_macros::dec; use ustr::Ustr; let order = self.order().trailing_stop_limit( InstrumentId::from("AUD/USD.CURRENEX"), OrderSide::Buy, Quantity::from(1_250_000), Price::from("0.71000"), // limit price dec!(0.00050), // limit_offset dec!(0.00100), // trailing_offset Some(TrailingOffsetType::Price), // optional (default PRICE) Some(Price::from("0.72000")), // activation_price None, // trigger_price (materializes from the offset on the first trail) Some(TriggerType::BidAsk), // optional (default DEFAULT) Some(TimeInForce::Gtc), // optional (default GTC) None, // expire_time Some(false), // post_only (default false) Some(true), // reduce_only (default false) None, // quote_quantity (default false) None, // display_qty None, // emulation_trigger None, // trigger_instrument_id None, // exec_algorithm_id None, // exec_algorithm_params Some(vec![Ustr::from("TRAILING_STOP")]), // tags None, // client_order_id );from decimal import Decimal from nautilus_trader.model import InstrumentId from nautilus_trader.model import OrderSide from nautilus_trader.model import Price from nautilus_trader.model import Quantity from nautilus_trader.model import TimeInForce from nautilus_trader.model import TrailingOffsetType from nautilus_trader.model import TrailingStopLimitOrder from nautilus_trader.model import TriggerType order: TrailingStopLimitOrder = self.order_factory.trailing_stop_limit( instrument_id=InstrumentId.from_str("AUD/USD.CURRENEX"), order_side=OrderSide.BUY, quantity=Quantity.from_int(1_250_000), price=Price.from_str("0.71000"), activation_price=Price.from_str("0.72000"), trigger_type=TriggerType.BID_ASK, # <-- optional (default DEFAULT) limit_offset=Decimal("0.00050"), trailing_offset=Decimal("0.00100"), trailing_offset_type=TrailingOffsetType.PRICE, time_in_force=TimeInForce.GTC, # <-- optional (default GTC) expire_time=None, # <-- optional (default None) reduce_only=True, # <-- optional (default False) tags=["TRAILING_STOP"], # <-- optional (default None) )工厂签名与参数详解
Rust 侧工厂方法OrderFactory::trailing_stop_limit的完整签名定义于 crates/common/src/factories/order.rs,Python 侧对应类型桩位于 python/nautilus_trader/common/init.pyi。两者参数一一对应,要点如下:
| 参数 | 类型 | 说明 |
|---|---|---|
instrument_id | InstrumentId | 交易品种 ID,如"AUD/USD.CURRENEX" |
order_side | OrderSide | BUY或SELL |
quantity | Quantity | 订单数量,必须为正数 |
price | Price \| None | 限价。可省略——若省略,限价会在首次追踪更新时由limit_offset物化生成(详见下文"限价延迟物化") |
limit_offset | Decimal | 限价相对市场价的偏移量,触发前限价随市场同步更新 |
trailing_offset | Decimal | 触发价相对市场价的追踪偏移量 |
trailing_offset_type | TrailingOffsetType \| None | 偏移量类型,默认PRICE |
activation_price | Price \| None | 激活价。订单提交后未达到该价格前不启动追踪 |
trigger_price | Price \| None | 初始触发价。通常省略,由首次追踪从偏移量物化 |
trigger_type | TriggerType \| None | 触发方法,默认DEFAULT(等效于BID_ASK的报价行为) |
time_in_force | TimeInForce \| None | 默认GTC;若为GTD则必须提供expire_time |
expire_time | int \| None | GTD 到期时间(Unix 纳秒),默认None |
post_only | bool \| None | 默认false |
reduce_only | bool \| None | 默认false,示例中置true用于只减仓 |
quote_quantity | bool \| None | 默认false |
display_qty | Quantity \| None | 冰山单可见数量,默认None |
emulation_trigger | TriggerType \| None | 本地模拟触发类型,设置后走OrderEmulator |
trigger_instrument_id | InstrumentId \| None | 跨品种触发时指定被监控的品种 |
exec_algorithm_id/exec_algorithm_params | 算法相关 | 附加执行算法 |
tags | Sequence[str] \| None | 订单标签 |
client_order_id | ClientOrderId \| None | 不传则由工厂生成 |
TrailingOffsetType:偏移量的四种表达
追踪偏移(以及限价偏移)如何"量纲化"由TrailingOffsetType决定,官方文档 orders/index.md 定义了四种取值:
PRICE:以价格差表示(文档示例0.00100USD 即此类);BASIS_POINTS:以基点百分比表示,100 个基点 = 1%;TICKS:以最小价格变动单位(tick)数量表示;PRICE_TIER:venue 特定的价格档位。
注意:文档同时指出,TrailingOffsetType缺失(None)对追踪订单而言是非法的,必须在创建时显式提供。
源码级解析:触发价与限价如何随市场更新
理解 Trailing-Stop-Limit 的行为,最直接的方式是阅读它的核心计算逻辑。crates/execution/src/trailing.rs中的trailing_stop_calculate函数负责在每次市场更新时为追踪止损单计算新的触发价与限价(trailing.rs),其关键语义如下:
只朝有利方向移动(单向收紧):
maybe_move闭包通过better_trigger比较候选价与当前价——OrderSide::Buy时c < p才算"更优",OrderSide::Sell时c > p才算"更优"。候选价若未改善,则保持原价不动。也就是说,买入追踪止损的触发价只会下移,卖出追踪止损的触发价只会上移,绝不可能反向放松。偏移量的换算(
compute闭包):Price:直接使用偏移量数值;BasisPoints:basis * offset / 10_000(100 基点 = 1%);Ticks:offset * price_increment(以 instrument 的price_increment为 tick 大小);- 其他类型(如
PriceTier)当前计算路径会直接报错。
买卖方向的价格基点:买入订单以
ask(卖价)为基点并加上偏移得到触发价/限价(basis + offset),卖出订单以bid(买价)为基点并减去偏移(basis - offset)。这与文档示例"以距当前 ask 0.00100 USD 的偏移追踪买入单"完全吻合。触发方法决定市场数据源:
LastPrice/MarkPrice:以最新成交价(或标记价)为基点;Default/BidAsk/LastOrBidAsk:买入以 ask、卖出以 bid 为基点(Default在本地表现为BidAsk报价行为,见 trailing.rs 及测试注释);DoubleLast、DoubleBidAsk、IndexPrice等类型当前不被该计算路径支持。
触发价缺失时自动物化:
trigger_price以"当前订单携带的触发价"为种子(只种触发价、绝不使用激活价);若触发价尚未物化(None),首次更新的候选价会直接成为初始触发价——这正是工厂示例中把trigger_price传None的原因(文档注释:materializes from the offset on the first trail)。Trailing-Stop-Limit 专属:当订单类型为
TrailingStopLimit时,函数还会用limit_offset计算候选限价(与触发价使用同一市场基点),并通过better_limit单向收紧。trailing_stop_calculate返回(new_trigger_price, new_limit_price)二元组,None表示对应价格未改善。
计算函数在两个执行路径中的调用
同一套计算函数被两处核心组件复用(见 crates/execution/src 目录):
- 模拟撮合引擎
crates/execution/src/matching_engine/mod.rs的update_trailing_stop_order(mod.rs):每次市场数据更新时,用price_increment、订单当前触发价、bid/ask/last调用trailing_stop_calculate;若市场数据尚不齐备导致计算失败,则记录 debug 日志并等待下一次更新;若两个价格均未改善则直接返回;否则通过generate_order_updated生成OrderUpdated事件,把新触发价/限价应用到订单上。这解释了文档所说"限价价格也随市场更新直到触发"的落地方式。 - 本地订单模拟器
crates/execution/src/order_emulator/emulator.rs(emulator.rs 附近):对设置了emulation_trigger的追踪止损单,同样调用trailing_stop_calculate更新本地触发价,并在触发条件满足时将其转换为LIMIT订单释放。
TrailingStopLimitOrder 订单模型与校验规则
订单类型的底层模型定义于 crates/model/src/orders/trailing_stop_limit.rs,TrailingStopLimitOrder结构体包含以下专属字段:
activation_price: Option<Price>——激活价;price: Option<Price>——当前限价(触发前随市场更新);trigger_price: Option<Price>——当前触发价;trigger_type: TriggerType——触发方法;limit_offset: Decimal、trailing_offset: Decimal、trailing_offset_type: TrailingOffsetType——两组偏移量及其类型;expire_time、is_post_only、display_qty、trigger_instrument_id——通用执行指令;is_activated: bool、is_triggered: bool、ts_triggered: Option<UnixNanos>——激活/触发状态及触发时间戳。
new_checked(trailing_stop_limit.rs)定义了创建时的完整校验规则,这些规则同样被 Python 与 Rust 工厂路径共用:
quantity必须为正数(否则报invalidQuantityfor 'quantity' not positive);display_qty(若提供)不得超过quantity;time_in_force为GTD时expire_time必填且不能为零;- 订单元数据必须通过
OrderInitialized::new_checked的全部不变量。
这些约束都有对应的单元测试佐证(同文件mod tests一节,如test_quantity_zero_err、test_display_qty_gt_quantity_err、test_gtd_without_expire_err)。此外,TryFrom<OrderInitialized>实现要求trigger_type、limit_offset、trailing_offset、trailing_offset_type四项在初始化事件中必须存在,否则视为谓词违反错误——说明这四项对 Trailing-Stop-Limit 而言是强制字段。
值得注意的两个行为细节
- 限价延迟物化:
has_price()的实现(trailing_stop_limit.rs)注释明确说明:限价在首次追踪更新从limit_offset物化之前可能为None,因此依赖 own-book / 价格的路径必须用has_price()判断而非假定限价存在。测试test_has_price_false_until_limit_materializes验证了这一点。 - 更新事件应用:
update方法(trailing_stop_limit.rs)在收到OrderUpdated时同步更新price、trigger_price、quantity与leaves_qty;apply方法在收到Triggered事件时置位is_triggered并记录ts_triggered。
订单状态机中的位置
TRIGGERED状态在订单生命周期中专门覆盖"stop-limit、trailing-stop-limit 或 limit-if-touched 订单在 venue 上被触发"这一情形(见 orders/index.md 的 Order status definitions 表)。触发后订单进入TRIGGERED,随后由释放出的限价单继续走ACCEPTED → PARTIALLY_FILLED → FILLED(或CANCELED/EXPIRED)等常规状态流。
实战建议与风险提示
结合官方文档与源码实现,使用 Trailing-Stop-Limit 时有几点值得注意:
- 与 Stop-Limit 共享的滑点与不成交风险:触发后释放的是限价单,快速反转行情下可能挂在盘口无法成交,仓位继续保持开放。若"必须离场"优先于"价格保护",请改用 Trailing-Stop-Market。
reduce_only=True是常见的组合用法:示例中即为reduce_only置真,配合SimulatedExchange的行为(仓位归零时自动撤单、按剩余仓位缩减数量),可用于实现只减仓的动态保护逻辑。- 触发方法与偏移类型的搭配要符合 venue 能力:不同 adapter/venue 对条件订单的支持程度不同,adapter 可能在提交前拒绝不支持的请求,或由 venue 直接拒单(见 orders/index.md 的说明)。本地模拟路径目前只接受
DEFAULT/BID_ASK/LAST_PRICE作为emulation_trigger(见 emulated.md)。 - 触发价/限价可由偏移自动物化:不必同时提供
price、trigger_price与两个偏移量;省略价格、只给偏移,让首次追踪更新自动生成价格,是文档示例推荐的简洁写法。 - 追踪只收紧不回退:无论行情如何反复,触发价与限价都只会朝有利方向单向移动,这既是特性也是约束——用它做"移动止损保护",而不是"灵活调整出场价"。
相关指南
- Orders 总览:订单概念、执行指令、TrailingOffsetType/TriggerType 枚举与 FIX OrdType 映射;
- Emulated orders:在无原生支持的 venue 上本地模拟追踪止损;
- Execution 概念:订单如何到达 venue、成交如何处理;
- Stop-Limit 指南:与本文订单共享"限价触发后可能不成交"的风险模型;
- Trailing-Stop-Market 指南:释放市价单的追踪止损变体。
延伸阅读:仓库中的关键实现位置
- 订单模型与校验:crates/model/src/orders/trailing_stop_limit.rs
- 触发/限价计算算法:crates/execution/src/trailing.rs
- 模拟撮合引擎中的追踪更新:crates/execution/src/matching_engine/mod.rs
- 本地模拟器调用:crates/execution/src/order_emulator/emulator.rs
- Rust 工厂实现:crates/common/src/factories/order.rs
- Python 类型桩:python/nautilus_trader/common/init.pyi
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考