nautechsystems/nautilus_trader · error
Failed to create quantity from
Error message
Failed to create quantity from {quantity_dec}: {e} What it means
parse_spot_margin_position_from_balance converts the OKX spot-in-use balance into a Nautilus quantity. Quantity::from_decimal_dp fails when the spotInUseAmt decimal cannot be represented at the instrument's size precision (wrong precision or malformed value), and the error is wrapped with the quantity value.
Solutions
- Check that size_precision passed to the function matches the instrument's size increment from the OKX instruments endpoint
- Log the raw spotInUseAmt and precision; verify the value has <= size_precision decimal places
- Re-fetch instrument definitions so precision metadata is current
- Round/normalize the quantity to size_precision before calling Quantity::from_decimal_dp
Example fix
// before
let quantity = Quantity::from_decimal_dp(quantity_dec, size_precision)
.map_err(|e| anyhow::anyhow!("Failed to create quantity from {quantity_dec}: {e}"))?;
// after
let quantity_dec = quantity_dec.round_dp(size_precision as u32);
let quantity = Quantity::from_decimal_dp(quantity_dec, size_precision)
.map_err(|e| anyhow::anyhow!("Failed to create quantity from {quantity_dec}: {e}"))?; Defensive patterns
Strategy: validation
Validate before calling
// Rust: check precision fits before calling
fn quantity_fits_precision(dec: rust_decimal::Decimal, precision: u8) -> bool {
dec.round_dp(precision as u32) == dec
}
assert!(quantity_fits_precision(quantity_dec, size_precision), "quantity exceeds size precision"); Try / catch
match parse_spot_margin_position_from_balance(&balance, account_id, instrument_id, size_precision, ts_init) {
Ok(report) => report,
Err(e) => { tracing::error!("position quantity rejected: {e:#}"); return; }
} Prevention
- Always derive size_precision from the fetched OKX instrument definition, never hardcode
- Re-sync instrument metadata when OKX changes lot size rules
- Log raw balance payloads on parse failure for diagnosis
When it happens
Trigger: Calling parse_spot_margin_position_from_balance with an OKX balance-and-position payload whose spotInUseAmt has more decimal places than size_precision, or a non-representable value (e.g. scientific notation, negative, or a wrong precision passed by the caller).
Common situations: Instrument definitions loaded with an incorrect size_precision for the trading pair; stale instrument metadata after an OKX lot-size change; spot-margin liability amounts with unexpected precision from the account/balance endpoint.
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
- Failed to convert quote-to-base quantity for ord_id=
- Failed to parse base quantity for ord_id=
- Failed to parse `ct_mult
- Failed to parse `ct_val
- Failed to parse fallback quantity for ord_id=
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b0ff56d00646c199.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/okx/src/common/parse.rs:999
}
// Check if spotInUseAmt is zero first
if spot_in_use_dec.is_zero() {
// No position if spotInUseAmt is zero (regardless of liability)
return Ok(None);
}
// Position side based on spotInUseAmt sign
let (position_side, quantity_dec) = if spot_in_use_dec.is_sign_negative() {
// Negative spotInUseAmt = sold (short position)
(PositionSide::Short, spot_in_use_dec.abs())
} else {
// Positive spotInUseAmt = bought (long position)
(PositionSide::Long, spot_in_use_dec)
};
let quantity = Quantity::from_decimal_dp(quantity_dec, size_precision)
.map_err(|e| anyhow::anyhow!("Failed to create quantity from {quantity_dec}: {e}"))?;
let ts_last = parse_millisecond_timestamp(balance.u_time);
Ok(Some(PositionStatusReport::new(
account_id,
instrument_id,
position_side,
quantity,
ts_last,
ts_init,
None, // report_id
None, // venue_position_id is None for net mode margin positions
None, // avg_px_open not available from balance
)))
}
/// Parses an OKX position into a Nautilus [`PositionStatusReport`].
///View on GitHub (pinned to 18893faf8b)