{"record":{"id":"687e2811cc2337ac","repo":"nautechsystems/nautilus_trader","slug":"failed-to-construct-initial-margin-e","errorCode":null,"errorMessage":"failed to construct initial margin: {e}","messagePattern":"failed to construct initial margin: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/websocket/parse.rs","lineNumber":1143,"sourceCode":"/// # Errors\n///\n/// Returns an error if either `Money::from_decimal` call rejects the value.\npub fn margin_balance_from_user_stats(stats: &LighterUserStats) -> anyhow::Result<MarginBalance> {\n    margin_balance_from_user_stats_with_currency(stats, Currency::get_or_create_crypto(\"USDC\"))\n}\n\n/// Builds the cross-margin [`MarginBalance`] in the supplied settlement currency.\n///\n/// # Errors\n///\n/// Returns an error if either `Money::from_decimal` call rejects the value.\npub fn margin_balance_from_user_stats_with_currency(\n    stats: &LighterUserStats,\n    settlement_currency: Currency,\n) -> anyhow::Result<MarginBalance> {\n    let initial_dec = (stats.collateral - stats.available_balance).max(Decimal::ZERO);\n    let initial = Money::from_decimal(initial_dec, settlement_currency)\n        .map_err(|e| anyhow::anyhow!(\"failed to construct initial margin: {e}\"))?;\n    let maintenance = Money::from_decimal(Decimal::ZERO, settlement_currency)\n        .map_err(|e| anyhow::anyhow!(\"failed to construct maintenance margin: {e}\"))?;\n    Ok(MarginBalance::new(initial, maintenance, None))\n}\n\n/// Assembles the unified [`AccountState`] from already-parsed components.\n///\n/// The reconciler in the `websocket::account_state` module owns the latest\n/// snapshot of each input stream and calls this once per emission.\n/// `AccountType::Margin` is invariant for Lighter; `base_currency` is `None`\n/// because the account holds multiple spot currencies.\n#[must_use]\npub fn build_unified_account_state(\n    balances: Vec<AccountBalance>,\n    margin: Option<MarginBalance>,\n    account_id: AccountId,\n    ts_event: UnixNanos,\n    ts_init: UnixNanos,","sourceCodeStart":1125,"sourceCodeEnd":1161,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/websocket/parse.rs#L1125-L1161","documentation":"margin_balance_from_user_stats_with_currency derives the initial margin as collateral minus available balance (floored at zero) and wraps it in a Money value in the settlement currency. Money::from_decimal rejects decimals whose precision exceeds the currency's precision, so if the Lighter user-stats values carry more decimal places than the settlement currency supports (e.g. more than 6 dp for USDC), the conversion fails and this error is raised.","triggerScenarios":"A Lighter account-stats websocket snapshot has collateral or available_balance with a decimal precision greater than the settlement currency's configured precision, making Money::from_decimal(initial_dec, settlement_currency) fail.","commonSituations":"Using a settlement Currency whose precision was configured too low (e.g. precision 2) while Lighter reports 6+ decimal places; a new settlement asset added on Lighter with a precision not matching the local Currency catalog; raw exchange values with unexpected extra decimals after a API change.","solutions":["Round the computed decimal to the settlement currency's precision before conversion: initial_dec = initial_dec.round_dp(settlement_currency.precision).","Verify the settlement Currency instance used for the account matches Lighter's actual asset precision (e.g. USDC at 6 dp).","Log stats.collateral, stats.available_balance and the currency precision when this fires to confirm which side has excess precision.","If the currency is newly listed, register it with the correct precision instead of reusing a generic Currency::USD placeholder."],"exampleFix":"// before\nlet initial_dec = (stats.collateral - stats.available_balance).max(Decimal::ZERO);\nlet initial = Money::from_decimal(initial_dec, settlement_currency)\n    .map_err(|e| anyhow::anyhow!(\"failed to construct initial margin: {e}\"))?;\n\n// after\nlet initial_dec = ((stats.collateral - stats.available_balance).max(Decimal::ZERO))\n    .round_dp(settlement_currency.precision);\nlet initial = Money::from_decimal(initial_dec, settlement_currency)\n    .map_err(|e| anyhow::anyhow!(\"failed to construct initial margin: {e}\"))?;","handlingStrategy":"validation","validationCode":"if initial_dec.scale() > settlement_currency.precision as u32 {\n    initial_dec = initial_dec.round_dp(settlement_currency.precision as u32);\n}","typeGuard":null,"tryCatchPattern":"match margin_balance_from_user_stats_with_currency(&stats, currency) {\n    Ok(mb) => apply(mb),\n    Err(e) => log::error!(\"margin conversion failed, check currency precision: {e}\"),\n}","preventionTips":["Configure settlement currencies with the same precision the exchange reports (e.g. USDC at 6 dp).","Round derived decimals to currency precision before any Money conversion.","Fail fast at account setup if the currency precision is implausible."],"tags":["rust","account-state","margin","precision"],"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"}