{"record":{"id":"8a55bcbfa42e4644","repo":"nautechsystems/nautilus_trader","slug":"ax-requires-whole-contract-quantities-was","errorCode":null,"errorMessage":"AX requires whole contract quantities, was {}","messagePattern":"AX requires whole contract quantities, was (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/architect_ax/src/common/parse.rs","lineNumber":196,"sourceCode":"}\n\n/// Converts a [`Quantity`] to an i64 contract count for AX orders.\n///\n/// AX uses integer contracts only. Uses integer arithmetic to avoid\n/// floating-point precision issues.\n///\n/// # Errors\n///\n/// Returns an error if:\n/// - The quantity represents a fractional number of contracts.\n/// - The quantity is zero.\npub fn quantity_to_contracts(quantity: Quantity) -> anyhow::Result<u64> {\n    let raw = quantity.raw;\n    let scale = 10_u64.pow(FIXED_PRECISION as u32) as QuantityRaw;\n\n    // AX requires whole contract quantities\n    if !raw.is_multiple_of(scale) {\n        anyhow::bail!(\n            \"AX requires whole contract quantities, was {}\",\n            quantity.as_f64()\n        );\n    }\n\n    // QuantityRaw is u128 under the `high-precision` feature and u64 otherwise,\n    // so the narrowing cast is conditional on the active feature set.\n    #[allow(clippy::unnecessary_cast)]\n    let contracts = (raw / scale) as u64;\n    if contracts == 0 {\n        anyhow::bail!(\"Order quantity must be at least 1 contract\");\n    }\n    Ok(contracts)\n}\n\n/// Converts a [`ClientOrderId`] to a deterministic AX `cid` in the non-negative `int64` range.\n///\n/// Inbound WebSocket `cid` values remain `u64` because venue messages can exceed `int64`.","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/architect_ax/src/common/parse.rs#L178-L214","documentation":"`quantity_to_contracts` converts a Nautilus Quantity into a whole-contract u64 for AX. AX only accepts integer contract counts, so if the raw fixed-precision value is not a multiple of 10^FIXED_PRECISION the conversion bails. It is a pre-submit guard against sending fractional quantity to the venue.","triggerScenarios":"Calling submit_order or modify_order on AX with a quantity like 0.5, 1.25, or any value with nonzero digits below the fixed precision; also invoked directly by unit tests.","commonSituations":"Strategies sizing positions from percent-of-equity or notional math producing fractional quantities; crypto-style fractional sizing carried over to a futures venue.","solutions":["Round the order quantity down/up to a whole contract before submission (e.g. quantize to precision 0)","Adjust strategy position sizing to emit integer contract quantities","Verify the instrument's quantity_precision/size_increment is a whole number when configuring the strategy"],"exampleFix":"// before\nctx.submit_order(order_with_qty(Quantity::from(0.5)))\n// after\nlet qty = Quantity::new(order.quantity().as_f64().floor(), 0);\nctx.submit_order(order_with_qty(qty))","handlingStrategy":"validation","validationCode":"fn whole_contracts(q: Quantity, precision: u32) -> bool {\n    let scale = 10u64.pow(precision) as QuantityRaw;\n    q.raw % scale == 0\n}\nassert!(whole_contracts(order.quantity(), FIXED_PRECISION));","typeGuard":null,"tryCatchPattern":"match quantity_to_contracts(order.quantity()) {\n    Ok(contracts) => submit_with(contracts),\n    Err(e) => { log::warn!(\"skipping submit: {e}\"); /* round and resubmit or skip */ }\n}","preventionTips":["Quantize strategy sizes to the instrument's size_increment before creating orders","Round position targets to whole contracts for futures venues","Unit-test sizing functions to guarantee integer outputs"],"tags":["rust","adapter","quantity","validation","orders"],"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-23T08:17:48.524Z"}