NautilusTrader 追踪止损市价单(Trailing-Stop-Market)完全指南:原理、参数与实战代码
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
追踪止损市价单(Trailing-Stop-Market)是 NautilusTrader 九种订单类型中最重要的条件跟踪型订单之一:它的止损触发价会随市场朝有利方向移动而保持固定偏移,一旦触发便立即释放一张市价单。本文基于 docs/concepts/orders/trailing_stop_market.md 展开,结合 TrailingStopMarketOrder 的 Rust 源码实现 与订单体系文档,系统讲解其原理、使用场景、Python/Rust 双语言构造代码、全部可选参数、FIX 协议映射、底层状态机与验证规则,帮助你直接在实盘或回测策略中正确使用追踪止损保护利润。
什么是 Trailing-Stop-Market 订单
Trailing-Stop-Market订单的核心特征是一条动态止损线:
- 订单维护一个触发价(trigger price),该触发价与市场参考价之间保持固定偏移(trailing offset);
- 当市场朝有利方向移动时,触发价跟随市场同步移动(即"追踪"),始终与最新市场价格保持设定的偏移;
- 当市场朝不利方向回撤达到偏移量时,触发价被触及,订单释放一张Market(市价)单立即执行。
在 FIX 5.0 SP2 协议中,Trailing-Stop-Market 没有专属的OrdType值,其映射为FIX OrdType <40>=3(Stop)加上 trailing peg(追踪钉住)字段。NautilusTrader 将其建模为独立的OrderType::TrailingStopMarket,是OrderType枚举九个成员之一,见 docs/concepts/orders/index.md 中的订单类型总表。
从源码结构看,crates/model/src/orders/trailing_stop_market.rs 中TrailingStopMarketOrder结构体专门承载了这类订单的全部领域状态:
pub struct TrailingStopMarketOrder { core: OrderCore, pub activation_price: Option<Price>, // 激活价:达到该价位后订单才开始生效 pub trigger_price: Option<Price>, // 触发价:初始可显式指定,也可由偏移首先生成 pub trigger_type: TriggerType, // 触发方法(默认 DEFAULT) pub trailing_offset: Decimal, // 追踪偏移量(核心字段) pub trailing_offset_type: TrailingOffsetType, // 偏移类型:PRICE / BASIS_POINTS / TICKS / PRICE_TIER pub expire_time: Option<UnixNanos>, // GTD 到期时间 pub display_qty: Option<Quantity>, // 冰山显示数量 pub trigger_instrument_id: Option<InstrumentId>, // 跨品种触发时的参考合约 pub is_activated: bool, // 是否已激活 pub is_triggered: bool, // 是否已触发 pub ts_triggered: Option<UnixNanos>, // 触发时间戳 }其中trailing_offset与trailing_offset_type是追踪逻辑的两个核心字段:前者决定偏移的大小,后者决定偏移的计量方式(见下文"追踪偏移类型"小节)。
使用场景:保护利润,同时保留上行空间
Trailing-Stop-Market的典型用途是在保护已有浮盈的同时,允许仓位继续吃下有利方向的行情:
- 窄偏移(tight offset):止损线贴市很近,回撤很小就会触发,锁定利润更果断,但普通波动就可能误触发,导致过早离场;
- 宽偏移(wide offset):止损线离市较远,能容忍正常波动、让利润奔跑,代价是回吐更多利润;
- 触发后释放的**市价单存在滑点(slippage)、被拒(rejected)或剧烈反转时无法成交(unfilled)**的风险——这是市价执行方式的固有代价,需要在仓位管理与风控中预留缓冲。
一句话总结使用决策:窄偏移适合波动小、对回撤容忍度低的标的;宽偏移适合趋势性强、日内噪声大的标的。订单的reduce_only参数可进一步约束其只能缩减既有仓位,避免意外开仓。
实战示例:在 Binance Futures 上卖出 ETHUSD-PERP
原文档给出了一个完整的实战案例:在 Binance Futures 交易所(COIN_M 保证金模式)的ETHUSD-PERP永续合约上,卖出 10 张合约。订单在价格达到5,000 USD时激活,随后以**当前最新成交价的 1%(以基点表示,即 100 个基点)**作为追踪偏移。
Python 版本
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 TrailingStopMarketOrder from nautilus_trader.model import TriggerType order: TrailingStopMarketOrder = self.order_factory.trailing_stop_market( instrument_id=InstrumentId.from_str("ETHUSD-PERP.BINANCE"), order_side=OrderSide.SELL, quantity=Quantity.from_int(10), activation_price=Price.from_str("5_000"), trigger_type=TriggerType.LAST_PRICE, # <-- optional (default DEFAULT) trailing_offset=Decimal(100), trailing_offset_type=TrailingOffsetType.BASIS_POINTS, time_in_force=TimeInForce.GTC, # <-- optional (default GTC) expire_time=None, # <-- optional (default None) reduce_only=True, # <-- optional (default False) tags=["TRAILING_STOP-1"], # <-- optional (default None) )Rust 版本
use nautilus_model::{ enums::{OrderSide, TimeInForce, TrailingOffsetType, TriggerType}, identifiers::InstrumentId, types::{Price, Quantity}, }; use rust_decimal::Decimal; use ustr::Ustr; let order = self.order().trailing_stop_market( InstrumentId::from("ETHUSD-PERP.BINANCE"), OrderSide::Sell, Quantity::from(10), Decimal::from(100), // trailing_offset Some(TrailingOffsetType::BasisPoints), // optional (default PRICE) Some(Price::from("5000")), // activation_price None, // trigger_price (materializes from the offset on the first trail) Some(TriggerType::LastPrice), // optional (default DEFAULT) Some(TimeInForce::Gtc), // optional (default GTC) None, // expire_time 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-1")]), // tags None, // client_order_id );两种语言调用的是同一套底层模型:Python 的OrderFactory.trailing_stop_market通过 pyo3 绑定 最终落到 Rust 的TrailingStopMarketOrder::new_checked;而 Rust 策略中self.order()返回的工厂方法同样构造该订单并自动填充 trader/strategy ID、client order ID 与时间戳,参见 docs/concepts/orders/index.md 的 "Order factory" 一节。
关键行为:activation_price 与 trigger_price 的关系
原文档特别强调了一条容易被忽视的规则:
如果同时省略
activation_price和trigger_price,订单立即在当前市场价格激活,且触发价会在第一次更新时由trailing_offset物化(materialize)生成。
也就是说:
- 显式给出
activation_price(如示例中的 5,000):市场必须先触及激活价,订单才进入有效追踪状态; - 显式给出
trigger_price:作为追踪的初始锚点; - 两者都省略:订单立即激活,首个触发价 = 当前市场价 ∓ 偏移量(方向取决于买卖方向)。
该行为在源码中得到印证:crates/model/src/orders/trailing_stop_market.rs 的测试test_reconstruct_with_trigger_and_activation_none验证了trigger_price与activation_price均为None的订单可以正常构建;is_activated标志在订单初始化时为false,通过set_activated()置为true(见 同文件 L242-L245),激活价可通过has_activation_price()查询,并能在事件回放中无损保留(测试test_activation_price_round_trips_through_event验证了OrderInitialized事件携带激活价并支持重建)。
参数全解:从默认值到校验规则
追踪偏移类型(TrailingOffsetType)
订单如何计量"固定偏移"由trailing_offset_type决定。依据 docs/concepts/orders/index.md 的 "Trailing offset type" 小节与 crates/model/src/enums.rs 的枚举定义:
| 枚举值 | 含义 |
|---|---|
PRICE | 以价格差计量偏移(默认值)。示例:偏移10即 10 个价格单位 |
BASIS_POINTS | 以基点计量百分比,100 个基点 = 1%。示例中Decimal(100)即 1% 偏移 |
TICKS | 以 tick(最小价格变动单位)个数计量 |
PRICE_TIER | 使用交易所特定的价格档位(venue-specific price tier) |
注意:缺失的trailing_offset_type用None表示,对追踪类订单是非法的。Rust 构造器的默认值为PRICE,因此不传该参数时需保证trailing_offset的单位是价格。
触发方法(TriggerType)
trigger_type决定"用哪个市场价来判定是否触发",同样是条件单的必备语义。依据 docs/concepts/orders/index.md 的 "Trigger type" 小节:
| 枚举值 | 含义 |
|---|---|
DEFAULT | 使用交易所默认触发方法(默认值) |
LAST_PRICE | 使用最新成交价 |
BID_ASK | BUY 单看 ask,SELL 单看 bid |
DOUBLE_LAST | 需要连续两次相同的 last price 才触发 |
DOUBLE_BID_ASK | 需要连续两次匹配的 bid/ask 才触发 |
LAST_OR_BID_ASK | 使用 last price 或按方向取 bid/ask |
MID_POINT | 使用 bid 与 ask 的中点 |
MARK_PRICE | 使用交易所标记价格(合约类常用) |
INDEX_PRICE | 使用交易所指数价格 |
示例中选用的TriggerType.LAST_PRICE意味着追踪参考价 = 最新成交价;若在流动性薄弱的合约上担心成交价毛刺,可改用MARK_PRICE或DOUBLE_LAST平滑触发判定。
其余可选参数与默认值
| 参数 | 默认值 | 说明 |
|---|---|---|
time_in_force | GTC | 生效时长;可选IOC/FOK/GTD/DAY等 |
expire_time | None | 配合GTD使用,指定订单到期时间;GTD必须提供expire_time |
reduce_only | False | 是否只允许缩减仓位(防止反向开仓) |
quote_quantity | False | 数量是否以报价货币计量 |
display_qty | None | 冰山订单的可见数量,须小于等于quantity |
emulation_trigger | None | 本地模拟触发条件(见下文"模拟执行") |
trigger_instrument_id | None | 跨品种触发:用另一合约的行情作为触发参考 |
tags | None | 订单标签,便于检索与统计 |
构造时的校验规则(源码级)
TrailingStopMarketOrder::new_checked(crates/model/src/orders/trailing_stop_market.rs)在构造阶段执行严格的正确性校验,失败即返回OrderError;对应测试用例位于同文件测试模块:
- 数量必须为正:
quantity非正直接报错(测试test_quantity_zero_err); - display_qty 不得超过 quantity:否则抛出
Condition failed: display_qty may not exceed quantity(测试test_display_qty_gt_quantity_err); - GTD 必须携带 expire_time:
TimeInForce::Gtd且expire_time缺失/为零时拒绝构造(测试test_gtd_without_expire_err); - 初始化事件字段完备性:由
OrderInitialized重建订单时,trigger_type、trailing_offset、trailing_offset_type三个字段缺一不可(见TryFrom<OrderInitialized>实现,L597-L654)。
订单生命周期与底层状态机
Trailing-Stop-Market 订单的生命周期与 NautilusTrader 统一的订单状态机完全一致(完整状态流转图见 docs/concepts/orders/index.md 的 "Order state flow" 小节),其特有节点集中在"激活 → 触发"阶段:
INITIALIZED:订单在本地实例化;- 提交后被交易所确认 →
ACCEPTED(在交易所"挂起"追踪中); - 市场触及激活价后订单被激活(
is_activated = true),触发价开始随市场移动; - Stop 被击中 →
TRIGGERED:is_triggered置为true,同时记录ts_triggered; - 释放市价单并成交 →
PARTIALLY_FILLED/FILLED。
从源码看,TrailingStopMarketOrder::apply(crates/model/src/orders/trailing_stop_market.rs)对OrderEventAny::Triggered事件设置is_triggered与ts_triggered;对Filled/FillVoided事件,若订单已持有触发价,会以触发价为基准自动计算并记录滑点(set_slippage(trigger_price))。测试test_trailing_stop_market_order_sets_slippage_when_filled演示了买入单在触发价 90.00、成交价 98.50 时,滑点被记为 8.50。
订单更新方面,update()方法(L522-L531)接受OrderUpdated事件:可以更新trigger_price(追踪过程中交易所推送的新触发价)与剩余数量,但拒绝携带price字段的更新(追踪单本身没有挂单价);测试test_trailing_stop_market_order_rejects_invalid_update_atomically验证了非法更新会被原子性拒绝,订单状态与事件历史保持不变。
在无原生支持的交易所上使用:模拟执行(Emulated orders)
并非所有交易所都原生支持追踪止损。NautilusTrader 的OrderEmulator组件允许在本地模拟追踪止损等条件单:订单停留在EMULATED状态,由本地行情驱动判定触发条件,触发后以RELEASED状态释放并转成普通的MARKET/LIMIT单提交给交易所,最终执行时只使用真实支持的订单类型。
若你的目标 venue 不支持 Trailing-Stop-Market,可参考 docs/concepts/orders/emulated.md 了解:
- 模拟的生命周期(
INITIALIZED→EMULATED→ 本地触发 →RELEASED→SUBMITTED); - 在构造订单时通过
emulation_trigger指定本地触发方法; - 查询与最佳实践(对模拟单的状态查询、撤销与归因需走模拟路径)。
追踪止损是模拟执行最典型的受益者:本地每收到一个 tick 就重新计算触发价,触发判定完全可控,且不会因交易所不支持而丢失策略逻辑。
延伸阅读
- 订单体系总览(含全部九种订单类型与 FIX 映射)
- 模拟订单(在无原生支持的交易所上模拟追踪止损)
- 执行(订单如何到达交易所、成交如何处理)
- TrailingStopMarketOrder 的 Python API 参考(含全部属性与方法)
- 订单状态流转与术语(open / closed / in-flight 定义)
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考