Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion py_clob_client/order_builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
33 changes: 32 additions & 1 deletion tests/order_builder/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SELL regression test missing price rounding assertions

Low Severity

The SELL half of test_market_order_uses_round_down_for_price only asserts that side == UtilsSell, without verifying that round_down was actually used for the price — unlike the BUY half, which explicitly checks effective_price against both round_down and round_normal results. This means a future regression that changes SELL price rounding back to round_normal would not be caught by this test, defeating the stated purpose of a regression test for issue #323.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ac9827a. Configure here.