Add significant digits Round_Up / round_down

This commit is contained in:
Matthias
2023-08-30 16:40:30 +00:00
parent 65c7607e36
commit 4ed46ef6b3
2 changed files with 59 additions and 1 deletions

View File

@@ -248,6 +248,39 @@ def amount_to_contract_precision(
return amount
def __price_to_precision_significant_digits(
price: float,
price_precision: float,
*,
rounding_mode: int = ROUND,
) -> float:
"""
Implementation of ROUND_UP/Round_down for significant digits mode.
"""
from decimal import ROUND_DOWN as dec_ROUND_DOWN
from decimal import ROUND_UP as dec_ROUND_UP
from decimal import Decimal
dec = Decimal(str(price))
string = f'{dec:f}'
precision = round(price_precision)
q = precision - dec.adjusted() - 1
sigfig = Decimal('10') ** -q
if q < 0:
string_to_precision = string[:precision]
# string_to_precision is '' when we have zero precision
below = sigfig * Decimal(string_to_precision if string_to_precision else '0')
above = below + sigfig
res = above if rounding_mode == ROUND_UP else below
precise = f'{res:f}'
else:
precise = '{:f}'.format(dec.quantize(
sigfig,
rounding=dec_ROUND_DOWN if rounding_mode == ROUND_DOWN else dec_ROUND_UP)
)
return float(precise)
def price_to_precision(
price: float,
price_precision: Optional[float],
@@ -289,7 +322,7 @@ def price_to_precision(
res = price_str - missing
return round(float(str(res)), 14)
return price
elif precisionMode in (SIGNIFICANT_DIGITS, DECIMAL_PLACES):
elif precisionMode == DECIMAL_PLACES:
ndigits = round(price_precision)
ticks = price * (10**ndigits)
@@ -299,5 +332,11 @@ def price_to_precision(
return floor(ticks) / (10**ndigits)
raise ValueError(f"Unknown rounding_mode {rounding_mode}")
elif precisionMode == SIGNIFICANT_DIGITS:
if rounding_mode in (ROUND_UP, ROUND_DOWN):
return __price_to_precision_significant_digits(
price, price_precision, rounding_mode=rounding_mode
)
raise ValueError(f"Unknown precisionMode {precisionMode}")
return price

View File

@@ -288,6 +288,25 @@ def test_amount_to_precision(amount, precision_mode, precision, expected,):
(2.9909, SIGNIFICANT_DIGITS, 2, 2.9, TRUNCATE),
(0.00000777, SIGNIFICANT_DIGITS, 2, 0.0000077, TRUNCATE),
(0.00000729, SIGNIFICANT_DIGITS, 2, 0.0000072, TRUNCATE),
# ROUND
(722.2, SIGNIFICANT_DIGITS, 1, 700.0, ROUND),
(790.2, SIGNIFICANT_DIGITS, 1, 800.0, ROUND),
(722.2, SIGNIFICANT_DIGITS, 2, 720.0, ROUND),
(722.2, SIGNIFICANT_DIGITS, 1, 800.0, ROUND_UP),
(722.2, SIGNIFICANT_DIGITS, 2, 730.0, ROUND_UP),
(777.7, SIGNIFICANT_DIGITS, 2, 780.0, ROUND_UP),
(777.7, SIGNIFICANT_DIGITS, 3, 778.0, ROUND_UP),
(722.2, SIGNIFICANT_DIGITS, 1, 700.0, ROUND_DOWN),
(722.2, SIGNIFICANT_DIGITS, 2, 720.0, ROUND_DOWN),
(777.7, SIGNIFICANT_DIGITS, 2, 770.0, ROUND_DOWN),
(777.7, SIGNIFICANT_DIGITS, 3, 777.0, ROUND_DOWN),
(0.000007222, SIGNIFICANT_DIGITS, 1, 0.000008, ROUND_UP),
(0.000007222, SIGNIFICANT_DIGITS, 2, 0.0000073, ROUND_UP),
(0.000007777, SIGNIFICANT_DIGITS, 2, 0.0000078, ROUND_UP),
(0.000007222, SIGNIFICANT_DIGITS, 1, 0.000007, ROUND_DOWN),
(0.000007222, SIGNIFICANT_DIGITS, 2, 0.0000072, ROUND_DOWN),
(0.000007777, SIGNIFICANT_DIGITS, 2, 0.0000077, ROUND_DOWN),
])
def test_price_to_precision(price, precision_mode, precision, expected, rounding_mode):
assert price_to_precision(