diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 737ec87a..8f07c228 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,12 +10,10 @@ ## New Features -* Use decorator for `from_pb` methods to ensure an error message with protobuf data is logged in case there are conversion problems. +* Add helper function to support creating quantities that conform to the API expectations. ## Bug Fixes -* Deal correctly with cancelled orders with no price or quantity - diff --git a/src/frequenz/client/electricity_trading/__init__.py b/src/frequenz/client/electricity_trading/__init__.py index fe86b8af..1df21662 100644 --- a/src/frequenz/client/electricity_trading/__init__.py +++ b/src/frequenz/client/electricity_trading/__init__.py @@ -212,6 +212,7 @@ async def stream_trades(): TradeState, UpdateOrder, ) +from ._utils import quantize_quantity __all__ = [ "Client", @@ -243,4 +244,5 @@ async def stream_trades(): "MIN_QUANTITY_MW", "PRECISION_DECIMAL_QUANTITY", "PRECISION_DECIMAL_PRICE", + "quantize_quantity", ] diff --git a/src/frequenz/client/electricity_trading/_utils.py b/src/frequenz/client/electricity_trading/_utils.py new file mode 100644 index 00000000..c861ef70 --- /dev/null +++ b/src/frequenz/client/electricity_trading/_utils.py @@ -0,0 +1,25 @@ +# License: MIT +# Copyright © 2025 Frequenz Energy-as-a-Service GmbH + +"""Utility functions for the electricity trading API client.""" + +from decimal import Decimal + +from ._client import PRECISION_DECIMAL_QUANTITY + + +def quantize_quantity(value: Decimal | float) -> Decimal: + """Convert a decimal to a quantity with the correct precision for the API. + + Simply rounds the value to the correct precision using HALF_EVEN rounding. + + Args: + value: The value to convert in float or Decimal. + + Returns: + The quantity with the correct precision as a Decimal. + """ + dec = Decimal(str(value)) if isinstance(value, float) else value + quantity_step = Decimal(f"1e-{PRECISION_DECIMAL_QUANTITY}") + quantized = Decimal(dec).quantize(quantity_step, rounding="ROUND_HALF_EVEN") + return quantized diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..cd125a5f --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,40 @@ +# License: MIT +# Copyright © 2023 Frequenz Energy-as-a-Service GmbH + +"""Tests for helper utilities module.""" + +from decimal import Decimal + +from frequenz.client.electricity_trading import quantize_quantity + + +def test_quantize_quantity() -> None: + """Test the quantize_quantity function.""" + + def test(inp: str, out: str) -> None: + # check for float + resf = quantize_quantity(float(inp)) + # ... and decimal + resd = quantize_quantity(Decimal(inp)) + + # check correct type + assert isinstance(resf, Decimal) + assert isinstance(resd, Decimal) + + # check correct value + assert resf == resd == Decimal(out) + + # check negative + resn = quantize_quantity(-float(inp)) + assert isinstance(resn, Decimal) + assert resn == Decimal(out) * -1 + + test("0.0", "0") + test("0.01", "0") + test("0.05", "0") # round down in ROUND_HALF_EVEN mode + test("0.051", "0.1") + test("0.1", "0.1") + test("0.15", "0.2") # round up in ROUND_HALF_EVEN mode + test("0.5", "0.5") + test("1", "1") + test("99.89", "99.9")