{"record":{"id":"a3bc1a77d748c35e","repo":"nautechsystems/nautilus_trader","slug":"quantity-for-currency-loses-precision-when-decre","errorCode":null,"errorMessage":"quantity for {currency} loses precision when decreasing raw scale","messagePattern":"quantity for (.+?) loses precision when decreasing raw scale","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/portfolio/src/manager.rs","lineNumber":1432,"sourceCode":"fn wallet_money_from_quantity(quantity: Quantity, currency: Currency) -> anyhow::Result<Money> {\n    anyhow::ensure!(!quantity.is_undefined(), \"quantity was undefined\");\n    Quantity::from_raw_checked(quantity.raw, quantity.precision)?;\n    check_fixed_raw_u128(u128::from(quantity.raw), quantity.precision)?;\n\n    let source_precision = quantity.precision.max(FIXED_PRECISION);\n    let target_precision = currency.precision.max(FIXED_PRECISION);\n    let raw = i128::try_from(u128::from(quantity.raw))\n        .map_err(|_| anyhow::anyhow!(\"quantity for {currency} exceeds signed raw bounds\"))?;\n    let raw = match source_precision.cmp(&target_precision) {\n        Ordering::Less => {\n            let scale = 10_i128.pow(u32::from(target_precision - source_precision));\n            raw.checked_mul(scale).ok_or_else(|| {\n                anyhow::anyhow!(\"quantity for {currency} overflowed while increasing raw scale\")\n            })?\n        }\n        Ordering::Greater => {\n            let scale = 10_i128.pow(u32::from(source_precision - target_precision));\n            anyhow::ensure!(\n                raw % scale == 0,\n                \"quantity for {currency} loses precision when decreasing raw scale\"\n            );\n            raw / scale\n        }\n        Ordering::Equal => raw,\n    };\n    check_fixed_raw_i128(raw, currency.precision)?;\n    let raw: MoneyRaw = raw\n        .try_into()\n        .map_err(|_| anyhow::anyhow!(\"quantity for {currency} exceeds Money raw bounds\"))?;\n\n    Money::from_raw_checked(raw, currency).map_err(Into::into)\n}\n\nfn reservation_precisions_match(\n    account: &dyn Account,\n    reservations: &AHashMap<Currency, Money>,","sourceCodeStart":1414,"sourceCodeEnd":1450,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/portfolio/src/manager.rs#L1414-L1450","documentation":"When the quantity's source precision is higher than the target precision, the raw value must be divided down by 10^(source-target). If the raw value is not exactly divisible, the conversion would lose precision, so ensure! raises this error rather than truncating silently — preserving exact discrete values.","triggerScenarios":"Converting a Quantity with more decimal places than the target currency's (or FIXED) precision, where the raw value has non-zero digits in the dropped positions — e.g. quantity precision 8 converted to currency precision 2 with raw not a multiple of 10^6.","commonSituations":"Trading instruments quoted with more precision than the settlement currency supports; misconfigured currency precision; adapters reporting fills with extra precision beyond the instrument's spec.","solutions":["Round/quantize the quantity to the currency precision before calling the wallet update","Align the currency's precision configuration with the instrument quote precision","Use the instrument's size/price precision when constructing Quantity values","If truncation is acceptable for your use case, explicitly round before the call instead of relying on the manager"],"exampleFix":"// before\nlet qty = Quantity::new(0.123456789, 9); // currency precision 2\nmanager.update_balance_locked_wallet(&instrument.id(), asset, qty)?;\n// after\nlet qty = Quantity::new(qty.as_f64(), currency.precision.max(FIXED_PRECISION)); // pre-rounded\nmanager.update_balance_locked_wallet(&instrument.id(), asset, qty)?;","handlingStrategy":"validation","validationCode":"let target_p = currency.precision.max(FIXED_PRECISION);\nif quantity.precision > target_p {\n    let scale = 10_u64.pow((quantity.precision - target_p) as u32);\n    anyhow::ensure!(quantity.raw % scale == 0, \"quantity {} not representable at precision {target_p}\", quantity);\n}","typeGuard":null,"tryCatchPattern":"manager.update_balance_locked_wallet(&id, currency, quantity)\n    .unwrap_or_else(|e| { warn!(\"precision loss for {currency}: {e}; rounding quantity\"); });","preventionTips":["Round quantities to the currency/FIXED precision before wallet updates","Configure currency precision to cover the instrument's quote precision","Clamp adapter-reported fill precision to the instrument specification"],"tags":["rust","portfolio","precision","rounding"],"backgroundTag":"value-out-of-range","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"}