Skip to content

Commit

Permalink
Merge pull request #1732 from gridsingularity/feature/GSYE-650-fix-or…
Browse files Browse the repository at this point in the history
…der_updater

GSYE-650: Fix OrderUpdater order rate progression
  • Loading branch information
hannesdiedrich authored Jan 24, 2024
2 parents a89cf46 + 4c9cb08 commit 735d07f
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 23 deletions.
3 changes: 2 additions & 1 deletion src/gsy_e/models/market/one_sided.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,10 @@ def accept_offer( # pylint: disable=too-many-locals
fee_price, trade_price = self._determine_offer_price(
1, energy, trade_rate, trade_bid_info, orig_offer_price)
offer.update_price(trade_price)
except Exception:
except Exception as ex:
# Exception happened - restore offer
self.offers[offer.id] = offer
log.error("Accept offer (%s) failed due to: %s", offer, ex)
raise

trade_id, residual_offer = self.bc_interface.handle_blockchain_trade_event(
Expand Down
4 changes: 2 additions & 2 deletions src/gsy_e/models/market/two_sided.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,15 +335,15 @@ def match_recommendations(
# by seller / buyer.
if not market_offer:
market_offer = self._get_offer_from_seller_origin_id(
recommended_pair.offer["seller_origin_id"])
recommended_pair.offer["seller"]["origin_uuid"])
if market_offer is None:
raise InvalidBidOfferPairException("Offer does not exist in the market")
recommended_pair.offer = market_offer.serializable_dict()

market_bid = self.bids.get(recommended_pair.bid["id"])
if not market_bid:
market_bid = self._get_bid_from_buyer_origin_id(
recommended_pair.bid["buyer_origin_id"])
recommended_pair.bid["buyer"]["origin_uuid"])
if market_bid is None:
raise InvalidBidOfferPairException("Bid does not exist in the market")
recommended_pair.bid = market_bid.serializable_dict()
Expand Down
19 changes: 9 additions & 10 deletions src/gsy_e/models/strategy/order_updater.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from dataclasses import dataclass
from typing import List

from gsy_framework.constants_limits import GlobalConfig
from pendulum import duration, DateTime, Duration

from gsy_e.models.market import MarketSlotParams
Expand Down Expand Up @@ -37,7 +36,7 @@ def __init__(self, parameters: OrderUpdaterParameters,
self._market_params = market_params
self._update_times: List[DateTime] = self._calculate_update_timepoints(
self._market_params.opening_time,
self._market_params.closing_time - GlobalConfig.tick_length,
self._market_params.closing_time - self._parameters.update_interval,
self._parameters.update_interval)

@staticmethod
Expand All @@ -52,18 +51,18 @@ def _calculate_update_timepoints(
return timepoints

def is_time_for_update(
self, current_time_slot: DateTime) -> bool:
self, current_time: DateTime) -> bool:
"""Check if the orders need to be updated."""
return current_time_slot in self._update_times
return current_time in self._update_times

def get_energy_rate(self, current_time_slot: DateTime) -> float:
def get_energy_rate(self, current_time: DateTime) -> float:
"""Calculate energy rate for the current time slot."""
assert current_time_slot >= self._market_params.opening_time
if current_time_slot == self._market_params.closing_time - GlobalConfig.tick_length:
return self._parameters.final_rate
time_elapsed_since_start = current_time_slot - self._market_params.opening_time
assert current_time >= self._market_params.opening_time
time_elapsed_since_start = current_time - self._market_params.opening_time
total_slot_length = (
self._market_params.closing_time - self._market_params.opening_time)
self._market_params.closing_time -
self._parameters.update_interval -
self._market_params.opening_time)
rate_range = abs(self._parameters.final_rate - self._parameters.initial_rate)
rate_diff_from_initial = (time_elapsed_since_start / total_slot_length) * rate_range
if self._parameters.initial_rate < self._parameters.final_rate:
Expand Down
3 changes: 2 additions & 1 deletion tests/strategies/forward/test_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ def test_forward_strategy_updates_orders_on_tick(
market_params = area.forward_markets[
market_type].get_market_parameters_for_market_slot(time_slot)
slot_completion_ratio = updater_params.update_interval.total_minutes() / (
market_params.closing_time - market_params.opening_time
market_params.closing_time - updater_params.update_interval
- market_params.opening_time
).total_minutes()
if isinstance(strategy, ForwardPVStrategy):
assert isclose(
Expand Down
10 changes: 8 additions & 2 deletions tests/strategies/test_heat_pump.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,14 @@ def test_orders_are_updated_correctly_on_spot_on_tick(self, heatpump_fixture):
now_mock.return_value = (
CURRENT_MARKET_SLOT + updater_params.update_interval)
strategy.event_tick()
# 15 minutes / 15 cts --> 1 ct/kWh after 1 minute
self._assert_bid(list(market_object.bids.values()), strategy, energy_to_buy, 1)
update_interval = duration(
minutes=ConstSettings.GeneralSettings.DEFAULT_UPDATE_INTERVAL)
initial_rate = GlobalConfig.FEED_IN_TARIFF
final_rate = GlobalConfig.market_maker_rate
expected_rate = (final_rate - initial_rate) / (
(duration(minutes=15)-update_interval) / update_interval)
self._assert_bid(
list(market_object.bids.values()), strategy, energy_to_buy, expected_rate)

@staticmethod
@pytest.mark.parametrize("heatpump_fixture", [
Expand Down
14 changes: 7 additions & 7 deletions tests/strategies/test_order_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from gsy_e.models.market import MarketSlotParams
from gsy_e.models.strategy.order_updater import OrderUpdater, OrderUpdaterParameters

UPDATE_INTERVAL = duration(minutes=5)


@pytest.fixture(name="order_updater_fixture")
def fixture_order_updater():
Expand All @@ -15,7 +17,7 @@ def fixture_order_updater():
opening_time = datetime(year=2022, month=1, day=1, hour=12, minute=30)

updater = OrderUpdater(
OrderUpdaterParameters(duration(minutes=5), initial_rate=30, final_rate=70),
OrderUpdaterParameters(UPDATE_INTERVAL, initial_rate=30, final_rate=70),
MarketSlotParams(
opening_time=opening_time, closing_time=opening_time + duration(minutes=30),
delivery_start_time=opening_time + duration(hours=2),
Expand All @@ -37,9 +39,6 @@ def test_order_updater_calculates_update_times_correctly(order_updater_fixture):
assert not updater.is_time_for_update(opening_time - duration(minutes=1))
# Returns False at the market close time
assert not updater.is_time_for_update(opening_time + duration(minutes=30))
# Returns True at one tick before the market close time
assert updater.is_time_for_update(
opening_time + duration(minutes=30) - duration(seconds=5))

current_time = opening_time
while current_time < opening_time + duration(minutes=30) - duration(seconds=5):
Expand All @@ -52,11 +51,12 @@ def test_get_energy_rate(order_updater_fixture):
updater = order_updater_fixture[1]
rate_range = 70 - 30
update_time = duration(minutes=30)
closing_time = opening_time + update_time - duration(seconds=5)
closing_time = opening_time + update_time
current_time = opening_time
while current_time < closing_time:
expected_rate = 30 + rate_range * ((current_time - opening_time) / update_time)
expected_rate = 30 + rate_range * (
(current_time - opening_time) / (update_time - UPDATE_INTERVAL))
assert isclose(updater.get_energy_rate(current_time), expected_rate)
current_time += duration(minutes=5)

assert updater.get_energy_rate(closing_time) == 70
assert updater.get_energy_rate(closing_time - UPDATE_INTERVAL) == 70

0 comments on commit 735d07f

Please sign in to comment.