Skip to content

Commit

Permalink
proper fixing for market clearing
Browse files Browse the repository at this point in the history
  • Loading branch information
maurerle committed Sep 28, 2023
1 parent 7fa389c commit a0d1a09
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 38 deletions.
55 changes: 22 additions & 33 deletions assume/markets/clearing_algorithms/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ def clear(
meta = []
orderbook.sort(key=market_getter)
for product, product_orders in groupby(orderbook, market_getter):
accepted_product_orders: Orderbook = []
accepted_demand_orders: Orderbook = []
accepted_supply_orders: Orderbook = []
product_orders = list(product_orders)
if product not in market_products:
rejected_orders.extend(product_orders)
Expand Down Expand Up @@ -89,15 +90,18 @@ def clear(
# now add the next demand order
dem_vol += -demand_order["volume"]
demand_order["accepted_volume"] = demand_order["volume"]
to_commit: Orderbook = []

# and add supply until the demand order is matched
while supply_orders and gen_vol < dem_vol:
supply_order = supply_orders.pop(0)
if supply_order["price"] <= demand_order["price"]:
added = supply_order["volume"] - supply_order.get(
"accepted_volume", 0
)
should_insert = not supply_order.get("accepted_volume")
supply_order["accepted_volume"] = supply_order["volume"]
to_commit.append(supply_order)
gen_vol += supply_order["volume"]
if should_insert:
accepted_supply_orders.append(supply_order)
gen_vol += added
else:
rejected_orders.append(supply_order)
# now we know which orders we need
Expand All @@ -107,51 +111,39 @@ def clear(

if diff < 0:
# gen < dem
# generation is not enough - split last demand bid
split_demand_order = demand_order.copy()
split_demand_order["accepted_volume"] = diff
# generation is not enough - accept partially
demand_order["accepted_volume"] = demand_order["volume"] - diff
rejected_orders.append(split_demand_order)

elif diff > 0:
# generation left over - split last generation bid
supply_order = to_commit[-1]
split_supply_order = supply_order.copy()
split_supply_order["volume"] = diff
# generation left over - accept generation bid partially
supply_order = accepted_supply_orders[-1]
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
gen_vol -= diff

# add left over to supply_orders again
supply_orders.insert(0, split_supply_order)

supply_orders.insert(0, supply_order)
demand_order["accepted_volume"] = demand_order["volume"]
else:
demand_order["accepted_volume"] = demand_order["volume"]

accepted_product_orders.append(demand_order)
accepted_product_orders.extend(to_commit)
accepted_demand_orders.append(demand_order)

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
]
if accepted_supply_orders:
clear_price = float(
max(map(itemgetter("price"), accepted_supply_orders))
)
else:
clear_price = 0

accepted_product_orders = accepted_demand_orders + accepted_supply_orders
for order in accepted_product_orders:
order["accepted_price"] = clear_price
accepted_demand_orders = [
x for x in accepted_product_orders if x["accepted_volume"] < 0
]
accepted_orders.extend(accepted_product_orders)

meta.append(
Expand Down Expand Up @@ -186,7 +178,8 @@ def clear(
meta = []
orderbook.sort(key=market_getter)
for product, product_orders in groupby(orderbook, market_getter):
accepted_product_orders: Orderbook = []
accepted_demand_orders: Orderbook = []
accepted_supply_orders: Orderbook = []
if product not in market_products:
rejected_orders.extend(product_orders)
# log.debug(f'found unwanted bids for {product} should be {market_products}')
Expand Down Expand Up @@ -246,27 +239,23 @@ def clear(
gen_vol -= diff

supply_orders.insert(0, split_supply_order)
demand_order["accepted_volume"] = demand_order["volume"]
else:
# diff == 0 perfect match
demand_order["accepted_volume"] = demand_order["volume"]

accepted_orders.append(demand_order)
accepted_demand_orders.append(demand_order)
# pay as bid
for supply_order in to_commit:
supply_order["accepted_price"] = supply_order["price"]

demand_order["accepted_price"] = supply_order["price"]
accepted_product_orders.extend(to_commit)
accepted_supply_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
]
accepted_demand_orders = [
x for x in accepted_product_orders if x["accepted_volume"] < 0
]
accepted_product_orders = accepted_demand_orders + accepted_supply_orders

accepted_orders.extend(accepted_product_orders)
meta.append(
Expand Down
29 changes: 26 additions & 3 deletions tests/test_simple_market_mechanisms.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ def test_market():
orderbook = extend_orderbook(products, 1000, 100, orderbook)
orderbook = extend_orderbook(products, 900, 50, orderbook)

simple_dayahead_auction_config.market_mechanism = clearing_mechanisms[
simple_dayahead_auction_config.market_mechanism
]
mr = PayAsClearRole(simple_dayahead_auction_config)
accepted, rejected, meta = mr.clear(orderbook, products)
assert meta[0]["demand_volume"] > 0
Expand Down Expand Up @@ -89,3 +86,29 @@ def test_simple_market_mechanism():
# print(meta)

# return mr.all_orders, meta


def test_market_pay_as_clear():
next_opening = simple_dayahead_auction_config.opening_hours.after(datetime.now())
products = get_available_products(
simple_dayahead_auction_config.market_products, next_opening
)
assert len(products) == 1

"""
Create Orderbook with constant order volumes and prices:
- dem1: volume = -1000, price = 3000
- gen1: volume = 1000, price = 100
- gen2: volume = 900, price = 50
"""
orderbook = extend_orderbook(products, -400, 3000)
orderbook = extend_orderbook(products, -100, 3000, orderbook)
orderbook = extend_orderbook(products, 300, 100, orderbook)
orderbook = extend_orderbook(products, 200, 50, orderbook)

mr = PayAsClearRole(simple_dayahead_auction_config)
accepted, rejected, meta = mr.clear(orderbook, products)
assert meta[0]["demand_volume"] > 0
assert meta[0]["price"] > 0
assert len(accepted) == 4
assert len(rejected) == 0
6 changes: 4 additions & 2 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def extend_orderbook(
products,
volume,
price,
orderbook=[],
orderbook=None,
bid_type="SB",
min_acceptance_ratio=None,
):
Expand All @@ -52,6 +52,8 @@ def extend_orderbook(
with specified values for price and volume
and appends the orderbook
"""
if not orderbook:
orderbook = []
if volume == 0:
return orderbook

Expand Down Expand Up @@ -94,7 +96,7 @@ def extend_orderbook(
"agent_id": agent_id,
"bid_id": f"bid_{len(orderbook)+1}",
"volume": volume,
"accepted_volume": None,
"accepted_volume": 0,
"price": price,
"accepted_price": None,
"only_hours": None,
Expand Down

0 comments on commit a0d1a09

Please sign in to comment.