Implemented imbalance price reconstruction

This commit is contained in:
Victor Mylle
2023-12-12 20:04:44 +00:00
parent c06cc10aa6
commit f74d588e62
14 changed files with 314119 additions and 1228 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,71 +1,100 @@
from datetime import datetime
import plotly.graph_objects as go
import numpy as np
import pandas as pd
incremental_bids = "../../data/incremental_bids.csv"
decremental_bids = "../../data/decremental_bids.csv"
class ImbalancePriceCalculator:
def __init__(self, bids) -> None:
self.bids = bids
self.bid_ladder = self.generate_bid_ladder()
def __init__(self, method: int = 1) -> None:
self.method = method
self.load_bids()
def generate_bid_ladder(self):
# Sort bids by Product (3th column of tuple, use order defined above) and then by price (second column of tuple)
# first sort on activaation order, then on bid price
bids = sorted(self.bids, key=lambda x: (x[3], x[1]))
# Calculate cummulative bids
cummulative_volume = 0
cummulative_bids = []
max_price = 0
for volume, price, _, order in bids:
cummulative_volume += volume
if price > max_price:
max_price = price
cummulative_bids.append((cummulative_volume, max_price, price, order))
print((cummulative_volume, max_price, price, order))
cummulative_bids = [(vol, price) for vol, price, _, _ in cummulative_bids]
return cummulative_bids
def load_bids(self):
df = pd.read_csv(incremental_bids, sep=";")
df["Datetime"] = pd.to_datetime(df["Datetime"], utc=True)
def get_imbalance_price(self, volume):
for bid in self.bid_ladder:
# sort by Datetime, Activation Order, Bid Price
df = df.sort_values(by=["Datetime", "Activation Order", "Bid Price"])
# next we need to calculate the cummulative bids for every datetime
incremental_cum_df = df.groupby(["Datetime"]).apply(self.generate_bid_ladder)
df = pd.read_csv(decremental_bids, sep=";")
df["Datetime"] = pd.to_datetime(df["Datetime"], utc=True)
decremental_cum_df = df.groupby(["Datetime"]).apply(self.generate_bid_ladder, incremental=False)
# merge the two dataframes on the Datetime
self.bid_ladder = pd.merge(incremental_cum_df, decremental_cum_df, on="Datetime", how="inner", suffixes=("_inc", "_dec"))
self.bid_ladder = self.bid_ladder.sort_values(by=["Datetime"])
def generate_bid_ladder(self, bids, incremental: bool = True):
if self.method == 1:
bids_df = bids.sort_values(by=["Bid Price"], ascending=[incremental])
elif self.method == 2:
bids_df = bids.sort_values(by=["Activation Order", "Bid Price"], ascending=[True, incremental])
bids_df['cumulative_volume'] = bids_df['Bid Volume'].cumsum()
if incremental:
bids_df['max_price'] = bids_df['Bid Price'].cummax()
else:
bids_df['max_price'] = bids_df['Bid Price'].cummin()
bid_ladder = list(zip(bids_df['cumulative_volume'], bids_df['max_price']))
bid_ladder_df = pd.DataFrame([[bid_ladder]], columns=['bid_ladder'])
return bid_ladder_df
def check_datetime(self, datetime):
return datetime in self.bid_ladder.index
def get_imbalance_price(self, datetime, volume):
if volume < 0:
volume = -volume
bid_ladder = self.bid_ladder.loc[self.bid_ladder.index == datetime]["bid_ladder_dec"].values[0]
else:
bid_ladder = self.bid_ladder.loc[self.bid_ladder.index == datetime]["bid_ladder_inc"].values[0]
for bid in bid_ladder:
if bid[0] > volume:
return bid[1]
return self.bid_ladder[-1][1]
def plot(self):
return bid_ladder[-1][1]
def plot(self, datetime):
row = self.bid_ladder.loc[self.bid_ladder.index == datetime]
dec_bids = row["bid_ladder_dec"].values[0]
inc_bids = row["bid_ladder_inc"].values[0]
# Prepare data for plot
x_interpolated = [vol for i in range(len(self.bid_ladder) - 1) for vol in [self.bid_ladder[i][0], self.bid_ladder[i+1][0]]]
y_interpolated = [price for cum_vol, price in self.bid_ladder for _ in (0, 1)]
x_inc_interpolated = [vol for i in range(len(inc_bids) - 1) for vol in [inc_bids[i][0], inc_bids[i+1][0]]]
y_inc_interpolated = [price for cum_vol, price in inc_bids for _ in (0, 1)]
x_dec_interpolated = [-vol for i in range(len(dec_bids) - 1) for vol in [dec_bids[i][0], dec_bids[i+1][0]]]
y_dec_interpolated = [price for cum_vol, price in dec_bids for _ in (0, 1)]
# Create and show the plot make sure hovering works in between the steps
fig = go.Figure()
fig.add_trace(go.Scatter(x=x_interpolated, y=y_interpolated, mode='lines+markers'))
fig.add_trace(go.Scatter(x=x_inc_interpolated, y=y_inc_interpolated, mode='lines+markers', name="inc"))
fig.add_trace(go.Scatter(x=x_dec_interpolated, y=y_dec_interpolated, mode='lines+markers', name="dec"))
fig.update_layout(
title='Bid ladder',
xaxis_title='Volume',
yaxis_title='Price',
hovermode='x unified'
)
fig.show()
# also print the prices needed for 100MW, 200MW, 300MW, 400MW, 500MW, 600MW, 700MW, 800MW, 900MW, 1000MW
for i in range(1, 11):
# get first cummulative_bid i where i*100 > cummulative_volume of i and smaller than i+1
mw = 100
for bid in self.bid_ladder:
if bid[0] > mw*i:
print(f"{mw*i}MW: {bid[1]}")
break
print(f"Max: {self.bid_ladder[-1][1]}")
def get_imbalance_price_2023(self, NRV_PREV, NRV):
pass
def get_imbalance_price_2023(self, datetime, NRV_PREV, NRV):
MIP = self.get_imbalance_price(datetime, abs(NRV))
MDP = self.get_imbalance_price(datetime, -abs(NRV))
return calculate_imbalance_price_2023(-NRV_PREV, -NRV, MIP, MDP)
# SI = -NRV
def calculate_imbalance_price_2023(SI_PREV: float, SI: float, MIP: float, MDP: float):
@@ -107,8 +136,4 @@ def calculate_imbalance_price_2023(SI_PREV: float, SI: float, MIP: float, MDP: f
imbalance_price = (pos_imbalance_price + neg_imbalance_price) / 2
# return alpha, imbalance price
return alpha, imbalance_price
return alpha, imbalance_price