{"record":{"id":"fc3525fed7a18fb4","repo":"nautechsystems/nautilus_trader","slug":"polymarket-market-buy-amount-pusd-truncates-to","errorCode":null,"errorMessage":"Polymarket market BUY amount {} pUSD truncates to zero at {LOT_SIZE_SCALE} decimal places","messagePattern":"Polymarket market BUY amount (.+?) pUSD truncates to zero at (.+?) decimal places","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/execution/order_builder.rs","lineNumber":206,"sourceCode":"\n    /// Builds and signs a market order for submission.\n    ///\n    /// `amount` semantics differ by side:\n    /// - BUY: `amount` is pUSD to spend\n    /// - SELL: `amount` is shares to sell\n    ///\n    /// Market orders never set an expiration.\n    pub fn build_market_order(\n        &self,\n        token_id: &str,\n        side: PolymarketOrderSide,\n        price: Decimal,\n        amount: Decimal,\n        neg_risk: bool,\n        tick_decimals: u32,\n    ) -> anyhow::Result<PolymarketOrder> {\n        if side == PolymarketOrderSide::Buy && amount.trunc_with_scale(LOT_SIZE_SCALE).is_zero() {\n            anyhow::bail!(\n                \"Polymarket market BUY amount {} pUSD truncates to zero at {LOT_SIZE_SCALE} decimal places\",\n                amount.normalize(),\n            );\n        }\n\n        let (maker_amount, taker_amount) =\n            compute_market_maker_taker_amounts(price, amount, side, tick_decimals);\n        self.build_and_sign(token_id, side, maker_amount, taker_amount, \"0\", neg_risk)\n    }\n\n    /// Computes the Polymarket order ID for a signed CLOB V2 order.\n    pub fn expected_order_id(\n        &self,\n        order: &PolymarketOrder,\n        neg_risk: bool,\n    ) -> anyhow::Result<VenueOrderId> {\n        let hash = order_hash(order, neg_risk)\n            .map_err(|e| anyhow::anyhow!(\"Failed to derive order hash: {e}\"))?;","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/execution/order_builder.rs#L188-L224","documentation":"For market BUY orders on Polymarket, amount is denominated in pUSD; it must round to at least one lot (0.01 pUSD) when truncated to LOT_SIZE_SCALE. build_market_order rejects amounts that truncate to zero, because a zero maker amount would produce an invalid order. This protects against tiny/dust buy amounts being submitted to the CLOB.","triggerScenarios":"Calling build_market_order (via submit_market_order) with side=Buy and an amount in pUSD whose value truncated to 2 decimal places equals zero (e.g. 0.004 pUSD).","commonSituations":"Strategy computes a position size smaller than the minimum notional; rounding or fee-adjustment shrinks the order amount below 0.01; unit tests with sub-lot amounts; currency conversion leaving fractional dust.","solutions":["Increase the market BUY amount to at least one lot (0.01 pUSD, practically the exchange minimum)","Clamp or skip orders whose computed amount is below the minimum notional before calling submit","Check upstream sizing/rounding logic that produced the sub-lot amount","If amounts are intentionally tiny, route them through limit orders or batch them"],"exampleFix":"// before\nlet amount = expected_notional * (1.0 - fee_buffer); // may fall below 0.01\nclient.submit_market_order(instrument, OrderSide::Buy, amount, ...).await?;\n// after\nlet amount = expected_notional * (1.0 - fee_buffer);\nif amount.trunc_with_scale(2) >= Decimal::from_str_exact(\"0.01\")? {\n    client.submit_market_order(instrument, OrderSide::Buy, amount, ...).await?;\n} else {\n    log::debug!(\"skip market BUY: amount {amount} below minimum lot\");\n}","handlingStrategy":"validation","validationCode":"use rust_decimal::Decimal;\nconst LOT_SIZE_SCALE: u32 = 2;\n\nfn market_buy_amount_ok(amount: Decimal) -> bool {\n    !amount.trunc_with_scale(LOT_SIZE_SCALE).is_zero()\n}\n\n// call before submit\nif !market_buy_amount_ok(amount) {\n    // skip or bump the order size\n}","typeGuard":null,"tryCatchPattern":"match client.submit_market_order(instrument, OrderSide::Buy, amount, ...).await {\n    Err(e) if e.to_string().contains(\"truncates to zero\") => {\n        warn!(\"market BUY amount {amount} below minimum lot; order skipped\");\n    }\n    Err(e) => return Err(e),\n    Ok(_) => {},\n}","preventionTips":["Enforce a minimum notional in strategy sizing logic before emitting orders","Round position sizes up to the lot scale instead of truncating for buys","Check fee/rounding adjustments don't shrink amounts below the minimum","Add unit tests for dust-sized order amounts"],"tags":["polymarket","order-size","validation","decimal"],"backgroundTag":"value-out-of-range","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"}