From ac9827a94e657a1e20dbf3af72c739e7b4ac1313 Mon Sep 17 00:00:00 2001 From: skyc1e Date: Wed, 8 Apr 2026 02:15:59 +0200 Subject: [PATCH] fix: use round_down for market order price instead of round_normal Market orders should not round price in the unfavorable direction. round_normal can round up (e.g. 0.555 -> 0.56), giving BUY orders fewer shares per dollar. round_down (0.555 -> 0.55) matches the TypeScript client's roundDown behavior for market orders. Limit orders (get_order_amounts) correctly use round_normal and are not changed. Closes #323 --- py_clob_client/order_builder/builder.py | 2 +- tests/order_builder/test_builder.py | 33 ++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/py_clob_client/order_builder/builder.py b/py_clob_client/order_builder/builder.py index 64fb7c96..30901df9 100644 --- a/py_clob_client/order_builder/builder.py +++ b/py_clob_client/order_builder/builder.py @@ -85,7 +85,7 @@ def get_order_amounts( def get_market_order_amounts( self, side: str, amount: float, price: float, round_config: RoundConfig ): - raw_price = round_normal(price, round_config.price) + raw_price = round_down(price, round_config.price) if side == BUY: raw_maker_amt = round_down(amount, round_config.size) diff --git a/tests/order_builder/test_builder.py b/tests/order_builder/test_builder.py index 42b35085..f866a391 100644 --- a/tests/order_builder/test_builder.py +++ b/tests/order_builder/test_builder.py @@ -12,7 +12,7 @@ from py_clob_client.signer import Signer from py_clob_client.order_builder.builder import OrderBuilder, ROUNDING_CONFIG -from py_clob_client.order_builder.helpers import decimal_places, round_normal +from py_clob_client.order_builder.helpers import decimal_places, round_normal, round_down from py_order_utils.model import ( POLY_GNOSIS_SAFE, EOA, @@ -3442,3 +3442,34 @@ def test_create_market_order_sell_0_0001_neg_risk(self): / float(signed_order.order["makerAmount"]), 0.0056, ) + + def test_market_order_uses_round_down_for_price(self): + """ + Regression test for #323: get_market_order_amounts should use + round_down for price, not round_normal, to match the TypeScript + client and avoid rounding prices in the unfavorable direction. + + With price=0.555 and tick_size="0.01" (price precision=2): + round_normal(0.555, 2) = 0.56 (rounds up — worse for buyer) + round_down(0.555, 2) = 0.55 (rounds down — correct) + """ + builder = OrderBuilder(signer) + config = ROUNDING_CONFIG["0.01"] + + # BUY: higher price means fewer shares per dollar — round_down is favorable + side, maker, taker = builder.get_market_order_amounts( + BUY, 100.0, 0.555, config + ) + self.assertEqual(side, UtilsBuy) + # With round_down, effective price is 0.55, so taker = 100/0.55 = 181.81... + # With round_normal, effective price would be 0.56, taker = 100/0.56 = 178.57... + # Verify price used was 0.55 (round_down), not 0.56 (round_normal) + effective_price = round_down(maker / taker, config.price) + self.assertEqual(effective_price, round_down(0.555, config.price)) + self.assertNotEqual(effective_price, round_normal(0.555, config.price)) + + # SELL: lower price means less received — round_down is conservative + side, maker, taker = builder.get_market_order_amounts( + SELL, 100.0, 0.555, config + ) + self.assertEqual(side, UtilsSell)