nautechsystems/nautilus_trader · error

TrailingStopMarket requires trailing_offset_type

Error message

TrailingStopMarket requires trailing_offset_type

What it means

Along with trailing_offset, a TrailingStopMarket order on OKX requires trailing_offset_type, which tells the adapter how to interpret the offset (BasisPoints -> callback_ratio, price/other units -> callback_spread). Missing it means the adapter cannot choose the OKX parameter encoding, so submission fails.

Source

Thrown at crates/adapters/okx/src/execution.rs:893

        let price = context.price;
        let is_reduce_only = context.is_reduce_only;

        let trailing_offset = order.trailing_offset();
        let trailing_offset_type = order.trailing_offset_type();
        let activation_price = order.activation_price();

        let close_fraction = get_param_as_string(&cmd.params, "close_fraction");
        let reduce_only = if close_fraction.is_some() {
            Some(true)
        } else {
            Some(is_reduce_only)
        };

        let (callback_ratio, callback_spread) = if order_type == OrderType::TrailingStopMarket {
            let offset = trailing_offset
                .ok_or_else(|| anyhow::anyhow!("TrailingStopMarket requires trailing_offset"))?;
            let offset_type = trailing_offset_type.ok_or_else(|| {
                anyhow::anyhow!("TrailingStopMarket requires trailing_offset_type")
            })?;

            match offset_type {
                TrailingOffsetType::BasisPoints => {
                    // Convert basis points to ratio (e.g., 100 bps = 0.01)
                    let ratio = offset / Decimal::from(10000);
                    (Some(ratio.to_string()), None)
                }
                TrailingOffsetType::Price => (None, Some(offset.to_string())),
                _ => {
                    anyhow::bail!("Unsupported trailing_offset_type for OKX: {offset_type:?}");
                }
            }
        } else {
            (None, None)
        };

        self.ws_dispatch_state.track_order_context(context);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set trailing_offset_type (e.g. TrailingOffsetType::BasisPoints or Price) whenever trailing_offset is set for TrailingStopMarket.
  2. Use the order factory's trailing_stop_market constructor which takes both parameters.
  3. Add a pre-submit validation in strategy code for trailing orders.
  4. If the desired unit is percent/bps, remember the adapter converts (100 bps = 0.01 ratio).

Example fix

// before
let cmd = ...TrailingStopMarket..., trailing_offset: Some(Decimal::from(50)), trailing_offset_type: None...
// after
let cmd = ...trailing_offset: Some(Decimal::from(50)), trailing_offset_type: Some(TrailingOffsetType::BasisPoints)...
Defensive patterns

Strategy: validation

Validate before calling

if order_type == OrderType::TrailingStopMarket && trailing_offset_type.is_none() {
    return Err(anyhow::anyhow!(
        "TrailingStopMarket requires trailing_offset_type"
    ));
}

Type guard

fn has_offset_type(t: &TrailingOffsetType) -> bool { true } // prefer Option::is_some check

Prevention

When it happens

Trigger: submit_order -> submit_conditional_order with order_type == TrailingStopMarket and trailing_offset_type == None, even when trailing_offset is provided.

Common situations: Strategy sets the numeric offset but forgets the unit; porting from venues where the offset type is implicit; factory helpers that only set one of the two fields.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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