nautechsystems/nautilus_trader · error

venue-leg fills cannot be represented exactly

Error message

venue-leg fills cannot be represented exactly

What it means

After computing the venue-leg filled Decimal, it is converted to a Quantity at the order's size_precision. If the value cannot be represented exactly at that precision (Quantity::from_decimal_dp rounds/fails), the subsequent equality check between the Quantity and the original Decimal fails, raising this error to avoid silently rounding fill data.

Source

Thrown at crates/adapters/polymarket/src/execution/reconciliation.rs:81

) -> anyhow::Result<(Quantity, Quantity)> {
    let mut filled = Decimal::ZERO;

    for event in order.events() {
        match event {
            OrderEventAny::Filled(event) if event.venue_order_id == venue_order_id => {
                filled += event.last_qty.as_decimal();
            }
            OrderEventAny::FillVoided(event) if event.venue_order_id == venue_order_id => {
                filled -= event.voided_qty.as_decimal();
            }
            _ => {}
        }
    }

    anyhow::ensure!(filled >= Decimal::ZERO, "venue-leg fills are negative");
    let current_leg_filled = Quantity::from_decimal_dp(filled, size_precision)
        .context("venue-leg fills exceed quantity precision")?;
    anyhow::ensure!(
        current_leg_filled.as_decimal() == filled,
        "venue-leg fills cannot be represented exactly"
    );
    let filled_before = order
        .filled_qty()
        .checked_sub(current_leg_filled)
        .context("current venue-leg fills exceed cumulative fills")?;
    let leg_quantity = order
        .quantity()
        .checked_sub(filled_before)
        .context("fills before current venue leg exceed logical quantity")?;
    Ok((filled_before, leg_quantity))
}

/// Shared context for trade-to-fill-report conversion.
pub(crate) struct FillContext<'a> {
    pub account_id: AccountId,
    pub user_address: &'a str,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Align the instrument's size_precision with the venue's actual fill size increments.
  2. Log the raw filled Decimal and compare with the order's size_precision to confirm the mismatch.
  3. Round venue fills to the instrument's tick size at ingestion so sums remain exactly representable.

Example fix

// before
let size_precision = instrument.size_precision(); // 2 dp
let qty = Quantity::from_decimal_dp(filled, size_precision)?; // filled has 4 dp
// after
let size_precision = 4; // match venue fill increments
let qty = Quantity::from_decimal_dp(filled, size_precision)?;
Defensive patterns

Strategy: validation

Validate before calling

let dp = filled.scale();
if dp as u32 > size_precision { return Err(anyhow!("fill precision {dp} exceeds instrument precision {size_precision}")); }

Type guard

fn fits_precision(d: Decimal, precision: u8) -> bool { (d.scale() as u32) <= precision as u32 }

Try / catch

match Quantity::from_decimal_dp(filled, size_precision) {
    Ok(q) => q,
    Err(_) => { log::warn!("rounding fill {filled} to precision {size_precision}"); Quantity::from_decimal_dp(filled.round_dp(size_precision), size_precision)? }
}

Prevention

When it happens

Trigger: Reconstructing fills (via load_orders_from_cache, query_order_command, generate_order_status_report(s)_impl) where summed event quantities carry more decimal places than the order's size_precision — e.g. partial fills with 4 dp against a 2 dp instrument definition.

Common situations: Instrument size_precision misconfigured vs the venue's actual fill increments; manual/odd lot fills from the venue exceeding the declared precision; stale instrument definitions after a venue change.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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