nautechsystems/nautilus_trader · error

quantity `{new_qty}` rounds to 0 ticks at size_precision {}

Error message

quantity `{new_qty}` rounds to 0 ticks at size_precision {}

What it means

The modify's new quantity is converted to integer base-amount ticks at the instrument's size_precision. If the resulting tick value is 0 (quantity too small or below rounding granularity) the order would be invalid on Lighter, so the adapter rejects it up front.

Source

Thrown at crates/adapters/lighter/src/execution.rs:1980

                derive_market_order_price_ticks(
                    trigger.as_decimal(),
                    is_buy,
                    price_precision,
                    slippage_bps,
                )?
            }
            _ => {
                let new_price = cmd.price.or(order.price()).ok_or_else(|| {
                    anyhow::anyhow!("modify_order requires a price (none on order or command)")
                })?;

                price_to_ticks(&new_price, price_precision)?
            }
        };

        let base_amount = quantity_to_ticks(&new_qty, instrument.size_precision())?;
        anyhow::ensure!(
            base_amount > 0,
            "quantity `{new_qty}` rounds to 0 ticks at size_precision {}",
            instrument.size_precision(),
        );
        let trigger_price_ticks = match new_trigger {
            Some(trigger) if trigger.raw != 0 => price_to_ticks(&trigger, price_precision)?,
            _ => 0,
        };

        if matches!(
            order.order_type(),
            OrderType::StopMarket
                | OrderType::StopLimit
                | OrderType::MarketIfTouched
                | OrderType::LimitIfTouched
        ) {
            anyhow::ensure!(
                trigger_price_ticks > 0,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Increase the new quantity so it is at least one tick at the instrument's size_precision.
  2. Check the instrument definition's size_precision and size_increment; align strategy quantities to whole ticks.
  3. If the intent is to fully close, cancel the order instead of modifying quantity to ~0.

Example fix

// before
let new_qty = Quantity::from(0.0004); // rounds to 0 ticks at precision 2
// after
let new_qty = Quantity::new(Decimal::new(1, 2), 2); // 0.01 = 1 tick
Defensive patterns

Strategy: validation

Validate before calling

let ticks = (new_qty.as_decimal() * Decimal::from(10u64.pow(instrument.size_precision() as u32)))
    .round()
    .to_u64().unwrap_or(0);
if ticks == 0 { return Err("quantity below one tick".into()); }

Try / catch

match client.modify_order(&cmd).await {
    Err(e) if e.to_string().contains("rounds to 0 ticks") => cancel_order(&cmd.client_order_id).await?,
    r => r?,
}

Prevention

When it happens

Trigger: prepare_signed_modify_order computes quantity_to_ticks(&new_qty, instrument.size_precision()) == 0 — the new quantity from cmd.quantity (or the order) rounds down to zero ticks, e.g. 0.0004 BTC at size_precision 2.

Common situations: Configured size_precision smaller than the quantity's decimals; very small reduce-only adjustments; position sizing computed in a different instrument's units; truncation from float-to-Decimal conversion losing precision.

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/f8ecd81fa9286d33. Report an issue: GitHub.