Write a Strategy (Rust)
A strategy extends an actor with order management. This guide walks through building a minimal strategy that subscribes to quotes and submits market orders. Read Write an Actor (Rust) first.
For background on strategy concepts and order management, see the Strategies and Rust concept guides.
Define the struct
A strategy owns a StrategyCore instead of a DataActorCore. The
StrategyCore wraps DataActorCore and adds an OrderFactory,
OrderManager, and portfolio integration.
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,
}Implement the constructor
StrategyConfig takes a strategy_id and an order_id_tag. The tag is
appended to all client order IDs from this strategy, preventing collisions
when multiple strategies trade the same instrument.
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"),
}
}
}Wire up the core and implement Debug
The nautilus_strategy! macro generates the native runtime wiring used by
registration and core-backed defaults, plus the Strategy trait impl. By
default it delegates to a field named core; pass a second argument for a
different field name.
Runtime registration uses blanket Actor and Component implementations that
require native wiring and Debug. The macro supplies the native wiring;
implement Debug manually or derive it.
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()
}
}Implement the DataActor trait
Data handling works the same as in an actor. Subscribe in on_start,
respond in handlers.
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(())
}
}self.order() builds orders and order lists. Available methods:
marketlimitstop_marketstop_limitmarket_to_limitmarket_if_touchedlimit_if_touchedtrailing_stop_markettrailing_stop_limitbracketcreate_listgenerate_client_order_idgenerate_order_list_id
submit_order is available on self through the Strategy trait impl
generated by the macro.
Native runtime access
Use the public facade by default:
clock()cache()order()portfolio()strategy_id()- The order management methods on
Strategy
| Strategy path | Use native traits? | Use this API |
|---|---|---|
| Native Rust binary | Only when needed | Facades on Strategy and DataActor |
| Rust configured from Python | Only when needed | Same as native Rust |
| Python‑authored strategy | No | Facades only |
| Plug‑in‑compatible strategy | No | Facades only |
Import DataActorNative or StrategyNative only for performance-sensitive
native code or host integration internals. They expose borrowed core state such
as:
clock_mut()cache_ref()cache_rc()order_factory()order_factory_rc()portfolio_rc()strategy_core()strategy_core_mut()
These types do not cross Python or plug-in boundaries. Once the trait is in
scope, call the native methods on the strategy value, not through its core
field.
Override Strategy hooks
To override Strategy trait methods such as order or position event
handlers, pass them in a block. The macro generates internal core() and
core_mut() plumbing automatically; do not redefine those accessors in the
block.
nautilus_strategy!(MyStrategy, {
fn on_order_rejected(&mut self, event: OrderRejected) {
log::warn!("Order rejected: {}", event.reason);
}
});Order management methods
The Strategy trait provides these facade methods:
| Method | Action |
|---|---|
submit_order | Submit a new order to the venue. |
submit_order_list | Submit a list of contingent orders. |
modify_order | Modify price, quantity, or trigger price. |
modify_orders | Modify multiple orders for the same instrument. |
cancel_order | Cancel a specific order. |
cancel_orders | Cancel a filtered set of orders. |
cancel_all_orders | Cancel all orders for an instrument. |
close_position | Close a position with a market order. |
close_all_positions | Close all open positions. |
Full examples
EmaCross: Dual-EMA crossover with indicator integration.GridMarketMaker: Grid market making with configurable levels and requoting.
Write an Actor (Rust)
An actor receives market data, custom data/signals, and system events but does not manage orders. This guide walks through building a SpreadMonitor that...
Run a Backtest (Rust)
Nautilus provides two Rust APIs for backtesting: BacktestEngine (low-level) and BacktestNode (high-level with catalog streaming). This guide covers both.