{"record":{"id":"2284a3ee699f3fcb","repo":"nautechsystems/nautilus_trader","slug":"position-commission-currency-differs-for-fill","errorCode":null,"errorMessage":"position commission currency differs for fill {}","messagePattern":"position commission currency differs for fill (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/execution/src/engine/mod.rs","lineNumber":3942,"sourceCode":"                .and_modify(|allocation| allocation.0 = allocation.0 + removed)\n                .or_insert((removed, None));\n            remaining_qty = remaining_qty - removed;\n        }\n        anyhow::ensure!(\n            remaining_qty.is_zero(),\n            \"position fragments do not cover voided quantity for fill {}\",\n            event.trade_id\n        );\n\n        if let Some(mut remaining_commission) = event.commission_voided {\n            for (position_id, _, _, commission) in fragments.iter().rev() {\n                if remaining_commission.is_zero() {\n                    break;\n                }\n                let Some(commission) = commission else {\n                    continue;\n                };\n                anyhow::ensure!(\n                    commission.currency == remaining_commission.currency,\n                    \"position commission currency differs for fill {}\",\n                    event.trade_id\n                );\n                let removed_raw = remaining_commission.raw.abs().min(commission.raw.abs());\n                let removed = Money::from_raw(\n                    removed_raw * remaining_commission.raw.signum(),\n                    remaining_commission.currency,\n                );\n                allocations\n                    .entry(*position_id)\n                    .and_modify(|allocation| {\n                        allocation.1 = Some(\n                            allocation\n                                .1\n                                .map_or(removed, |commission| commission + removed),\n                        );\n                    })","sourceCodeStart":3924,"sourceCodeEnd":3960,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/execution/src/engine/mod.rs#L3924-L3960","documentation":"While distributing a voided commission across the positions touched by a fill, the engine requires that every position fragment's recorded commission is denominated in the same currency as the commission being voided. If a fragment's commission currency differs from remaining_commission.currency, exact Money arithmetic would be undefined across currencies, so the engine raises this error.","triggerScenarios":"Applying a fill void whose commission_voided currency does not equal the currency of the commission recorded on one of the positions involved — e.g. commissions were booked in quote currency but the void event carries base-currency commission, or multi-currency instruments with mixed commission currencies per fill.","commonSituations":"Configuring a strategy's commission currency inconsistently between backtest and live; adapters reporting commissions in different currencies across partial fills; changing instrument or account currency config between runs while replaying cached events.","solutions":["Ensure the void event's commission currency matches the currency used when the original fill's commission was booked.","Check strategy/instrument configuration so commission_currency is consistent for all fills of the instrument.","If commissions genuinely span currencies, split the void per currency and apply separate void events per currency.","Log each position's recorded commission currency at fill time and compare with the void event to locate the mismatched fragment."],"exampleFix":"// before: voiding a USD commission against a fill booked in USD fee\nlet voided = Money::new(2.5, Currency::USD());\nengine.apply_fill_void(FillVoided { commission_voided: Some(voided), .. })?;\n// after: match the currency of the recorded commission\nlet voided = Money::new(2.5, recorded_fill.commission.currency);\nengine.apply_fill_void(FillVoided { commission_voided: Some(voided), .. })?;","handlingStrategy":"validation","validationCode":"// ensure the void commission currency matches the fill's recorded commission\nif let Some(voided) = &event.commission_voided {\n    assert_eq!(voided.currency, recorded_fill(event.trade_id).commission.currency,\n        \"void commission currency differs from booked commission\");\n}","typeGuard":null,"tryCatchPattern":"match engine.apply_fill_void(event) {\n    Err(e) if e.to_string().contains(\"commission currency differs\") => {\n        log::error!(\"currency mismatch voiding fill {}: {e}\", event.trade_id);\n        // re-emit the void with the fill's booked commission currency\n    }\n    other => other?,\n}","preventionTips":["Configure one commission currency per instrument and use it for every fill and void.","Carry commission currency end-to-end in adapters instead of assuming account currency.","Never hard-code currencies in strategy fee calculations."],"tags":["execution-engine","commission","currency-mismatch","money-arithmetic"],"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-14T05:17:10.506Z"}