Skip to content
All library documents

Building a Rust Trading Strategy with Quote Subscriptions and Order Management

Article NautilusTrader

Summary

This guide explains how to build a minimal trading strategy in Rust using NautilusTrader. A strategy stores a runtime core, configures its strategy identifier and order ID tag, and uses a macro to provide native runtime wiring. It also needs a Debug implementation for registration. The example strategy subscribes to quotes when it starts and submits a market buy order from its quote handler.

The guide outlines facade methods for accessing the clock, cache, portfolio, order builder, and strategy order management. It lists supported order types and operations such as submitting, modifying, and canceling orders, and describes how to override strategy event hooks. The example demonstrates framework integration rather than a tested trading signal: it buys on every quote and gives no performance evidence, risk controls, or exit logic. Readers are directed to separate examples for an EMA crossover and grid market making.

Key ideas

  • A Rust strategy stores a StrategyCore for runtime wiring and uses facade methods for ordinary strategy logic.
  • The strategy macro supplies runtime wiring, while the strategy must also implement or derive Debug.
  • A strategy can subscribe to quote data and respond by building and submitting orders.
  • The Strategy interface includes methods for submitting, modifying, and canceling orders, as well as closing positions.
  • The sample buys on every quote and does not describe entry filters, exit rules, or performance results.

Tags

Full text
# Write a Strategy (Rust)


# 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)](write_rust_actor.md) first.

For background on strategy concepts and order management, see the
[Strategies](../concepts/strategies.md) and [Rust](../concepts/rust.md)
concept guides.

## Define the struct

A strategy stores a `StrategyCore` field for runtime wiring. Normal strategy
logic does not use the field directly; use the facade methods on `self`.

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

The tag cannot contain a hyphen, because the runtime reads it back from the
final hyphen-separated part of the strategy ID. `StrategyCore::new` panics on
an invalid tag; use `StrategyCore::new_checked` to handle it as an error
instead.

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

```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.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:

- `market`
- `limit`
- `stop_market`
- `stop_limit`
- `market_to_limit`
- `market_if_touched`
- `limit_if_touched`
- `trailing_stop_market`
- `trailing_stop_limit`
- `bracket`
- `create_list`
- `generate_client_order_id`
- `generate_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 in strategy logic:

- `clock()`
- `cache()`
- `order()`
- `portfolio()`
- `strategy_id()`
- The order management methods on `Strategy`

Normal 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, or explicit latency-sensitive native code. The [Rust native traits](../concepts/rust.md#native-traits) section
covers the native-traits applicability matrix and these method tables:

- [`DataActorNative` methods](../concepts/rust.md#dataactornative-methods)
- [`StrategyNative` methods](../concepts/rust.md#strategynative-methods)

## Override Strategy hooks

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.

```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 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`](https://github.com/nautechsystems/nautilus_trader/tree/develop/crates/trading/src/examples/strategies/ema_cross):
  Dual-EMA crossover with indicator integration.
- [`GridMarketMaker`](https://github.com/nautechsystems/nautilus_trader/tree/develop/crates/trading/src/examples/strategies/grid_mm):
  Grid market making with configurable levels and requoting.

Shown in full with attribution under the source's licence. Licence: LGPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.