docs/how_to/write_rust_strategy.md
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.
A strategy stores a StrategyCore field for runtime wiring. Normal strategy
logic does not use the field directly; use the facade methods on self.
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,
}
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"),
}
}
}
The nautilus_strategy! macro generates the native runtime wiring used by
registration and the Strategy trait impl. By default it delegates to a field
named core; pass a second argument for a different field name. The macro
does not make your strategy or its StrategyCore deref to runtime internals.
It also adds config(), which returns the StrategyConfig passed to
StrategyCore::new.
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()
}
}
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_idsubmit_order is available on self through the Strategy trait impl
generated by the macro.
Use the public facade in strategy logic:
clock()cache()order()portfolio()strategy_id()StrategyNormal strategy code does not import DataActorNative or StrategyNative, and
does not call native handles such as:
core()core_mut()strategy_core()strategy_core_mut()order_factory()order_factory_rc()portfolio_rc()Those native handles expose borrowed runtime state and stay in engine, runtime, registration, PyO3, testkit, plug-in host, or explicit latency-sensitive native code. The Rust native traits section covers the native-traits applicability matrix and these method tables:
To override Strategy trait methods such as order or position event
handlers, pass them in a block. The macro generates the internal plumbing
automatically; keep DataActor handlers in the separate impl DataActor
block.
nautilus_strategy!(MyStrategy, {
fn on_order_rejected(&mut self, event: OrderRejected) {
log::warn!("Order rejected: {}", event.reason);
}
});
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. |
EmaCross:
Dual-EMA crossover with indicator integration.GridMarketMaker:
Grid market making with configurable levels and requoting.