nautechsystems/nautilus_trader · error

price decimals {decimals} exceeds maximum {MAX_DECIMALS}

Error message

price decimals {decimals} exceeds maximum {MAX_DECIMALS}

What it means

`parse_price_from_ticks` converts an integer tick value and a decimal count into a Nautilus `Price`. It rejects any `decimals` value above `MAX_DECIMALS` (the precision limit `Price::from_mantissa_exponent` supports), because the resulting price precision would be unrepresentable.

Source

Thrown at crates/adapters/lighter/src/common/parse.rs:46

    datetime::{NANOSECONDS_IN_MILLISECOND, NANOSECONDS_IN_SECOND},
};
use nautilus_model::types::{Price, Quantity, fixed::FIXED_PRECISION};
use rust_decimal::Decimal;

/// Maximum decimal places that fit into Nautilus [`Price`] / [`Quantity`].
pub const MAX_DECIMALS: u8 = FIXED_PRECISION;

/// Converts a Lighter price tick count into a Nautilus [`Price`].
///
/// Lighter encodes limit and trigger prices as `u32` multiples of `10^-decimals`
/// quote-asset units. The conversion uses pure integer arithmetic via
/// [`Price::from_mantissa_exponent`].
///
/// # Errors
///
/// Returns an error if `decimals` exceeds [`MAX_DECIMALS`].
pub fn parse_price_from_ticks(ticks: u32, decimals: u8) -> anyhow::Result<Price> {
    anyhow::ensure!(
        decimals <= MAX_DECIMALS,
        "price decimals {decimals} exceeds maximum {MAX_DECIMALS}",
    );
    let exponent = -(decimals as i8);
    Ok(Price::from_mantissa_exponent(
        i64::from(ticks),
        exponent,
        decimals,
    ))
}

/// Converts a Lighter base-amount tick count into a Nautilus [`Quantity`].
///
/// Order sizes on the wire are signed `i64` multiples of `10^-decimals`
/// base-asset units. Nautilus [`Quantity`] is non-negative, so a negative
/// `ticks` value is rejected: callers (e.g. position parsers) extract the
/// sign separately before invoking this parser.
///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the market's price precision and ensure it is <= MAX_DECIMALS before parsing.
  2. Round or scale the tick value to a supported precision if the source data has excess decimals.
  3. Inspect `MAX_DECIMALS` in crates/adapters/lighter/src/common/parse.rs to know the allowed limit.

Example fix

// before
let price = parse_price_from_ticks(ticks, 20)?;
// after
let decimals = decimals.min(MAX_DECIMALS);
let price = parse_price_from_ticks(ticks, decimals)?;
Defensive patterns

Strategy: validation

Validate before calling

if decimals > lighter::parse::MAX_DECIMALS {
    decimals = lighter::parse::MAX_DECIMALS; // or reject the instrument
}

Type guard

fn price_precision_supported(d: u8) -> bool { d <= lighter::parse::MAX_DECIMALS }

Try / catch

let price = parse_price_from_ticks(ticks, decimals)
    .map_err(|e| { eprintln!("price parse failed: {e}"); e })?;

Prevention

When it happens

Trigger: Calling `parse_price_from_ticks(ticks, decimals)` with `decimals > MAX_DECIMALS`, e.g. when parsing an instrument definition whose price precision exceeds the adapter's maximum.

Common situations: Loading a Lighter instrument with unusually high price precision; misreading a market's quoted decimals field; hardcoding decimals from a different market.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/b315389ede9ed9c0. Report an issue: GitHub.