{"record":{"id":"fbd45deaa4631e6f","repo":"nautechsystems/nautilus_trader","slug":"missing-fee-for-spread-fill-report-sprd-id-okx","errorCode":null,"errorMessage":"missing fee for spread fill report sprd_id={}; OKX sprd-orders updates omit fee","messagePattern":"missing fee for spread fill report sprd_id=(.+?); OKX sprd-orders updates omit fee","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/okx/src/websocket/parse.rs","lineNumber":2044,"sourceCode":"\n            if (current_filled - prev_qty).is_zero() {\n                log::debug!(\n                    \"Skipping duplicate spread fill: acc_fill_sz='{}' unchanged from previous={}\",\n                    msg.acc_fill_sz,\n                    prev_qty\n                );\n                return Ok(None);\n            }\n        }\n    } else {\n        anyhow::bail!(\n            \"Cannot determine spread fill quantity: fill_sz='{}' and acc_fill_sz='{}'\",\n            msg.fill_sz,\n            msg.acc_fill_sz\n        );\n    }\n\n    anyhow::bail!(\n        \"missing fee for spread fill report sprd_id={}; OKX sprd-orders updates omit fee\",\n        msg.sprd_id\n    )\n}\n\n/// Parses an OKX order message into a Nautilus fill report.\n///\n/// # Errors\n///\n/// Returns an error if order quantities, prices, or fees cannot be parsed.\npub fn parse_fill_report(\n    msg: &OKXOrderMsg,\n    instrument: &InstrumentAny,\n    account_id: AccountId,\n    previous_fee: Option<Money>,\n    previous_filled_qty: Option<Quantity>,\n    ts_init: UnixNanos,\n) -> anyhow::Result<Option<FillReport>> {","sourceCodeStart":2026,"sourceCodeEnd":2062,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/websocket/parse.rs#L2026-L2062","documentation":"Fill reports require a commission/fee value, but OKX sprd-orders channel updates do not include a fee field. The parser deliberately bails to signal that a spread fill report cannot be built from this channel — fills with fees must come from another source (e.g. a fills channel or REST).","triggerScenarios":"Calling parse_spread_order_fill_report (via parse_spread_order_event / parse_spread_order_msg) on any spread fill — the sprd-orders update never carries a fee, so this always triggers once quantity parsing succeeds.","commonSituations":"Subscribing to sprd-orders expecting complete fill data; building fills from the order channel instead of the dedicated spread fills channel; fee-cache not populated for spread orders.","solutions":["Use the dedicated spread public/executes (fills) channel or REST spread fills endpoint, which includes fee data, to build fill reports","Pre-populate a fee cache keyed by sprd_id if the design supports it (as parse_fill_report does for orders)","Treat sprd-orders updates as order-status only and merge fees from a separate source"],"exampleFix":"// before\nlet fill = parse_spread_order_fill_report(&msg, prev_qty)?; // bails: missing fee\n// after\n// subscribe to the spread fills channel instead of deriving fills from sprd-orders\nws.subscribe(\"sprd-public-executes\", ...).await?;","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match parse_spread_order_fill_report(&msg, prev_qty) {\n    Err(e) if e.to_string().contains(\"missing fee for spread fill\") => {\n        // fetch fee via REST spread fills or the fills channel, then merge\n    }\n    other => other?,\n}","preventionTips":["Do not source spread fills with fees from sprd-orders; use the dedicated spread fills channel/REST endpoint","Maintain a fee cache keyed by sprd_id like the regular order fill flow"],"tags":["okx","websocket","spread","missing-field"],"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-14T00:17:10.932Z"}