From 7f12324264bc56ca50068537ad809c8300480f39 Mon Sep 17 00:00:00 2001 From: Florian Maurer Date: Fri, 15 Sep 2023 15:49:44 +0200 Subject: [PATCH 1/2] check that an orderbook is in one of the open auctions clear up all_orders to fix piling up orders --- assume/markets/base_market.py | 12 ++++++++---- .../markets/clearing_algorithms/complex_clearing.py | 2 -- examples/inputs/example_01c/config.yml | 3 +-- tests/test_market.py | 9 ++++++--- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/assume/markets/base_market.py b/assume/markets/base_market.py index d3537d80..e1695143 100644 --- a/assume/markets/base_market.py +++ b/assume/markets/base_market.py @@ -4,7 +4,6 @@ from datetime import datetime from itertools import groupby from operator import itemgetter -from queue import Queue from mango import Role @@ -35,7 +34,7 @@ class MarketMechanism: def __init__(self, marketconfig: MarketConfig): self.marketconfig = marketconfig - self.open_auctions = Queue() + self.open_auctions = set() self.all_orders = [] def validate_registration(self, meta: dict) -> bool: @@ -71,6 +70,10 @@ def validate_orderbook(self, orderbook: Orderbook, agent_tuple) -> None: assert order["price"] <= max_price, f"maximum_bid_price {order['price']}" assert order["price"] >= min_price, f"minimum_bid_price {order['price']}" + # check that the product is part of an open auction + product = (order["start_time"], order["end_time"], order["only_hours"]) + assert product in self.open_auctions, "no open auction" + if max_volume: assert ( abs(order["volume"]) <= max_volume @@ -212,7 +215,7 @@ async def opening(self): "products": products, } - self.open_auctions.put(opening_message) + self.open_auctions |= set(opening_message["products"]) for agent in self.registered_agents: agent_addr, agent_id = agent @@ -340,7 +343,8 @@ async def clear_market(self, market_products: list[MarketProduct]): # pending_orderbook, market_meta, ) = self.clear(self.all_orders, market_products) - self.open_auctions.get() + self.all_orders = [] + self.open_auctions - set(market_products) # self.all_orders = pending_orderbook accepted_orderbook.sort(key=itemgetter("agent_id")) diff --git a/assume/markets/clearing_algorithms/complex_clearing.py b/assume/markets/clearing_algorithms/complex_clearing.py index 51c15832..808e8cba 100644 --- a/assume/markets/clearing_algorithms/complex_clearing.py +++ b/assume/markets/clearing_algorithms/complex_clearing.py @@ -254,8 +254,6 @@ def clear( if all(order_profit >= 0 for order_profit in orders_profit): break - self.all_orders = [] - return extract_results( model=instance, orders=orderbook, diff --git a/examples/inputs/example_01c/config.yml b/examples/inputs/example_01c/config.yml index f1219aac..256f6444 100644 --- a/examples/inputs/example_01c/config.yml +++ b/examples/inputs/example_01c/config.yml @@ -100,8 +100,7 @@ dam_with_complex_opt_clearing: maximum_bid_price: 3000 minimum_bid_price: -500 price_unit: EUR/MWh - market_mechanism: pay_as_clear_complex_opt + market_mechanism: pay_as_clear_complex additional_fields: - bid_type - - accepted_price - profile diff --git a/tests/test_market.py b/tests/test_market.py index 1478e198..2c9e2e05 100644 --- a/tests/test_market.py +++ b/tests/test_market.py @@ -44,18 +44,18 @@ async def test_market_init(market_role: MarketRole): "sender_addr": market_role.context.addr, "sender_id": market_role.context.aid, } - + end = start + rd(hours=1) orderbook = [ { "start_time": start, - "end_time": start + rd(hours=1), + "end_time": end, "volume": 120, "price": 120, "agent_id": "gen1", "only_hours": None, } ] - + market_role.open_auctions |= {(start, end, None)} market_role.handle_orderbook(content={"orderbook": orderbook}, meta=meta) assert len(market_role.all_orders) == 1 @@ -77,6 +77,7 @@ async def test_market_tick(market_role: MarketRole): "only_hours": None, } ] + market_role.open_auctions |= {(start, end, None)} market_role.handle_orderbook(content={"orderbook": orderbook}, meta=meta) assert len(market_role.all_orders) == 1 assert market_role.all_orders[0]["price"] == 1201 @@ -105,6 +106,7 @@ async def test_market_max(market_role: MarketRole): market_role.marketconfig.maximum_bid_price = 1000 market_role.marketconfig.minimum_bid_price = -500 market_role.marketconfig.maximum_bid_volume = 9090 + market_role.open_auctions |= {(start, end, None)} orderbook = [ { @@ -192,6 +194,7 @@ async def test_market_unmatched(market_role: MarketRole): "only_hours": None, } ] + market_role.open_auctions |= {(start, end, None)} market_role.handle_orderbook(content={"orderbook": orderbook}, meta=meta) content = { From 0d5e42d43066b68fea457a38379b309edd6b4395 Mon Sep 17 00:00:00 2001 From: Florian Maurer Date: Fri, 15 Sep 2023 16:12:19 +0200 Subject: [PATCH 2/2] add unused supply_orders to rejected_orders clean up setting accepted_volume and price on rejected orders fix setting the volume of the split order in clearing --- assume/markets/base_market.py | 3 +++ .../clearing_algorithms/all_or_nothing.py | 12 ------------ assume/markets/clearing_algorithms/simple.py | 16 +++++++++------- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/assume/markets/base_market.py b/assume/markets/base_market.py index e1695143..d0d33aed 100644 --- a/assume/markets/base_market.py +++ b/assume/markets/base_market.py @@ -344,6 +344,9 @@ async def clear_market(self, market_products: list[MarketProduct]): market_meta, ) = self.clear(self.all_orders, market_products) self.all_orders = [] + for order in rejected_orderbook: + order["accepted_volume"] = 0 + order["accepted_price"] = 0 self.open_auctions - set(market_products) # self.all_orders = pending_orderbook diff --git a/assume/markets/clearing_algorithms/all_or_nothing.py b/assume/markets/clearing_algorithms/all_or_nothing.py index e8060b74..f7037c03 100644 --- a/assume/markets/clearing_algorithms/all_or_nothing.py +++ b/assume/markets/clearing_algorithms/all_or_nothing.py @@ -81,12 +81,6 @@ def clear( # resulting i is the cut point accepted_product_orders.extend(demand_orders[:i]) accepted_product_orders.extend(supply_orders[:i]) - - for order in supply_orders[i:]: - order["accepted_volume"] = 0 - for order in demand_orders[i:]: - order["accepted_volume"] = 0 - rejected_orders.extend(demand_orders[i:]) rejected_orders.extend(supply_orders[i:]) @@ -168,12 +162,6 @@ def clear( accepted_product_orders.extend(demand_orders[:i]) accepted_product_orders.extend(supply_orders[:i]) - - for order in supply_orders[i:]: - order["accepted_volume"] = 0 - for order in demand_orders[i:]: - order["accepted_volume"] = 0 - rejected_orders.extend(demand_orders[i:]) rejected_orders.extend(supply_orders[i:]) diff --git a/assume/markets/clearing_algorithms/simple.py b/assume/markets/clearing_algorithms/simple.py index 95537174..ecc8d549 100644 --- a/assume/markets/clearing_algorithms/simple.py +++ b/assume/markets/clearing_algorithms/simple.py @@ -82,7 +82,6 @@ def clear( for demand_order in demand_orders: if not supply_orders: # if no more generation - reject left over demand - demand_order["accepted_volume"] = 0 rejected_orders.append(demand_order) continue @@ -100,7 +99,6 @@ def clear( to_commit.append(supply_order) gen_vol += supply_order["volume"] else: - supply_order["accepted_volume"] = 0 rejected_orders.append(supply_order) # now we know which orders we need # we only need to see how to arrange it. @@ -118,7 +116,7 @@ def clear( # generation left over - split last generation bid supply_order = to_commit[-1] split_supply_order = supply_order.copy() - split_supply_order["accepted_volume"] = diff + split_supply_order["volume"] = diff supply_order["accepted_volume"] = supply_order["volume"] - diff # changed supply_order is still part of to_commit and will be added # only volume-diff can be sold for current price @@ -133,6 +131,9 @@ def clear( accepted_product_orders.append(demand_order) accepted_product_orders.extend(to_commit) + for order in supply_orders: + rejected_orders.append(order) + # set clearing price - merit order - uniform pricing accepted_supply_orders = [ x for x in accepted_product_orders if x["accepted_volume"] > 0 @@ -204,7 +205,6 @@ def clear( for demand_order in demand_orders: if not supply_orders: # if no more generation - reject left over demand - demand_order["accepted_volume"] = 0 rejected_orders.append(demand_order) continue @@ -218,7 +218,6 @@ def clear( to_commit.append(supply_order) gen_vol += supply_order["volume"] else: - supply_order["accepted_volume"] = 0 rejected_orders.append(supply_order) # now we know which orders we need # we only need to see how to arrange it. @@ -236,8 +235,8 @@ def clear( # generation left over - split generation supply_order = to_commit[-1] split_supply_order = supply_order.copy() - split_supply_order["accepted_volume"] = diff - supply_order["accepted_volume"] -= supply_order["volume"] - diff + split_supply_order["volume"] = diff + supply_order["accepted_volume"] = supply_order["volume"] - diff # only volume-diff can be sold for current price # add left over to supply_orders again gen_vol -= diff @@ -255,6 +254,9 @@ def clear( demand_order["accepted_price"] = supply_order["price"] accepted_product_orders.extend(to_commit) + for order in supply_orders: + rejected_orders.append(order) + accepted_supply_orders = [ x for x in accepted_product_orders if x["accepted_volume"] > 0 ]