用 Rust 编写 NautilusTrader 交易策略:从结构体定义到订单管理全流程实战
2026/9/12 21:10:26 网站建设 项目流程

用 Rust 编写 NautilusTrader 交易策略:从结构体定义到订单管理全流程实战

【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader

本指南围绕 NautilusTrader 的 Rust 原生策略开发,逐步构建一个订阅行情报价(QuoteTick)并提交市价单的最小策略,完整覆盖StrategyCore运行时接线、nautilus_strategy!宏、DataActor回调、OrderApi订单构造与Strategy订单管理门面方法。阅读本文后,你将能够独立编写、注册并运行一个 Rust 原生交易策略,并理解其与引擎、运行时、缓存和组合(Portfolio)之间的交互边界。建议先阅读 Write an Actor (Rust) 掌握 Actor 基础,因为策略本质上是"扩展了订单管理能力的 Actor"。

策略与 Actor 的关系

在 NautilusTrader 中,Strategy构建于DataActor之上并叠加订单管理能力。从源码结构看,StrategyCore内部持有一个DataActorCore(字段名为actor),并额外持有OrderManagerOrderFactoryPortfolio引用、GTD 定时器表(gtd_timers)以及市场退出(market exit)状态机字段。这意味着策略天然继承了 Actor 的全部能力:

  • 历史数据请求(historical data requests)
  • 实时数据订阅(live data feed subscriptions)
  • 定时器与时间警报(time alerts / timers)
  • 缓存访问(cache access)
  • 组合访问(portfolio access)
  • 订单与持仓的创建和管理(orders & positions)

概念细节可参考 Strategies 与 Rust 两个概念指南。

第一步:定义策略结构体

一个 Rust 策略需要持有StrategyCore字段作为运行时接线入口。普通策略逻辑不直接操作该字段,而是通过self上的门面(facade)方法完成数据订阅、订单构造与提交:

use nautilus_common::actor::DataActor; use nautilus_model::{ data::QuoteTick, enums::OrderSide, identifiers::{InstrumentId, StrategyId}, types::Quantity, }; use nautilus_trading::{nautilus_strategy, strategy::{Strategy, StrategyConfig, StrategyCore}}; pub struct MyStrategy { core: StrategyCore, instrument_id: InstrumentId, trade_size: Quantity, }

StrategyCore的文档注释可知,该结构体被设计为"作为用户自定义策略结构体的成员持有",并通过nautilus_strategy!宏提供StrategyStrategyNativeDataActor所需的 trait 访问器。它不会 deref 到DataActorCore,这是门面模式在编译期层面的保证——策略代码无法直接触及内部运行时状态。

第二步:实现构造函数

策略构造的核心是构建StrategyConfig。它接受strategy_idorder_id_tag两个关键标识:

impl MyStrategy { pub fn new(instrument_id: InstrumentId) -> Self { let config = StrategyConfig { strategy_id: Some(StrategyId::from("MY_STRAT-001")), order_id_tag: Some("001".to_string()), ..Default::default() }; Self { core: StrategyCore::new(config), instrument_id, trade_size: Quantity::from("1.0"), } } }

order_id_tag 的语义与约束

order_id_tag会被追加到该策略产生的所有 client order ID 上,用于防止多个策略交易同一标的时 ID 冲突。关键规则:

  • 不能包含连字符(-:因为运行时需要从策略 ID 的最后一个连字符分隔段中读回该 tag。StrategyCore::new遇到非法 tag 会panic;使用StrategyCore::new_checked则会返回错误,便于在构造期优雅处理。
  • tag 与 strategy_id 的合成规则:从StrategyCore::new_checked的源码可以看出,若同时提供strategy_idorder_id_tag,运行时会生成形如ExampleStrategy-XNAS-T01的最终策略 ID;若只提供strategy_id,则直接从其最后一段提取 tag(如ExampleStrategy-XNAS→ tag 为XNAS)。
  • 未配置 strategy_id 时:构造期使用占位的Strategy-Noneactor ID,注册时才替换为派生 ID 与分配的数值 tag(从000开始递增)。相关测试见 core.rs 测试模块。

仓库测试对非法 tag 的验证非常直观(core.rs):order_id_tag: Some("A-B")会触发"order_id_tag cannot contain the '-' strategy ID separator, was 'A-B'"错误;非 ASCII 字符同样会被拒绝。

StrategyConfig 完整字段

StrategyConfig还提供以下可配置项(均可用..Default::default()覆盖):

字段默认值说明
strategy_idNone策略唯一 ID
order_id_tagNone订单 ID 标签,不能含-
use_uuid_client_order_idsfalse是否用 UUID4 生成 client order ID
use_hyphens_in_client_order_idstrue生成的 client order ID 是否带连字符
oms_typeNone订单管理系统类型,影响持仓 ID 的生成方式
external_order_instrument_idsNone策略声明认领的外部订单标的集合
manage_contingent_ordersfalse是否自动管理 OTO/OCO/OUO 开仓条件单
manage_gtd_expiryfalse是否由策略管理 GTD 到期(到期自动撤单)
manage_stopfalsestop()前先执行市场退出(平仓+撤单)
market_exit_interval_ms100市场退出完成度检查间隔
market_exit_max_attempts100市场退出最大检查次数(默认 100ms×100≈10s)
market_exit_time_in_forceGTC平仓市价单的 TIF(GTD 会被校验拒绝)
market_exit_reduce_onlytrue平仓市价单是否 reduce-only
log_events/log_commandstrue是否记录事件/命令日志
log_rejected_due_post_only_as_warningtruepost-only 拒绝是否按警告记录

StrategyConfig::validate(config.rs)会收集所有违规项:market_exit_interval_ms必须为正且不超过纳秒转换上限(u64::MAX / 1_000_000),market_exit_max_attempts必须为正,market_exit_time_in_force不允许为GTD

第三步:接入宏并实现 Debug

nautilus_strategy!宏是 Rust 策略与原生运行时之间的接线层。调用形式如下:

nautilus_strategy!(MyStrategy); impl std::fmt::Debug for MyStrategy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("MyStrategy").finish() } }

nautilus_strategy!宏定义 可以看到它实际生成的代码:

  1. 固有方法config():返回传入StrategyCore::newStrategyConfig引用。
  2. impl DataActorNative for MyStrategy:提供core()/core_mut()访问器,委托给core字段。
  3. impl StrategyNative for MyStrategy:提供strategy_core()/strategy_core_mut()
  4. impl Strategy for MyStrategy:生成Strategytrait 实现。

宏默认委托给名为core的字段;若字段名不同,可传第二个参数(如nautilus_strategy!(MyStrategy, strat_core))。宏不会让策略或其StrategyCorederef 到运行时内部,因此正常回调中应使用self上的门面方法而非生成的 native 访问器。

运行时注册依赖 blanket 的ActorComponent实现,它们要求 native 接线和Debug:宏提供接线,Debug需要手动实现或 derive。仓库中的EmaCross正是这种"宏 + 手写Debug"的标准模式。

第四步:实现 DataActor trait

数据处理方式与 Actor 完全一致——在on_start中订阅,在对应 handler 中响应:

impl DataActor for MyStrategy { fn on_start(&mut self) -> anyhow::Result<()> { self.subscribe_quotes(self.instrument_id, None, None); Ok(()) } fn on_quote(&mut self, quote: &QuoteTick) -> anyhow::Result<()> { let order = self.order().market( self.instrument_id, OrderSide::Buy, self.trade_size, None, None, None, None, None, None, None, ); self.submit_order(order, None, None, None)?; Ok(()) } }

要点说明:

  • subscribe_quotesDataActortrait 上的门面方法,可通过self直接调用(参数为instrument_id、可选的book_typedepth,此处均传None)。
  • 每个 handler 返回anyhow::Result<()>,与 Actor 的回调约定一致。
  • self.order()返回用户级订单构造 API(OrderApi),submit_order则由宏生成的Strategytrait 实现提供。
  • order_id_tag会体现在生成的 client order ID 中。仓库测试(core.rs)验证了注册后生成的 ID 格式为O-19700101-000000-001-T01-1(前缀O-、日期、序号、tag、自增计数)。

第五步:OrderApi 订单构造方法

self.order()(即OrderApi)内部持有OrderFactory的引用,提供以下订单构造方法:

方法说明
market市价单(支持 TIF、reduce_only、quote_quantity、执行算法、tags、自定义 client_order_id)
limit限价单(追加价格、到期时间、post_only、display_qty、模拟触发等参数)
stop_market停止-市价单(需 trigger_price 与 trigger_type)
stop_limit停止-限价单(需价格 + 触发价格)
market_to_limit市价转限价单
market_if_touched触价市价单
limit_if_touched触价限价单
trailing_stop_market移动停止-市价单(trailing_offset + 偏移类型 + 激活价)
trailing_stop_limit移动停止-限价单(price + limit_offset + trailing_offset)
bracket括号单:一次性生成入场单 + 止盈腿 + 止损腿三条关联订单(builder 风格)
create_list从给定订单创建订单列表(OrderList)
generate_client_order_id手动生成 client order ID
generate_order_list_id手动生成 order list ID

各构造方法的完整参数签名可参考 api.rs(如markettime_in_forcereduce_onlyquote_quantityexec_algorithm_idexec_algorithm_paramstagsclient_order_id等全部为Option)。bracket方法支持对入场单、止盈腿、止损腿分别配置订单类型、触发类型、TIF、post_only、tags 等,是构建复杂风控结构的便捷入口(api.rs)。

注意:这些方法均标注"若订单参数校验失败或 order factory 已被可变借用则 panic",因此不要在回调中长时间持有借用。

第六步:原生运行时访问边界

策略逻辑中应使用公共门面(facade):

  • clock():时钟访问(时间戳、定时器)
  • cache():缓存访问(行情、订单、持仓查询)
  • order():订单构造 API
  • portfolio():组合读取 API(账户余额、保证金、盈亏、净敞口等)
  • strategy_id():策略 ID
  • Strategytrait 上的订单管理方法

不要在普通策略代码中导入DataActorNativeStrategyNative,也不要直接调用core()core_mut()strategy_core()strategy_core_mut()order_factory()order_factory_rc()portfolio_rc()等 native 句柄。这些句柄暴露了借用状态的运行时对象,属于 engine、runtime、注册流程、PyO3、testkit 或明确对延迟敏感的原生代码。

StrategyNativetrait 的源码可见,order_factory()返回RefMut<OrderFactory>portfolio_rc()返回Rc<RefCell<Portfolio>>,这些借用类型不会跨 Python 边界。若你的策略需要同时运行在 Python 端或插件化接口上,必须只使用门面方法。完整的 native-traits 适用性矩阵与方法表见 Rust 概念指南的 Native Traits 章节、DataActorNative方法表 与StrategyNative方法表。

第七步:重写 Strategy 钩子

若要覆盖Strategytrait 的方法(如订单、持仓事件处理器),把它们放在宏的 brace 块中传入;DataActor的 handler 则保持在独立的impl DataActor块中:

nautilus_strategy!(MyStrategy, { fn on_order_rejected(&mut self, event: OrderRejected) { log::warn!("Order rejected: {}", event.reason); } });

宏会自动生成内部管道(plumbing),你只需关注业务逻辑。宏同样支持"自定义字段名 + 钩子块"的组合形式,如nautilus_strategy!(MyStrategy, strat_core, { ... }),见 macros.rs。

第八步:订单管理门面方法

Strategytrait 提供以下订单管理门面方法:

方法作用
submit_order向交易所提交新订单
submit_order_list提交一组条件订单(订单列表)
modify_order修改价格、数量或触发价
modify_orders批量修改同一标的的多笔订单
cancel_order撤销指定订单
cancel_orders撤销筛选出的一组订单
cancel_all_orders撤销某标的所有订单
close_position用市价单平掉某持仓
close_all_positions平掉所有未平持仓

提交的订单会进入引擎命令路由:若指定了emulation_trigger则先进入OrderEmulator;若指定了exec_algorithm_id则先进入对应ExecutionAlgorithm;否则先进入RiskEngine(风控引擎)。撤单/改单的路由规则、批量取消的约束(同标的、不含模拟单/本地单)以及cancel_all_orders的跨策略作用域细节,均可参考 Strategies 概念指南 的订单管理章节。

注册策略到引擎

策略写好之后即可注册运行。回测引擎与实盘节点使用相同的注册方式:

// 回测 let strategy = MyStrategy::new(instrument_id); engine.add_strategy(strategy)?; // 实盘 let strategy = MyStrategy::new(instrument_id); node.add_strategy(strategy)?;

注册时StrategyCore::register会完成三件关键初始化:创建OrderFactory(以 trader ID + strategy ID 为参数)、创建OrderManager、挂载Portfolio引用。在此之前,self.order()等方法会因未注册而 panic(源码中以expect("Strategy not registered: OrderFactory not initialized")明确标注),这也解释了为什么应在on_start中做系统级工作、构造函数只初始化普通状态。

仓库中的完整参考实现

原仓库提供两个开箱即用的策略示例,是学习的最佳范本:

  • EmaCross:双 EMA 交叉策略,演示了指标集成——在on_start中订阅行情、在on_quote中喂入ExponentialMovingAverage指标并依据快慢线交叉产生买卖信号,完整结构见 strategy.rs,其配套 config.rs 展示了从配置构建策略的from_config模式。
  • GridMarketMaker:网格做市策略,演示可配置的网格层级与重新报价(requoting)逻辑,目录内含 README 与测试。

这两个示例覆盖了本文的全部要点:StrategyCore接线、宏生成、门面方法使用、handler 覆盖与测试验证,可直接作为模板二次开发。

延伸阅读

  • Write an Actor (Rust):Actor 基础,理解订阅、请求与回调行为
  • Strategies 概念指南:策略生命周期 handler、时钟与定时器、缓存/组合访问、订单命令的完整路由
  • Rust 概念指南:native traits 适用性矩阵与全部 handler 方法表
  • Backtesting 概念:用历史数据回测策略
  • Run a Rust Backtest:回测引擎的实际运行流程

【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询