{"record":{"id":"3a12bcbd55751e44","repo":"nautechsystems/nautilus_trader","slug":"missing-limit-offset-for-trailing-stop-limit-cal","errorCode":null,"errorMessage":"Missing `limit_offset` for trailing stop limit calculation","messagePattern":"Missing `limit_offset` for trailing stop limit calculation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/execution/src/trailing.rs","lineNumber":128,"sourceCode":"                anyhow::bail!(\"`TrailingOffsetType` {trailing_offset_type} not currently supported\")\n            }\n        };\n        let value = match order_side {\n            OrderSide::Buy => basis + offset,\n            OrderSide::Sell => basis - offset,\n        };\n        Price::from_decimal_dp(value, price_increment.precision).map_err(Into::into)\n    };\n\n    match trigger_type {\n        TriggerType::LastPrice | TriggerType::MarkPrice => {\n            let last = last.ok_or(OrderError::InvalidStateTransition)?;\n            let cand_trigger = compute(trailing_offset, last)?;\n            new_trigger_price = maybe_move(&mut trigger_price, cand_trigger, better_trigger);\n\n            if order_type == OrderType::TrailingStopLimit {\n                let limit_offset = order.limit_offset().ok_or_else(|| {\n                    anyhow::anyhow!(\"Missing `limit_offset` for trailing stop limit calculation\")\n                })?;\n                let cand_limit = compute(limit_offset, last)?;\n                new_limit_price = maybe_move(&mut limit_price, cand_limit, better_limit);\n            }\n        }\n        TriggerType::Default | TriggerType::BidAsk | TriggerType::LastOrBidAsk => {\n            let (bid, ask) = (\n                bid.ok_or_else(|| anyhow::anyhow!(\"Bid required\"))?,\n                ask.ok_or_else(|| anyhow::anyhow!(\"Ask required\"))?,\n            );\n            let basis = match order_side {\n                OrderSide::Buy => ask,\n                OrderSide::Sell => bid,\n            };\n            let cand_trigger = compute(trailing_offset, basis)?;\n            new_trigger_price = maybe_move(&mut trigger_price, cand_trigger, better_trigger);\n\n            if order_type == OrderType::TrailingStopLimit {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/execution/src/trailing.rs#L110-L146","documentation":"For a `TrailingStopLimit` order with `TriggerType::LastPrice` or `MarkPrice`, the calculator needs a `limit_offset` to compute the new limit price from the last price. `OrderAny::limit_offset()` returned `None`, so the calculation aborts — a trailing-stop-limit order without a limit offset is incomplete and cannot be repriced correctly.","triggerScenarios":"Calling `trailing_stop_calculate` (or `update_trailing_stop_order`) with `order_type == TrailingStopLimit`, `trigger_type` of `LastPrice`/`MarkPrice`, and a `last` price present, while the order has no `limit_offset` set.","commonSituations":"Building a TrailingStopLimit order and setting only `trailing_offset` but not `limit_offset`; a venue adapter or strategy template that populates trailing-stop-market fields but not limit fields; deserialized orders from schema versions without `limit_offset`.","solutions":["Set `limit_offset` on the TrailingStopLimit order at construction time (same unit semantics governed by `trailing_offset_type`).","If the order should be a trailing stop market, change `order_type` to `TrailingStopMarket` so the limit-offset branch is not taken.","Guard before calling: `if order.order_type() == OrderType::TrailingStopLimit && order.limit_offset().is_none() { ... }` and repair or reject the order."],"exampleFix":"// before\nlet order = OrderAny::builder()\n    .order_type(OrderType::TrailingStopLimit)\n    .trailing_offset(Decimal::from(50))\n    .trailing_offset_type(TrailingOffsetType::Ticks)\n    .build()?;\n// after\nlet order = OrderAny::builder()\n    .order_type(OrderType::TrailingStopLimit)\n    .trailing_offset(Decimal::from(50))\n    .limit_offset(Decimal::from(10))\n    .trailing_offset_type(TrailingOffsetType::Ticks)\n    .build()?;","handlingStrategy":"validation","validationCode":"fn ensure_limit_offset(order: &OrderAny) -> Result<(), String> {\n    if order.order_type() == OrderType::TrailingStopLimit && order.limit_offset().is_none() {\n        return Err(format!(\n            \"TrailingStopLimit order {} is missing limit_offset\",\n            order.client_order_id()\n        ));\n    }\n    Ok(())\n}","typeGuard":"fn is_complete_trailing_stop_limit(order: &OrderAny) -> bool {\n    order.order_type() != OrderType::TrailingStopLimit || order.limit_offset().is_some()\n}","tryCatchPattern":null,"preventionTips":["Treat `limit_offset` as mandatory whenever the order type is TrailingStopLimit.","Centralize order construction in a factory that enforces the trailing-stop-limit field set.","Add a unit test asserting TrailingStopLimit orders always carry limit_offset before reaching trailing_stop_calculate."],"tags":["rust","trailing-stop","missing-field","trailing-stop-limit"],"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"}