{"record":{"id":"b5d6c1b958ff102e","repo":"nautechsystems/nautilus_trader","slug":"polymarket-collateral-sized-limit-buy-amount-pu-b5d6c1","errorCode":null,"errorMessage":"Polymarket collateral-sized limit BUY amount {} pUSD cannot preserve limit price {} after venue quantization","messagePattern":"Polymarket collateral-sized limit BUY amount (.+?) pUSD cannot preserve limit price (.+?) after venue quantization","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/execution/order_builder.rs","lineNumber":172,"sourceCode":"        tick_decimals: u32,\n    ) -> anyhow::Result<PolymarketOrder> {\n        anyhow::ensure!(\n            price > Decimal::ZERO,\n            \"Polymarket collateral-sized limit BUY price must be positive\"\n        );\n\n        let (maker_amount, taker_amount) =\n            compute_quote_buy_maker_taker_amounts(price, amount, tick_decimals);\n        anyhow::ensure!(\n            maker_amount > Decimal::ZERO,\n            \"Polymarket collateral-sized limit BUY amount {} pUSD truncates to zero at {LOT_SIZE_SCALE} decimal places\",\n            amount.normalize(),\n        );\n        anyhow::ensure!(\n            taker_amount > Decimal::ZERO,\n            \"Polymarket collateral-sized limit BUY derives a zero share quantity\"\n        );\n        anyhow::ensure!(\n            taker_amount * price == maker_amount,\n            \"Polymarket collateral-sized limit BUY amount {} pUSD cannot preserve limit price {} after venue quantization\",\n            amount.normalize(),\n            price.normalize(),\n        );\n\n        self.build_and_sign(\n            token_id,\n            PolymarketOrderSide::Buy,\n            maker_amount,\n            taker_amount,\n            expiration,\n            neg_risk,\n        )\n    }\n\n    /// Builds and signs a market order for submission.\n    ///","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/execution/order_builder.rs#L154-L190","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Adjust the collateral amount so that after quantization, taker_amount * price equals maker_amount exactly (quantize amount to price-compatible increments).","Slightly round the amount up/down to the nearest value that preserves the limit price post-quantization before calling build_limit_order_from_collateral.","Confirm price and tick_decimals match the venue market; a wrong tick_decimals changes the quantization grid."],"exampleFix":"// before\nlet amount = Decimal::new(10037, 2); // 100.37 pUSD at price 0.333 -> quantization breaks invariant\n// after\nlet amount = (price * shares_wanted).round_dp(LOT_SIZE_SCALE); // choose amount exactly = price * quantized shares","handlingStrategy":"validation","validationCode":"let maker = (amount * price_unit_factor).round_dp(lot_size_scale);\nlet taker = (amount / price).round_dp(lot_size_scale);\nif taker * price != maker { /* re-quantize amount */ }","typeGuard":"fn preserves_limit_price(amount: Decimal, price: Decimal, dp: u32) -> bool {\n    let maker = (amount).round_dp(dp);\n    let taker = (amount / price).round_dp(dp);\n    taker * price == maker\n}","tryCatchPattern":null,"preventionTips":["Derive amount as price * quantized_shares instead of an arbitrary decimal notional.","Re-quantize the amount to a price-compatible increment before building.","Test with prices that have non-terminating decimal expansions (e.g. 0.333)."],"tags":["polymarket","invariant","quantization","decimal-precision"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}