diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 40e493f7..176fb35a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,8 @@ ## New Features +* Add trading-cli tool to interact with the trading API. This initial version only provides a tool to list day-ahead prices from the entsoe API. + ## Bug Fixes diff --git a/pyproject.toml b/pyproject.toml index 36509fa8..4cc9a315 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ classifiers = [ requires-python = ">= 3.11, < 4" # TODO(cookiecutter): Remove and add more dependencies if appropriate dependencies = [ + "entsoe-py >= 0.6.16, < 1", "frequenz-api-common >= 0.6.3, < 0.7.0", "grpcio >= 1.66.2, < 2", "frequenz-channels >= 1.0.0, < 2", @@ -37,6 +38,9 @@ dependencies = [ ] dynamic = ["version"] +[project.scripts] +trading-cli = "frequenz.client.electricity_trading.cli.__main__:main" + [[project.authors]] name = "Frequenz Energy-as-a-Service GmbH" email = "floss@frequenz.com" @@ -66,6 +70,7 @@ dev-mkdocs = [ dev-mypy = [ "mypy == 1.13.0", "grpc-stubs == 1.53.0.5", + "pandas-stubs == 2.2.2.240807", "types-Markdown == 3.7.0.20241204", "types-protobuf == 5.28.3.20241030", # For checking the noxfile, docs/ script, and tests @@ -174,7 +179,7 @@ packages = ["frequenz.client.electricity_trading"] strict = true [[tool.mypy.overrides]] -module = ["mkdocs_macros.*", "sybil", "sybil.*", "deepdiff"] +module = ["mkdocs_macros.*", "sybil", "sybil.*", "deepdiff", "entsoe", "entsoe."] ignore_missing_imports = true [tool.setuptools_scm] diff --git a/src/frequenz/client/electricity_trading/cli/__init__.py b/src/frequenz/client/electricity_trading/cli/__init__.py new file mode 100644 index 00000000..50eb50eb --- /dev/null +++ b/src/frequenz/client/electricity_trading/cli/__init__.py @@ -0,0 +1,4 @@ +# License: MIT +# Copyright © 2025 Frequenz Energy-as-a-Service GmbH + +"""Package for CLI tool to interact with the trading API.""" diff --git a/src/frequenz/client/electricity_trading/cli/__main__.py b/src/frequenz/client/electricity_trading/cli/__main__.py new file mode 100644 index 00000000..9fa1aff0 --- /dev/null +++ b/src/frequenz/client/electricity_trading/cli/__main__.py @@ -0,0 +1,43 @@ +# License: MIT +# Copyright © 2025 Frequenz Energy-as-a-Service GmbH + +"""CLI tool to interact with the trading API.""" + +import argparse +from datetime import datetime, timedelta +from zoneinfo import ZoneInfo + +from frequenz.client.electricity_trading.cli.day_ahead import list_day_ahead_prices + + +def main() -> None: + """Run main entry point for the CLI tool.""" + tz = ZoneInfo("Europe/Berlin") + midnight = datetime.combine(datetime.now(tz), datetime.min.time(), tzinfo=tz) + parser = argparse.ArgumentParser() + parser.add_argument("--entsoe_key", type=str, required=True) + parser.add_argument( + "--start", + type=datetime.fromisoformat, + required=False, + default=midnight, + ) + parser.add_argument( + "--end", + type=datetime.fromisoformat, + required=False, + default=midnight + timedelta(days=2), + ) + parser.add_argument("--country_code", type=str, required=False, default="DE_LU") + args = parser.parse_args() + + list_day_ahead_prices( + entsoe_key=args.entsoe_key, + start=args.start, + end=args.end, + country_code=args.country_code, + ) + + +if __name__ == "__main__": + main() diff --git a/src/frequenz/client/electricity_trading/cli/day_ahead.py b/src/frequenz/client/electricity_trading/cli/day_ahead.py new file mode 100644 index 00000000..3cccb3f8 --- /dev/null +++ b/src/frequenz/client/electricity_trading/cli/day_ahead.py @@ -0,0 +1,36 @@ +# License: MIT +# Copyright © 2025 Frequenz Energy-as-a-Service GmbH + +"""Functions for CLI tool to interact with the trading API.""" + +from datetime import datetime + +import pandas as pd +from entsoe import EntsoePandasClient + + +def list_day_ahead_prices( + entsoe_key: str, + start: datetime, + end: datetime, + country_code: str, +) -> None: + """ + List day-ahead prices for a given country code. + + Args: + entsoe_key: The API key for the Entsoe API + start: The start date of the query + end: The end date of the query + country_code: The country code for which to query the prices + """ + start_ts = pd.Timestamp(start) + end_ts = pd.Timestamp(end) + + client = EntsoePandasClient(api_key=entsoe_key) + da_prices = client.query_day_ahead_prices(country_code, start=start_ts, end=end_ts) + + da_prices.name = "price" + da_prices.index.name = "timestamp" + + print(da_prices.to_csv())