Skip to content

Commit 2c0e6a2

Browse files
committed
Add CLI command to create limit orders
The command allows creating a limit order on a testing instance, which is identified by containing the string "test" in its URL. The market side is derived from the sign of the quantity. For now only delivery area codes in EUROPE_EIC format are supported. Signed-off-by: cwasicki <[email protected]> fu create Signed-off-by: cwasicki <[email protected]>
1 parent 6cf69ef commit 2c0e6a2

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

src/frequenz/client/electricity_trading/cli/__main__.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import click
1111

1212
from frequenz.client.electricity_trading.cli.day_ahead import list_day_ahead_prices
13+
from frequenz.client.electricity_trading.cli.etrading import (
14+
create_order as run_create_order,
15+
)
1316
from frequenz.client.electricity_trading.cli.etrading import (
1417
list_orders as run_list_orders,
1518
)
@@ -54,6 +57,51 @@ def list_orders(url: str, key: str, *, start: datetime, gid: int) -> None:
5457
asyncio.run(run_list_orders(url=url, key=key, delivery_start=start, gid=gid))
5558

5659

60+
@cli.command()
61+
@click.option("--url", required=True, type=str)
62+
@click.option("--key", required=True, type=str)
63+
@click.option("--start", required=True, type=iso)
64+
@click.option("--gid", required=True, type=int)
65+
@click.option("--quantity", required=True, type=str)
66+
@click.option("--price", required=True, type=str)
67+
@click.option("--area", required=True, type=str)
68+
@click.option("--currency", default="EUR", type=str)
69+
@click.option("--duration", default=900, type=int)
70+
def create_order(
71+
# pylint: disable=too-many-arguments
72+
url: str,
73+
key: str,
74+
*,
75+
start: datetime,
76+
gid: int,
77+
quantity: str,
78+
price: str,
79+
area: str,
80+
currency: str,
81+
duration: int,
82+
) -> None:
83+
"""Create an order.
84+
85+
This is only allowed in test instances.
86+
"""
87+
if "test" not in url:
88+
raise ValueError("Creating orders is only allowed in test instances.")
89+
90+
asyncio.run(
91+
run_create_order(
92+
url=url,
93+
key=key,
94+
delivery_start=start,
95+
gid=gid,
96+
quantity_mw=quantity,
97+
price=price,
98+
delivery_area=area,
99+
currency=currency,
100+
duration=timedelta(seconds=duration),
101+
)
102+
)
103+
104+
57105
@cli.command()
58106
@click.option("--entsoe-key", required=True, type=str)
59107
@click.option("--start", default=midnight(), type=iso)

src/frequenz/client/electricity_trading/cli/etrading.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44
"""CLI tool to interact with the trading API."""
55

66
from datetime import datetime, timedelta, timezone
7+
from decimal import Decimal
78
from enum import Enum
89

910
from frequenz.client.electricity_trading import (
1011
Client,
12+
Currency,
13+
DeliveryArea,
1114
DeliveryPeriod,
15+
EnergyMarketCodeType,
16+
MarketSide,
1217
OrderDetail,
18+
OrderType,
19+
Power,
20+
Price,
1321
PublicTrade,
1422
)
1523

@@ -107,6 +115,63 @@ async def list_orders(
107115
print_order(order)
108116

109117

118+
# pylint: disable=too-many-arguments
119+
async def create_order(
120+
url: str,
121+
key: str,
122+
*,
123+
gid: int,
124+
delivery_start: datetime,
125+
delivery_area: str,
126+
price: str,
127+
quantity_mw: str,
128+
currency: str,
129+
duration: timedelta,
130+
) -> None:
131+
"""Create a limit order for a given price (in EUR/MWh) and quantity (in MW).
132+
133+
The market side is determined by the sign of the quantity, positive for buy orders
134+
and negative for sell orders. The delivery period is 15 minutes starting at
135+
`delivery_start`. The delivery area code is expected to be in EUROPE_EIC format.
136+
137+
Args:
138+
url: URL of the trading API.
139+
key: API key.
140+
gid: Gridpool ID.
141+
delivery_start: Start of the delivery period.
142+
delivery_area: Delivery area code.
143+
price: Price of the order.
144+
quantity_mw: Quantity in MW, positive for buy orders and negative for sell orders.
145+
currency: Currency of the price.
146+
duration: Duration of the delivery period.
147+
"""
148+
client = Client(server_url=url, auth_key=key)
149+
150+
side = MarketSide.SELL if quantity_mw[0] == "-" else MarketSide.BUY
151+
quantity = Power(mw=Decimal(quantity_mw.lstrip("-")))
152+
check_delivery_start(delivery_start)
153+
order = await client.create_gridpool_order(
154+
gridpool_id=gid,
155+
delivery_area=DeliveryArea(
156+
code=delivery_area,
157+
code_type=EnergyMarketCodeType.EUROPE_EIC,
158+
),
159+
delivery_period=DeliveryPeriod(
160+
start=delivery_start,
161+
duration=duration,
162+
),
163+
order_type=OrderType.LIMIT,
164+
side=side,
165+
price=Price(
166+
amount=Decimal(price),
167+
currency=Currency[currency],
168+
),
169+
quantity=quantity,
170+
)
171+
172+
print_order(order)
173+
174+
110175
def print_trade_header() -> None:
111176
"""Print trade header in CSV format."""
112177
header = (

0 commit comments

Comments
 (0)