Back to Nautilus Trader

Write a Strategy (Rust)

docs/how_to/write_rust_strategy.md

1.226.04.7 KB
Original Source

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.

rust
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.

rust
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 Deref<Target = DataActorCore>, DerefMut, and the Strategy trait impl (the core()/core_mut() accessors). By default it delegates to a field named core; pass a second argument for a different field name.

Debug is a trait bound on DataActor, so implement it manually or derive it.

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()
    }
}

Implement the DataActor trait

Data handling works the same as in an actor. Subscribe in on_start, respond in handlers.

rust
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.core.order_factory().market(
            self.instrument_id,
            OrderSide::Buy,
            self.trade_size,
            None, None, None, None, None, None, None,
        );
        self.submit_order(order, None, None)?;
        Ok(())
    }
}

self.core.order_factory() builds order objects. Available methods: market, limit, stop_market, stop_limit, market_if_touched, limit_if_touched, and trailing_stop_market.

submit_order is available on self through the Strategy trait impl generated by the macro.

Override Strategy hooks

To override Strategy trait methods such as order or position event handlers, pass them in a block. The macro generates core() and core_mut() automatically; do not redefine them in the block.

rust
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 methods through StrategyCore:

MethodAction
submit_orderSubmit a new order to the venue.
submit_order_listSubmit a list of contingent orders.
modify_orderModify price, quantity, or trigger price.
cancel_orderCancel a specific order.
cancel_ordersCancel a filtered set of orders.
cancel_all_ordersCancel all orders for an instrument.
close_positionClose a position with a market order.
close_all_positionsClose all open positions.

Full examples

  • EmaCross: Dual-EMA crossover with indicator integration.
  • GridMarketMaker: Grid market making with configurable levels and requoting.