Skip to content
Merged
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
4 changes: 1 addition & 3 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- Here goes the main new features and examples or instructions on how to use them -->

## Bug Fixes

* Deal correctly with cancelled orders with no price or quantity

<!-- Here goes notable bug fixes that are worth a special mention or explanation -->
2 changes: 2 additions & 0 deletions src/frequenz/client/electricity_trading/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ async def stream_trades():
TradeState,
UpdateOrder,
)
from ._utils import quantize_quantity

__all__ = [
"Client",
Expand Down Expand Up @@ -243,4 +244,5 @@ async def stream_trades():
"MIN_QUANTITY_MW",
"PRECISION_DECIMAL_QUANTITY",
"PRECISION_DECIMAL_PRICE",
"quantize_quantity",
]
25 changes: 25 additions & 0 deletions src/frequenz/client/electricity_trading/_utils.py
Original file line number Diff line number Diff line change
@@ -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
40 changes: 40 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -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")
Loading