{"record":{"id":"3cdaa0cbb5292cb7","repo":"nautechsystems/nautilus_trader","slug":"missing-trailingoffsettype-for-trailing-stop-cal","errorCode":null,"errorMessage":"Missing `TrailingOffsetType` for trailing stop calculation","messagePattern":"Missing `TrailingOffsetType` for trailing stop calculation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/execution/src/trailing.rs","lineNumber":75,"sourceCode":"    // Seed from the current trigger only (never the activation price): when the trigger has\n    // not yet materialized it stays `None` here so the offset candidate below becomes the\n    // initial trigger on the first update (matches v1 `TrailingStopCalculator`).\n    let mut trigger_price = trigger_px.or(order.trigger_price());\n\n    let mut limit_price = if order_type == OrderType::TrailingStopLimit {\n        order.price()\n    } else {\n        None\n    };\n\n    let trigger_type = order\n        .trigger_type()\n        .ok_or_else(|| anyhow::anyhow!(\"Missing `TriggerType` for trailing stop calculation\"))?;\n    let trailing_offset = order.trailing_offset().ok_or_else(|| {\n        anyhow::anyhow!(\"Missing `trailing_offset` for trailing stop calculation\")\n    })?;\n    let trailing_offset_type = order.trailing_offset_type().ok_or_else(|| {\n        anyhow::anyhow!(\"Missing `TrailingOffsetType` for trailing stop calculation\")\n    })?;\n    let mut new_trigger_price: Option<Price>;\n    let mut new_limit_price: Option<Price> = None;\n\n    let maybe_move = |current: &mut Option<Price>,\n                      candidate: Price,\n                      better: fn(Price, Price) -> bool|\n     -> Option<Price> {\n        match current {\n            Some(p) if better(candidate, *p) => {\n                *current = Some(candidate);\n                Some(candidate)\n            }\n            None => {\n                *current = Some(candidate);\n                Some(candidate)\n            }\n            _ => None,","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/execution/src/trailing.rs#L57-L93","documentation":"`trailing_stop_calculate` requires the order to carry a trailing offset type (`TrailingOffsetType`) to know how to interpret the trailing offset (absolute price, basis points, or ticks). `OrderAny::trailing_offset_type()` returned `None`, meaning the order was built without this field, so the calculator cannot compute the offset and aborts with an anyhow error. It guards against silently computing a trailing price with an undefined offset semantics.","triggerScenarios":"Calling `trailing_stop_calculate` (directly or via `update_trailing_stop_order`) with an `OrderAny` whose `trailing_offset_type` is `None` — typically a TrailingStopMarket/TrailingStopLimit order constructed without setting `trailing_offset_type`.","commonSituations":"Hand-building a trailing stop order and forgetting the `trailing_offset_type` field (e.g. copying a factory that only sets `trailing_offset`); deserializing an order from a venue payload or older persisted state that lacked the field; custom order factories that initialize optional fields to `None`.","solutions":["Set `trailing_offset_type` when constructing the trailing stop order (e.g. `TrailingOffsetType::Price`, `BasisPoints`, or `Ticks`) so it matches the unit of `trailing_offset`.","Before calling the calculator, check `order.trailing_offset_type().is_some()` and reject/repair the order otherwise.","If the order came from deserialization or persisted state, migrate/upgrade the stored data to include the offset type."],"exampleFix":"// before\nlet order = OrderAny::builder()\n    .order_type(OrderType::TrailingStopMarket)\n    .trailing_offset(Decimal::from(100))\n    .build()?;\n// after\nlet order = OrderAny::builder()\n    .order_type(OrderType::TrailingStopMarket)\n    .trailing_offset(Decimal::from(100))\n    .trailing_offset_type(TrailingOffsetType::Price)\n    .build()?;","handlingStrategy":"validation","validationCode":"fn ensure_trailing_offset_type(order: &OrderAny) -> Result<(), String> {\n    if order.trailing_offset_type().is_none() {\n        return Err(format!(\n            \"order {} of type {} is missing trailing_offset_type\",\n            order.client_order_id(), order.order_type()\n        ));\n    }\n    Ok(())\n}","typeGuard":"fn has_trailing_offset_type(order: &OrderAny) -> bool {\n    order.trailing_offset_type().is_some()\n}","tryCatchPattern":null,"preventionTips":["Always set both `trailing_offset` and `trailing_offset_type` together when creating trailing stop orders.","Use order factory helpers/builder methods that require the offset type rather than hand-building orders.","Validate order completeness (offset, offset type, trigger price) before submitting to the execution pipeline."],"tags":["rust","trailing-stop","missing-field","order-validation"],"backgroundTag":"missing-required-argument","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}