nautechsystems/nautilus_trader · error

Missing `trailing_offset` for trailing stop calculation

Error message

Missing `trailing_offset` for trailing stop calculation

What it means

trailing_stop_calculate requires a numeric trailing_offset, but `order.trailing_offset()` returned None. Without the offset there is no distance definition for recalculating the trailing stop's trigger price.

Source

Thrown at crates/execution/src/trailing.rs:72

        anyhow::bail!("Invalid `OrderType` {order_type} for trailing stop calculation");
    }

    // Seed from the current trigger only (never the activation price): when the trigger has
    // not yet materialized it stays `None` here so the offset candidate below becomes the
    // initial trigger on the first update (matches v1 `TrailingStopCalculator`).
    let mut trigger_price = trigger_px.or(order.trigger_price());

    let mut limit_price = if order_type == OrderType::TrailingStopLimit {
        order.price()
    } else {
        None
    };

    let trigger_type = order
        .trigger_type()
        .ok_or_else(|| anyhow::anyhow!("Missing `TriggerType` for trailing stop calculation"))?;
    let trailing_offset = order.trailing_offset().ok_or_else(|| {
        anyhow::anyhow!("Missing `trailing_offset` for trailing stop calculation")
    })?;
    let trailing_offset_type = order.trailing_offset_type().ok_or_else(|| {
        anyhow::anyhow!("Missing `TrailingOffsetType` for trailing stop calculation")
    })?;
    let mut new_trigger_price: Option<Price>;
    let mut new_limit_price: Option<Price> = None;

    let maybe_move = |current: &mut Option<Price>,
                      candidate: Price,
                      better: fn(Price, Price) -> bool|
     -> Option<Price> {
        match current {
            Some(p) if better(candidate, *p) => {
                *current = Some(candidate);
                Some(candidate)
            }
            None => {
                *current = Some(candidate);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Specify trailing_offset (price or percentage per trailing_offset_type) when creating the trailing stop order
  2. Fix the order factory/config so trailing_offset is always set for TrailingStop* orders
  3. Migrate deserialized order payloads to include trailing_offset
  4. In tests, build orders with a valid trailing_offset fixture value

Example fix

// before
order = order_factory.trailing_stop_limit(
    side=OrderSide.BUY, trigger_type=TriggerType.LAST_PRICE,
)
// after
order = order_factory.trailing_stop_limit(
    side=OrderSide.BUY, trigger_type=TriggerType.LAST_PRICE,
    trailing_offset=Decimal("0.005"),
    trailing_offset_type=TrailingOffsetType.BASIS_POINTS,
)
Defensive patterns

Strategy: validation

Validate before calling

if order.trailing_offset() is None:
    raise ValueError("Trailing stop order requires trailing_offset")

Type guard

fn has_trailing_offset(order: &OrderAny) -> bool {
    order.trailing_offset().is_some() && order.trailing_offset_type().is_some()
}

Try / catch

match trailing_stop_calculate(order_type, trigger_px, &order, bid, ask, last) {
    Ok(prices) => apply(prices),
    Err(e) => { log::error!("trailing calc failed: {e}"); leave_trigger_unchanged() }
}

Prevention

When it happens

Trigger: Calling trailing_stop_calculate (or update_trailing_stop_order) with an order whose trailing_offset() is None — the order was created without trailing_offset or constructed/deserialized incompletely.

Common situations: Omitting trailing_offset in order templates or strategy config; older serialized orders lacking the field; test fixtures building invalid trailing stop orders.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/8dced1978007903e. Report an issue: GitHub.