From 2943702d08e3f77178dd493f01cede3659d36e80 Mon Sep 17 00:00:00 2001 From: DukeDeSouth Date: Mon, 2 Mar 2026 15:31:21 -0500 Subject: [PATCH 1/2] fix: use Decimal.quantize for base-10 rounding precision round_down(9.53, 2) returns 9.52 because floor(9.53 * 100) = floor(952.999...) = 952 in IEEE 754. Same class of bug affects round_up, round_normal, and to_token_decimals. Switched all four functions from float arithmetic (floor/ceil) to Decimal.quantize with ROUND_FLOOR/ROUND_CEILING/ROUND_HALF_UP. Decimal is already imported in helpers.py but was only used in decimal_places(). No new dependencies, signatures unchanged. Closes #142 Made-with: Cursor --- py_clob_client/order_builder/helpers.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/py_clob_client/order_builder/helpers.py b/py_clob_client/order_builder/helpers.py index c686aee6..1b3d7094 100644 --- a/py_clob_client/order_builder/helpers.py +++ b/py_clob_client/order_builder/helpers.py @@ -1,24 +1,24 @@ -from math import floor, ceil -from decimal import Decimal +from decimal import Decimal, ROUND_FLOOR, ROUND_HALF_UP, ROUND_CEILING def round_down(x: float, sig_digits: int) -> float: - return floor(x * (10**sig_digits)) / (10**sig_digits) + d = Decimal(str(x)) + return float(d.quantize(Decimal(10) ** -sig_digits, rounding=ROUND_FLOOR)) def round_normal(x: float, sig_digits: int) -> float: - return round(x * (10**sig_digits)) / (10**sig_digits) + d = Decimal(str(x)) + return float(d.quantize(Decimal(10) ** -sig_digits, rounding=ROUND_HALF_UP)) def round_up(x: float, sig_digits: int) -> float: - return ceil(x * (10**sig_digits)) / (10**sig_digits) + d = Decimal(str(x)) + return float(d.quantize(Decimal(10) ** -sig_digits, rounding=ROUND_CEILING)) def to_token_decimals(x: float) -> int: - f = (10**6) * x - if decimal_places(f) > 0: - f = round_normal(f, 0) - return int(f) + d = Decimal(str(x)) * Decimal("1000000") + return int(d.quantize(Decimal("1"), rounding=ROUND_HALF_UP)) def decimal_places(x: float) -> int: From 5a323da82bd0b47a559cdf850ce4562e4993f2a5 Mon Sep 17 00:00:00 2001 From: DukeDeSouth Date: Mon, 2 Mar 2026 15:44:47 -0500 Subject: [PATCH 2/2] fix: use ROUND_HALF_EVEN to match Python round() semantics round_normal() previously used Python's round() which implements banker's rounding (ROUND_HALF_EVEN). The initial Decimal.quantize fix incorrectly used ROUND_HALF_UP, changing midpoint behavior (e.g. 0.25 at 1dp: 0.2 vs 0.3). Fixed per Bugbot review. Made-with: Cursor --- py_clob_client/order_builder/helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py_clob_client/order_builder/helpers.py b/py_clob_client/order_builder/helpers.py index 1b3d7094..4f930efa 100644 --- a/py_clob_client/order_builder/helpers.py +++ b/py_clob_client/order_builder/helpers.py @@ -1,4 +1,4 @@ -from decimal import Decimal, ROUND_FLOOR, ROUND_HALF_UP, ROUND_CEILING +from decimal import Decimal, ROUND_FLOOR, ROUND_HALF_EVEN, ROUND_CEILING def round_down(x: float, sig_digits: int) -> float: @@ -8,7 +8,7 @@ def round_down(x: float, sig_digits: int) -> float: def round_normal(x: float, sig_digits: int) -> float: d = Decimal(str(x)) - return float(d.quantize(Decimal(10) ** -sig_digits, rounding=ROUND_HALF_UP)) + return float(d.quantize(Decimal(10) ** -sig_digits, rounding=ROUND_HALF_EVEN)) def round_up(x: float, sig_digits: int) -> float: @@ -18,7 +18,7 @@ def round_up(x: float, sig_digits: int) -> float: def to_token_decimals(x: float) -> int: d = Decimal(str(x)) * Decimal("1000000") - return int(d.quantize(Decimal("1"), rounding=ROUND_HALF_UP)) + return int(d.quantize(Decimal("1"), rounding=ROUND_HALF_EVEN)) def decimal_places(x: float) -> int: