{"record":{"id":"9e0fdb63c392511b","repo":"nautechsystems/nautilus_trader","slug":"failed-to-parse-fee","errorCode":null,"errorMessage":"Failed to parse fee={:?}: {}","messagePattern":"Failed to parse fee=(.+?): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/okx/src/websocket/parse.rs","lineNumber":2161,"sourceCode":"            msg.fill_sz\n        );\n    };\n\n    let fee_str = msg\n        .fee\n        .as_deref()\n        .filter(|fee| !fee.trim().is_empty())\n        .ok_or_else(|| anyhow::anyhow!(\"missing fee for fill report inst_id={}\", msg.inst_id))?;\n    let fee_dec = Decimal::from_str(fee_str)\n        .map_err(|e| anyhow::anyhow!(\"Failed to parse fee '{fee_str}': {e}\"))?;\n\n    let fee_currency = parse_fee_currency(msg.fee_ccy.as_str(), fee_dec, || {\n        format!(\"fill report for inst_id={}\", msg.inst_id)\n    });\n\n    // OKX sends fees as negative numbers (e.g., \"-2.5\" for a $2.5 charge), parse_fee negates to positive\n    let total_fee = parse_fee(Some(fee_str), fee_currency)\n        .map_err(|e| anyhow::anyhow!(\"Failed to parse fee={:?}: {}\", msg.fee, e))?;\n\n    // OKX sends cumulative fees, so we subtract the previous total to get this fill's fee\n    let commission = if let Some(previous_fee) = previous_fee {\n        if total_fee.currency == previous_fee.currency {\n            let incremental = total_fee - previous_fee;\n\n            if incremental < Money::zero(fee_currency) {\n                log::debug!(\n                    \"Negative incremental fee detected - likely a maker rebate or fee refund: order_id={}, total_fee={}, previous_fee={}, incremental={}\",\n                    msg.ord_id.as_str(),\n                    total_fee,\n                    previous_fee,\n                    incremental,\n                );\n            }\n\n            // Skip corruption check when previous is negative (rebate), as transitions from\n            // rebate to charge legitimately have incremental > total (e.g., -1 → +2 gives +3)","sourceCodeStart":2143,"sourceCodeEnd":2179,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/websocket/parse.rs#L2143-L2179","documentation":"The adapter parses the fee string into a Money amount (parse_fee negates OKX's negative-sign convention so charges become positive). This error is thrown when parse_fee fails even though the raw Decimal conversion succeeded — typically the fee currency could not be resolved or the value is out of the allowed range for Money.","triggerScenarios":"parse_fill_report: parse_fee(Some(fee_str), fee_currency) returns Err — e.g. a precision/currency issue in the fee_currency derived from fee_ccy, or a value that cannot be represented as Money at the currency's precision.","commonSituations":"fee_ccy from OKX is a currency unknown to the local currency registry (new listing, unusual quote ccy); fee magnitude beyond Money precision; mismatch between the decimal precision of the fee and the currency's precision.","solutions":["Register the fee currency (fee_ccy) in the local currency/currency-registry used by parse_fee_currency and parse_fee.","Log msg.fee and msg.fee_ccy for the failing fill and compare against the currencies the adapter knows.","Update the adapter's currency mapping if OKX introduced new fee currencies.","Report upstream if the fee value is valid but Money construction still fails (precision bug in the adapter)."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Rust: ensure fee_ccy is a known currency before processing fills\nfn fee_currency_known(ccy: &str, registry: &CurrencyRegistry) -> bool {\n    registry.contains(ccy)\n}","typeGuard":"fn known_fee_ccy<'a>(msg: &'a OkxOrderMsg, registry: &CurrencyRegistry) -> Option<&'a str> {\n    registry.contains(&msg.fee_ccy).then_some(msg.fee_ccy.as_str())\n}","tryCatchPattern":"match parse_fill_report(&msg, ...) {\n    Ok(r) => emit(r),\n    Err(e) if e.to_string().contains(\"Failed to parse fee=\") => {\n        register_currency(&msg.fee_ccy); // then retry once\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Register every fee currency you may be charged in (quote ccys of traded instruments).","Keep the currency registry updated with OKX listings.","Log fee/fee_ccy pairs on failure to spot new currencies fast."],"tags":["okx","fee","currency","order-fill"],"backgroundTag":"invalid-argument-value","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"}