nautechsystems/nautilus_trader · error
AX requires whole contract quantities, was
Error message
AX requires whole contract quantities, was {} What it means
`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.
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
Example fix
// before ctx.submit_order(order_with_qty(Quantity::from(0.5))) // after let qty = Quantity::new(order.quantity().as_f64().floor(), 0); ctx.submit_order(order_with_qty(qty))
Defensive patterns
Strategy: validation
Validate before calling
fn whole_contracts(q: Quantity, precision: u32) -> bool {
let scale = 10u64.pow(precision) as QuantityRaw;
q.raw % scale == 0
}
assert!(whole_contracts(order.quantity(), FIXED_PRECISION)); Try / catch
match quantity_to_contracts(order.quantity()) {
Ok(contracts) => submit_with(contracts),
Err(e) => { log::warn!("skipping submit: {e}"); /* round and resubmit or skip */ }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Strategies sizing positions from percent-of-equity or notional math producing fractional quantities; crypto-style fractional sizing carried over to a futures venue.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Order quantity must be at least 1 contract
- missing trigger price for Derive trigger order
- Unsupported order type
- closing quantity was zero
- Empty underlying for
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/8a55bcbfa42e4644.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/architect_ax/src/common/parse.rs:196
}
/// Converts a [`Quantity`] to an i64 contract count for AX orders.
///
/// AX uses integer contracts only. Uses integer arithmetic to avoid
/// floating-point precision issues.
///
/// # Errors
///
/// Returns an error if:
/// - The quantity represents a fractional number of contracts.
/// - The quantity is zero.
pub fn quantity_to_contracts(quantity: Quantity) -> anyhow::Result<u64> {
let raw = quantity.raw;
let scale = 10_u64.pow(FIXED_PRECISION as u32) as QuantityRaw;
// AX requires whole contract quantities
if !raw.is_multiple_of(scale) {
anyhow::bail!(
"AX requires whole contract quantities, was {}",
quantity.as_f64()
);
}
// QuantityRaw is u128 under the `high-precision` feature and u64 otherwise,
// so the narrowing cast is conditional on the active feature set.
#[allow(clippy::unnecessary_cast)]
let contracts = (raw / scale) as u64;
if contracts == 0 {
anyhow::bail!("Order quantity must be at least 1 contract");
}
Ok(contracts)
}
/// Converts a [`ClientOrderId`] to a deterministic AX `cid` in the non-negative `int64` range.
///
/// Inbound WebSocket `cid` values remain `u64` because venue messages can exceed `int64`.View on GitHub (pinned to 18893faf8b)