nautechsystems/nautilus_trader · error

Polymarket collateral-sized limit BUY amount {} pUSD cannot

Error message

Polymarket collateral-sized limit BUY amount {} pUSD cannot preserve limit price {} after venue quantization

What it means

After the venue quantizes maker_amount and taker_amount to LOT_SIZE_SCALE decimals, the invariant taker_amount * price == maker_amount must still hold exactly, otherwise the effective fill price would silently differ from the requested limit price. The builder throws when quantization breaks this equality.

Source

Thrown at crates/adapters/polymarket/src/execution/order_builder.rs:172

        tick_decimals: u32,
    ) -> anyhow::Result<PolymarketOrder> {
        anyhow::ensure!(
            price > Decimal::ZERO,
            "Polymarket collateral-sized limit BUY price must be positive"
        );

        let (maker_amount, taker_amount) =
            compute_quote_buy_maker_taker_amounts(price, amount, tick_decimals);
        anyhow::ensure!(
            maker_amount > Decimal::ZERO,
            "Polymarket collateral-sized limit BUY amount {} pUSD truncates to zero at {LOT_SIZE_SCALE} decimal places",
            amount.normalize(),
        );
        anyhow::ensure!(
            taker_amount > Decimal::ZERO,
            "Polymarket collateral-sized limit BUY derives a zero share quantity"
        );
        anyhow::ensure!(
            taker_amount * price == maker_amount,
            "Polymarket collateral-sized limit BUY amount {} pUSD cannot preserve limit price {} after venue quantization",
            amount.normalize(),
            price.normalize(),
        );

        self.build_and_sign(
            token_id,
            PolymarketOrderSide::Buy,
            maker_amount,
            taker_amount,
            expiration,
            neg_risk,
        )
    }

    /// Builds and signs a market order for submission.
    ///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Adjust the collateral amount so that after quantization, taker_amount * price equals maker_amount exactly (quantize amount to price-compatible increments).
  2. Slightly round the amount up/down to the nearest value that preserves the limit price post-quantization before calling build_limit_order_from_collateral.
  3. Confirm price and tick_decimals match the venue market; a wrong tick_decimals changes the quantization grid.

Example fix

// before
let amount = Decimal::new(10037, 2); // 100.37 pUSD at price 0.333 -> quantization breaks invariant
// after
let amount = (price * shares_wanted).round_dp(LOT_SIZE_SCALE); // choose amount exactly = price * quantized shares
Defensive patterns

Strategy: validation

Validate before calling

let maker = (amount * price_unit_factor).round_dp(lot_size_scale);
let taker = (amount / price).round_dp(lot_size_scale);
if taker * price != maker { /* re-quantize amount */ }

Type guard

fn preserves_limit_price(amount: Decimal, price: Decimal, dp: u32) -> bool {
    let maker = (amount).round_dp(dp);
    let taker = (amount / price).round_dp(dp);
    taker * price == maker
}

Prevention

When it happens

Trigger: build_limit_order_from_collateral with an amount/price combination whose rounded maker and taker amounts no longer multiply back to the original price — typical when amount is not an exact multiple of price at the venue's quantization.

Common situations: Collateral budgets that are arbitrary decimals (e.g. 100.37 pUSD) combined with prices like 0.333; tests asserting exact price preservation; venue tick-decimal changes making previously valid amounts invalid.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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