Esempio n. 1
0
def test_iaa_event_trade_buys_accepted_bid(iaa_double_sided):
    iaa_double_sided.higher_market.forwarded_bid = \
        iaa_double_sided.higher_market.forwarded_bid
    iaa_double_sided.event_bid_traded(
        bid_trade=Trade('trade_id', pendulum.now(tz=TIME_ZONE),
                        iaa_double_sided.higher_market.forwarded_bid, 'owner',
                        'someone_else'),
        market_id=iaa_double_sided.higher_market.id)
    assert len(iaa_double_sided.lower_market.calls_energy_bids) == 1

    expected_price = 10 * (1 -
                           iaa_double_sided.lower_market.transfer_fee_ratio)
    assert iaa_double_sided.higher_market.forwarded_bid.price == expected_price
    assert iaa_double_sided.lower_market.calls_bids_price[-1] == 10.0
Esempio n. 2
0
def test_event_trade(area_test2, commercial_test2):
    commercial_test2.event_activate()
    commercial_test2.event_market_cycle()
    traded_offer = Offer(id='id', price=20, energy=1, seller='FakeArea',)
    commercial_test2.event_trade(market_id=area_test2.test_market.id,
                                 trade=Trade(id='id',
                                             time='time',
                                             offer=traded_offer,
                                             seller='FakeArea',
                                             buyer='buyer'
                                             )
                                 )
    assert len(area_test2.test_market.created_offers) == 1
    assert area_test2.test_market.created_offers[-1].energy == sys.maxsize
Esempio n. 3
0
    def test_matching_list_affects_only_matches_after_start_index(self):
        matchings = [
            BidOfferMatch(offer=Offer('offer_id', pendulum.now(), 1, 1, 'S'),
                          offer_energy=1,
                          bid=Bid('bid_id', pendulum.now(), 1, 1, 'B', 'S'),
                          bid_energy=1),
            BidOfferMatch(offer=Offer('offer_id2', pendulum.now(), 2, 2, 'S'),
                          offer_energy=2,
                          bid=Bid('bid_id2', pendulum.now(), 2, 2, 'B', 'S'),
                          bid_energy=2),
            BidOfferMatch(offer=Offer('offer_id', pendulum.now(), 1, 1, 'S'),
                          offer_energy=1,
                          bid=Bid('bid_id', pendulum.now(), 1, 1, 'B', 'S'),
                          bid_energy=1)
        ]

        offer_trade = Trade('trade',
                            1,
                            Offer('offer_id', pendulum.now(), 1, 1, 'S'),
                            'S',
                            'B',
                            residual=Offer('residual_offer', pendulum.now(),
                                           0.5, 0.5, 'S'))
        bid_trade = Trade('bid_trade',
                          1,
                          Bid('bid_id2', pendulum.now(), 1, 1, 'S', 'B'),
                          'S',
                          'B',
                          residual=Bid('residual_bid_2', pendulum.now(), 1, 1,
                                       'S', 'B'))

        matchings = TwoSidedPayAsClear._replace_offers_bids_with_residual_in_matching_list(
            matchings, 1, offer_trade, bid_trade)
        assert len(matchings) == 3
        assert matchings[0].offer.id == 'offer_id'
        assert matchings[1].bid.id == 'residual_bid_2'
        assert matchings[2].offer.id == 'residual_offer'
Esempio n. 4
0
def test_iaa_event_trade_buys_accepted_bid(iaa_double_sided):
    iaa_double_sided.higher_market.forwarded_bid = \
        iaa_double_sided.higher_market.forwarded_bid._replace(price=20)
    iaa_double_sided.event_bid_traded(
        bid_trade=Trade('trade_id',
                        pendulum.now(tz=TIME_ZONE),
                        iaa_double_sided.higher_market.forwarded_bid,
                        'owner',
                        'someone_else',
                        price_drop=False),
        market_id=iaa_double_sided.higher_market.id)
    assert len(iaa_double_sided.lower_market.calls_energy_bids) == 1

    assert iaa_double_sided.higher_market.forwarded_bid.price == 20.0
    assert iaa_double_sided.lower_market.calls_bids_price[-1] == 10.0
Esempio n. 5
0
    def accept_bid(self, bid, energy, seller, buyer=None, *, time=None, trade_rate: float = None,
                   trade_offer_info=None, already_tracked=False, seller_origin=None):
        self.calls_energy_bids.append(energy)
        self.calls_bids.append(bid)
        self.calls_bids_price.append(bid.price)
        if trade_rate is None:
            trade_rate = bid.price / bid.energy
        else:
            assert trade_rate <= (bid.price / bid.energy)

        market_bid = [b for b in self._bids if b.id == bid.id][0]
        if energy < market_bid.energy:
            residual_energy = bid.energy - energy
            residual = Bid('res', bid.price, residual_energy, bid.buyer, seller,
                           buyer_origin='res')
            traded = Bid(bid.id, (trade_rate * energy), energy, bid.buyer, seller,
                         buyer_origin='res')
            return Trade('trade_id', time, traded, traded.seller, bid.buyer, residual,
                         buyer_origin=bid.buyer_origin, seller_origin=seller_origin)
        else:
            traded = Bid(bid.id, (trade_rate * energy), energy, bid.buyer, seller,
                         buyer_origin=bid.id)
            return Trade('trade_id', time, traded, traded.seller, bid.buyer,
                         buyer_origin=bid.buyer_origin, seller_origin=seller_origin)
Esempio n. 6
0
 def __init__(self, count):
     self.count = count
     self.id = str(count)
     self.trade = Trade('id', 'time',
                        Offer('id', now(), 11.8, 0.5, 'FakeArea'),
                        'FakeArea', 'buyer')
     self.created_offers = []
     self.offers = {
         'id': Offer('id', now(), 11.8, 0.5, 'FakeArea'),
         'id2': Offer('id2', now(), 20, 0.5, 'A'),
         'id3': Offer('id3', now(), 20, 1, 'A'),
         'id4': Offer('id4', now(), 19, 5.1, 'A')
     }
     self.bids = {}
     self.created_balancing_offers = []
Esempio n. 7
0
def test_assert_if_trade_rate_is_higher_than_bid_rate(
        load_hours_strategy_test3):
    market_id = 0
    load_hours_strategy_test3._bids[market_id] = \
        [Bid("bid_id", now(), 30, 1, buyer="FakeArea", seller="producer")]
    expensive_bid = Bid("bid_id",
                        now(),
                        31,
                        1,
                        buyer="FakeArea",
                        seller="producer")
    trade = Trade("trade_id", "time", expensive_bid, load_hours_strategy_test3,
                  "buyer")

    with pytest.raises(AssertionError):
        load_hours_strategy_test3.event_trade(market_id=market_id, trade=trade)
Esempio n. 8
0
def test_iaa_event_offer_split_and_trade_correctly_populate_forwarded_offer_entries(
        iaa2):
    residual_offer_id = 'res_id'
    original_offer_id = 'id'
    original = iaa2.higher_market.forwarded_offer
    assert original.energy == 2
    accepted = Offer(original.id, pendulum.now(), 1, 1, original.seller)
    residual = Offer(residual_offer_id, pendulum.now(), 1, 1, original.seller)

    iaa2.event_offer_split(market_id=iaa2.higher_market.id,
                           original_offer=original,
                           accepted_offer=accepted,
                           residual_offer=residual)
    engine = next(
        (e for e in iaa2.engines if residual_offer_id in e.forwarded_offers),
        None)
    assert engine is not None, "Residual of forwarded offers not found in forwarded_offers"

    # after the split event:
    # all three offer ids are part of the forwarded_offer member
    assert set(engine.forwarded_offers.keys()) == {
        residual_offer_id, original_offer_id, 'uuid'
    }
    # and the accepted offer was added
    assert engine.forwarded_offers[
        original_offer_id].target_offer.energy == accepted.energy
    # and the residual offer was added
    assert engine.forwarded_offers[
        residual_offer_id].target_offer.energy == accepted.energy

    iaa2.event_trade(trade=Trade('trade_id',
                                 pendulum.now(tz=TIME_ZONE),
                                 accepted,
                                 'owner',
                                 'someone_else',
                                 residual,
                                 fee_price=0.0),
                     market_id=iaa2.lower_market.id)

    # after the trade event:
    # the forwarded_offers only contain the residual offer
    assert set(engine.forwarded_offers.keys()) == {residual_offer_id}
    offer_info = engine.forwarded_offers[residual_offer_id]
    assert offer_info.source_offer.id == "uuid"
    assert offer_info.target_offer.id == residual_offer_id
Esempio n. 9
0
def test_iaa_event_trade_buys_partial_accepted_bid(iaa_double_sided):
    iaa_double_sided._get_market_from_market_id = lambda x: iaa_double_sided.higher_market
    total_bid = iaa_double_sided.higher_market.forwarded_bid
    accepted_bid_price = (total_bid.price / total_bid.energy) * 1
    residual_bid_price = (total_bid.price / total_bid.energy) * 0.1
    accepted_bid = Bid(total_bid.id, accepted_bid_price, 1, total_bid.buyer,
                       total_bid.seller)
    residual_bid = Bid('residual_bid', residual_bid_price, 0.1,
                       total_bid.buyer, total_bid.seller)
    iaa_double_sided.event_bid_changed(
        market_id=iaa_double_sided.higher_market,
        existing_bid=total_bid,
        new_bid=residual_bid)
    iaa_double_sided.event_bid_traded(
        bid_trade=Trade('trade_id', pendulum.now(tz=TIME_ZONE), accepted_bid,
                        'owner', 'someone_else', 'residual_offer'),
        market_id=iaa_double_sided.higher_market.id)
    assert iaa_double_sided.lower_market.calls_energy_bids[0] == 1
def test_event_trade(area_test2, bus_test2):
    bus_test2.event_activate()
    bus_test2.event_market_cycle()
    traded_offer = Offer(
        id="id",
        time=pendulum.now(),
        price=20,
        energy=1,
        seller="FakeArea",
    )
    bus_test2.event_trade(market_id=area_test2.test_market.id,
                          trade=Trade(id="id",
                                      time="time",
                                      offer_bid=traded_offer,
                                      seller="FakeArea",
                                      buyer="buyer"))
    assert len(area_test2.test_market.created_offers) == 1
    assert area_test2.test_market.created_offers[-1].energy == sys.maxsize
Esempio n. 11
0
def test_iaa_event_trade_buys_partial_accepted_bid(iaa_double_sided):
    iaa_double_sided._get_market_from_market_id = lambda x: iaa_double_sided.higher_market
    original_bid = iaa_double_sided.higher_market.forwarded_bid
    accepted_bid_price = (original_bid.price / original_bid.energy) * 1
    residual_bid_price = (original_bid.price / original_bid.energy) * 0.1
    accepted_bid = Bid(original_bid.id, original_bid.time, accepted_bid_price,
                       1, original_bid.buyer)
    residual_bid = Bid('residual_bid', original_bid.time, residual_bid_price,
                       0.1, original_bid.buyer)
    iaa_double_sided.event_bid_split(market_id=iaa_double_sided.higher_market,
                                     original_bid=original_bid,
                                     accepted_bid=accepted_bid,
                                     residual_bid=residual_bid)
    iaa_double_sided.event_bid_traded(
        bid_trade=Trade('trade_id', pendulum.now(tz=TIME_ZONE), accepted_bid,
                        'owner', 'someone_else', 'residual_offer'),
        market_id=iaa_double_sided.higher_market.id)
    assert iaa_double_sided.lower_market.calls_energy_bids[0] == 1
Esempio n. 12
0
 def accept_offer(self,
                  offer,
                  buyer,
                  *,
                  energy=None,
                  time=None,
                  already_tracked=False,
                  trade_rate: float = None,
                  trade_bid_info=None,
                  buyer_origin=None):
     trade = Trade('trade_id',
                   time,
                   offer,
                   offer.seller,
                   buyer,
                   seller_origin=offer.seller_origin,
                   buyer_origin=buyer_origin)
     self.traded_offers.append(trade)
     return trade
Esempio n. 13
0
 def accept_offer(self,
                  offer,
                  id,
                  *,
                  energy=None,
                  time=None,
                  already_tracked=False,
                  trade_rate: float = None,
                  trade_bid_info=None,
                  buyer_origin=None):
     if self.raises:
         raise MarketException
     else:
         offer.energy = energy
         return Trade('trade',
                      0,
                      offer,
                      offer.seller,
                      'FakeOwner',
                      seller_origin=offer.seller_origin,
                      buyer_origin=buyer_origin)
Esempio n. 14
0
def test_iaa_event_trade_bid_does_not_update_forwarded_bids_on_partial(iaa_bid, called, partial):
    iaa_bid.lower_market.delete_bid = called
    low_to_high_engine = iaa_bid.engines[0]
    source_bid = list(low_to_high_engine.markets.source.bids.values())[0]
    target_bid = list(low_to_high_engine.markets.target.bids.values())[0]
    bidinfo = BidInfo(source_bid=source_bid, target_bid=target_bid)
    low_to_high_engine.forwarded_bids[source_bid.id] = bidinfo
    low_to_high_engine.forwarded_bids[target_bid.id] = bidinfo
    low_to_high_engine.event_bid_traded(
        bid_trade=Trade('trade_id',
                        pendulum.now(tz=TIME_ZONE),
                        low_to_high_engine.markets.target.bids[target_bid.id],
                        seller='someone_else',
                        buyer='owner',
                        residual=partial))
    if not partial:
        assert source_bid.id not in low_to_high_engine.forwarded_bids
        assert target_bid.id not in low_to_high_engine.forwarded_bids
    else:
        assert source_bid.id in low_to_high_engine.forwarded_bids
        assert target_bid.id in low_to_high_engine.forwarded_bids
def test_event_trade_after_offer_changed_partial_offer(area_test2,
                                                       commercial_test2):
    original_offer = Offer(id='old_id',
                           time=pendulum.now(),
                           price=20,
                           energy=1,
                           seller='FakeArea')
    accepted_offer = Offer(id='old_id',
                           time=pendulum.now(),
                           price=15,
                           energy=0.75,
                           seller='FakeArea')
    residual_offer = Offer(id='res_id',
                           time=pendulum.now(),
                           price=5,
                           energy=0.25,
                           seller='FakeArea')
    commercial_test2.offers.post(original_offer, area_test2.test_market.id)
    commercial_test2.event_offer_split(market_id=area_test2.test_market.id,
                                       original_offer=original_offer,
                                       accepted_offer=accepted_offer,
                                       residual_offer=residual_offer)
    assert original_offer.id in commercial_test2.offers.split
    assert commercial_test2.offers.split[original_offer.id] == accepted_offer
    commercial_test2.event_trade(market_id=area_test2.test_market.id,
                                 trade=Trade(id='id',
                                             time='time',
                                             offer=original_offer,
                                             seller='FakeArea',
                                             buyer='buyer'))

    assert residual_offer in commercial_test2.offers.posted
    assert commercial_test2.offers.posted[
        residual_offer] == area_test2.test_market.id
    assert len(commercial_test2.offers.posted) == 1
    assert len(commercial_test2.offers.split) == 1
    assert len(commercial_test2.offers.sold) == 1
    assert original_offer in commercial_test2.offers.sold_in_market(
        area_test2.test_market.id)
Esempio n. 16
0
def test_event_bid_traded_removes_bid_from_pending_if_energy_req_0(load_hours_strategy_test5,
                                                                   market_test2,
                                                                   called):
    ConstSettings.IAASettings.MARKET_TYPE = 2

    trade_market = load_hours_strategy_test5.area.next_market
    load_hours_strategy_test5.remove_bid_from_pending = called
    load_hours_strategy_test5.event_activate()
    load_hours_strategy_test5.area.markets = {TIME: trade_market}
    load_hours_strategy_test5.event_market_cycle()
    load_hours_strategy_test5.event_tick(area=area_test2)
    bid = list(load_hours_strategy_test5._bids.values())[0][0]
    # Increase energy requirement to cover the energy from the bid + threshold
    load_hours_strategy_test5.energy_requirement_Wh[TIME] = bid.energy * 1000 + 0.000009
    trade = Trade('idt', None, bid, 'B', load_hours_strategy_test5.owner.name, residual=True)
    load_hours_strategy_test5.event_bid_traded(market_id=trade_market.id, bid_trade=trade)

    assert len(load_hours_strategy_test5.remove_bid_from_pending.calls) == 1
    assert load_hours_strategy_test5.remove_bid_from_pending.calls[0][0][0] == repr(bid.id)
    assert load_hours_strategy_test5.remove_bid_from_pending.calls[0][0][1] == \
        repr(trade_market)

    ConstSettings.IAASettings.MARKET_TYPE = 1
Esempio n. 17
0
 def test_publish_event_converts_python_objects_to_json(self):
     offer = Offer("1", 2, 3, "A")
     trade = Trade("2", now(), Offer("accepted", 7, 8, "Z"), "B", "C")
     new_offer = Offer("3", 4, 5, "D")
     existing_offer = Offer("4", 5, 6, "E")
     kwargs = {"offer": offer,
               "trade": trade,
               "new_offer": new_offer,
               "existing_offer": existing_offer}
     for dispatcher in [self.area.dispatcher.market_event_dispatcher,
                        self.device1.dispatcher.market_event_dispatcher,
                        self.device2.dispatcher.market_event_dispatcher]:
         dispatcher.publish_event(dispatcher.area.uuid, MarketEvent.OFFER, **kwargs)
         assert dispatcher.redis.publish.call_count == 1
         payload = json.loads(dispatcher.redis.publish.call_args_list[0][0][1])
         assert isinstance(payload["kwargs"]["offer"], str)
         assert offer_from_JSON_string(payload["kwargs"]["offer"]) == offer
         assert isinstance(payload["kwargs"]["trade"], str)
         assert trade_from_JSON_string(payload["kwargs"]["trade"]) == trade
         assert isinstance(payload["kwargs"]["new_offer"], str)
         assert offer_from_JSON_string(payload["kwargs"]["new_offer"]) == new_offer
         assert isinstance(payload["kwargs"]["existing_offer"], str)
         assert offer_from_JSON_string(payload["kwargs"]["existing_offer"]) == existing_offer
    def test_publish_event_subscribes_to_response_and_publishes(self):
        offer = Offer("1", now(), 2, 3, "A")
        trade = Trade("2", now(), Offer("accepted", now(), 7, 8, "Z"), "B", "C")
        new_offer = Offer("3", now(), 4, 5, "D")
        existing_offer = Offer("4", now(), 5, 6, "E")
        kwargs = {"offer": offer,
                  "trade": trade,
                  "new_offer": new_offer,
                  "existing_offer": existing_offer}

        self.publisher.publish_event(MarketEvent.OFFER, **kwargs)
        self.publisher.redis.sub_to_channel.assert_called_once_with(
            "market/test_id/notify_event/response",
            self.publisher.response_callback
        )

        expected_result = {k: v.to_json_string() for k, v in kwargs.items()}
        self.publisher.redis.publish.assert_called_once()
        assert self.publisher.redis.publish.call_args_list[0][0][0] == \
            "market/test_id/notify_event"
        publish_call_args = json.loads(self.publisher.redis.publish.call_args_list[0][0][1])
        assert publish_call_args["event_type"] == MarketEvent.OFFER.value
        assert len(DeepDiff(publish_call_args["kwargs"], expected_result)) == 0
def test_event_trade_after_offer_changed_partial_offer(area_test2, bus_test2):
    original_offer = Offer(id="old_id",
                           time=pendulum.now(),
                           price=20,
                           energy=1,
                           seller="FakeArea")
    accepted_offer = Offer(id="old_id",
                           time=pendulum.now(),
                           price=15,
                           energy=0.75,
                           seller="FakeArea")
    residual_offer = Offer(id="res_id",
                           time=pendulum.now(),
                           price=5,
                           energy=0.25,
                           seller="FakeArea")
    bus_test2.offers.post(original_offer, area_test2.test_market.id)
    bus_test2.event_offer_split(market_id=area_test2.test_market.id,
                                original_offer=original_offer,
                                accepted_offer=accepted_offer,
                                residual_offer=residual_offer)
    assert original_offer.id in bus_test2.offers.split
    assert bus_test2.offers.split[original_offer.id] == accepted_offer
    bus_test2.event_trade(market_id=area_test2.test_market.id,
                          trade=Trade(id="id",
                                      time="time",
                                      offer_bid=original_offer,
                                      seller="FakeArea",
                                      buyer="buyer"))

    assert residual_offer in bus_test2.offers.posted
    assert bus_test2.offers.posted[residual_offer] == area_test2.test_market.id
    assert len(bus_test2.offers.posted) == 1
    assert len(bus_test2.offers.split) == 1
    assert len(bus_test2.offers.sold) == 1
    assert original_offer in bus_test2.offers.sold_in_market(
        area_test2.test_market.id)
Esempio n. 20
0
def test_balancing_offers_are_created_if_device_in_registry(
        balancing_fixture, area_test2):
    ConstSettings.BalancingSettings.ENABLE_BALANCING_MARKET = True
    DeviceRegistry.REGISTRY = {'FakeArea': (30, 40)}
    balancing_fixture.event_activate()
    balancing_fixture.event_market_cycle()
    balancing_fixture.event_balancing_market_cycle()
    expected_balancing_demand_energy = \
        balancing_fixture.balancing_energy_ratio.demand * \
        balancing_fixture.energy_per_slot_Wh
    actual_balancing_demand_energy = \
        area_test2.test_balancing_market.created_balancing_offers[0].energy
    assert len(area_test2.test_balancing_market.created_balancing_offers) == 1
    assert actual_balancing_demand_energy == -expected_balancing_demand_energy
    actual_balancing_demand_price = \
        area_test2.test_balancing_market.created_balancing_offers[0].price

    assert actual_balancing_demand_price == expected_balancing_demand_energy * 30
    selected_offer = area_test2.current_market.sorted_offers[0]
    balancing_fixture.energy_requirement_Wh[area_test2.current_market.time_slot] = \
        selected_offer.energy * 1000.0
    balancing_fixture.event_trade(market_id=area_test2.current_market.id,
                                  trade=Trade(id='id',
                                              time=area_test2.now,
                                              offer=selected_offer,
                                              seller='B',
                                              buyer='FakeArea'))
    assert len(area_test2.test_balancing_market.created_balancing_offers) == 2
    actual_balancing_supply_energy = \
        area_test2.test_balancing_market.created_balancing_offers[1].energy
    expected_balancing_supply_energy = \
        selected_offer.energy * balancing_fixture.balancing_energy_ratio.supply
    assert actual_balancing_supply_energy == expected_balancing_supply_energy
    actual_balancing_supply_price = \
        area_test2.test_balancing_market.created_balancing_offers[1].price
    assert actual_balancing_supply_price == expected_balancing_supply_energy * 40
    DeviceRegistry.REGISTRY = {}
Esempio n. 21
0
def test_iaa_event_trade_bid_updates_forwarded_bids_on_partial(
        iaa_bid, called, partial):
    iaa_bid.lower_market.delete_bid = called
    low_to_high_engine = iaa_bid.engines[0]
    iaa_bid._get_market_from_market_id = lambda x: low_to_high_engine.markets.target
    if partial:
        accepted_bid = Bid(*low_to_high_engine.markets.target._bids[0])
        accepted_bid = \
            accepted_bid._replace(price=(accepted_bid.energy-0.2) *
                                        (accepted_bid.price/accepted_bid.energy),
                                  energy=accepted_bid.energy-0.2)
        partial_bid = Bid('1234', 12, 0.2, 'owner', 'someone_else')
        low_to_high_engine.event_bid_changed(
            market_id=low_to_high_engine.markets.target,
            existing_bid=accepted_bid,
            new_bid=partial_bid)
    else:
        accepted_bid = low_to_high_engine.markets.target._bids[0]
        partial_bid = False
    source_bid = list(low_to_high_engine.markets.source.bids.values())[0]
    target_bid = list(low_to_high_engine.markets.target.bids.values())[0]
    bidinfo = BidInfo(source_bid=source_bid, target_bid=target_bid)
    low_to_high_engine.forwarded_bids[source_bid.id] = bidinfo
    low_to_high_engine.forwarded_bids[target_bid.id] = bidinfo

    low_to_high_engine.event_bid_traded(bid_trade=Trade('trade_id',
                                                        pendulum.now(
                                                            tz=TIME_ZONE),
                                                        accepted_bid,
                                                        seller='someone_else',
                                                        buyer='owner',
                                                        residual=partial_bid))

    assert source_bid.id not in low_to_high_engine.forwarded_bids
    assert target_bid.id not in low_to_high_engine.forwarded_bids
    if partial:
        assert partial_bid.id in low_to_high_engine.forwarded_bids
    def accept_bid(self, bid: Bid, energy: float = None,
                   seller: str = None, buyer: str = None, already_tracked: bool = False,
                   trade_rate: float = None, trade_offer_info=None, seller_origin=None):
        market_bid = self.bids.pop(bid.id, None)
        if market_bid is None:
            raise BidNotFound("During accept bid: " + str(bid))

        seller = market_bid.seller if seller is None else seller
        buyer = market_bid.buyer if buyer is None else buyer
        energy = market_bid.energy if energy is None else energy
        if trade_rate is None:
            trade_rate = market_bid.price / market_bid.energy

        assert trade_rate <= (market_bid.price / market_bid.energy) + FLOATING_POINT_TOLERANCE, \
            f"trade rate: {trade_rate} market {market_bid.price / market_bid.energy}"

        orig_price = bid.original_bid_price if bid.original_bid_price is not None else bid.price
        residual = None

        if energy <= 0:
            raise InvalidTrade("Energy cannot be negative or zero.")
        elif energy > market_bid.energy:
            raise InvalidTrade("Traded energy cannot be more than the bid energy.")
        elif energy < market_bid.energy:
            # Partial bidding
            energy_portion = energy / market_bid.energy
            residual_price = (1 - energy_portion) * market_bid.price
            residual_energy = market_bid.energy - energy

            # Manually creating the bid in order to not double-charge the fee of the
            # residual bid
            changed_bid = Bid(
                str(uuid.uuid4()), residual_price, residual_energy,
                buyer, seller,
                original_bid_price=(1 - energy_portion) * orig_price,
                buyer_origin=market_bid.buyer_origin
            )

            self.bids[changed_bid.id] = changed_bid
            self.bid_history.append(changed_bid)

            self._notify_listeners(MarketEvent.BID_CHANGED,
                                   existing_bid=bid, new_bid=changed_bid)
            residual = changed_bid

            revenue, fees, final_trade_rate = GridFees.calculate_trade_price_and_fees(
                trade_offer_info, self.transfer_fee_ratio
            )
            self.market_fee += fees
            final_price = energy * final_trade_rate
            bid = Bid(bid.id, final_price, energy, buyer, seller,
                      original_bid_price=energy_portion * orig_price,
                      buyer_origin=bid.buyer_origin)
        else:
            revenue, fees, final_trade_rate = GridFees.calculate_trade_price_and_fees(
                trade_offer_info, self.transfer_fee_ratio
            )
            self.market_fee += fees
            final_price = energy * final_trade_rate
            bid = bid._replace(price=final_price)

        trade = Trade(str(uuid.uuid4()), self._now, bid, seller,
                      buyer, residual, already_tracked=already_tracked,
                      offer_bid_trade_info=GridFees.propagate_original_offer_info_on_bid_trade(
                          trade_offer_info, self.transfer_fee_ratio),
                      buyer_origin=bid.buyer_origin, seller_origin=seller_origin
                      )

        if already_tracked is False:
            self._update_stats_after_trade(trade, bid, bid.buyer, already_tracked)
            log.info(f"[TRADE][BID] [{self.time_slot_str}] {trade}")
            final_bid = bid._replace(price=final_price)
            trade = trade._replace(offer=final_bid)

        self._notify_listeners(MarketEvent.BID_TRADED, bid_trade=trade)
        if not trade.residual:
            self._notify_listeners(MarketEvent.BID_DELETED, bid=market_bid)
        return trade
Esempio n. 23
0
def _trade(price, buyer, energy=1, seller=None, fee_price=None):
    return Trade('id', 0, FakeOffer(price, energy, seller),
                 seller, buyer, None, fee_price=fee_price)
Esempio n. 24
0
    def accept_offer(self,
                     offer_or_id: Union[str, Offer],
                     buyer: str,
                     *,
                     energy: int = None,
                     time: DateTime = None,
                     already_tracked: bool = False,
                     trade_rate: float = None,
                     trade_bid_info=None,
                     buyer_origin=None) -> Trade:
        if self.readonly:
            raise MarketReadOnlyException()

        if isinstance(offer_or_id, Offer):
            offer_or_id = offer_or_id.id
        offer = self.offers.pop(offer_or_id, None)
        if offer is None:
            raise OfferNotFoundException()

        if energy is None:
            energy = offer.energy

        original_offer = offer
        residual_offer = None

        if trade_rate is None:
            trade_rate = offer.energy_rate

        orig_offer_price = self._calculate_original_prices(offer)

        try:
            if time is None:
                time = self.now

            if energy == 0:
                raise InvalidTrade("Energy can not be zero.")
            elif energy < offer.energy:
                # partial energy is requested

                accepted_offer, residual_offer = self.split_offer(
                    offer, energy, orig_offer_price)

                fee_price, trade_price = self.determine_offer_price(
                    energy_portion=energy / accepted_offer.energy,
                    energy=energy,
                    trade_rate=trade_rate,
                    trade_bid_info=trade_bid_info,
                    orig_offer_price=orig_offer_price)

                offer = accepted_offer
                offer.update_price(trade_price)

            elif energy > offer.energy:
                raise InvalidTrade(
                    "Energy can't be greater than offered energy")
            else:
                # Requested energy is equal to offer's energy - just proceed normally
                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:
            # Exception happened - restore offer
            self.offers[offer.id] = offer
            raise

        trade_id, residual_offer = \
            self.bc_interface.handle_blockchain_trade_event(
                offer, buyer, original_offer, residual_offer
            )

        # Delete the accepted offer from self.offers:
        self.offers.pop(offer.id, None)
        offer_bid_trade_info = self.fee_class.propagate_original_bid_info_on_offer_trade(
            trade_original_info=trade_bid_info)
        trade = Trade(trade_id,
                      time,
                      offer,
                      offer.seller,
                      buyer,
                      residual_offer,
                      offer_bid_trade_info=offer_bid_trade_info,
                      seller_origin=offer.seller_origin,
                      buyer_origin=buyer_origin,
                      fee_price=fee_price)
        self.bc_interface.track_trade_event(trade)

        if already_tracked is False:
            self._update_stats_after_trade(trade, offer, buyer)
            log.info(f"[TRADE] [{self.name}] [{self.time_slot_str}] {trade}")

        # TODO: Use non-blockchain non-event-driven version for now for both blockchain and
        # normal runs.
        self._notify_listeners(MarketEvent.TRADE, trade=trade)
        return trade
Esempio n. 25
0
def _trade(price, buyer, energy=1, seller=None, fee_price=0.):
    return Trade('id', now(tz=constants.TIME_ZONE), FakeOffer(price, energy, seller),
                 seller, buyer, None, fee_price=fee_price)
Esempio n. 26
0
    def accept_bid(self,
                   bid: Bid,
                   energy: float = None,
                   seller: str = None,
                   buyer: str = None,
                   already_tracked: bool = False,
                   trade_rate: float = None,
                   trade_offer_info=None,
                   seller_origin=None):
        market_bid = self.bids.pop(bid.id, None)
        if market_bid is None:
            raise BidNotFound("During accept bid: " + str(bid))

        seller = market_bid.seller if seller is None else seller
        buyer = market_bid.buyer if buyer is None else buyer
        energy = market_bid.energy if energy is None else energy

        orig_price = bid.original_bid_price if bid.original_bid_price is not None else bid.price
        residual_bid = None

        if energy <= 0:
            raise InvalidTrade("Energy cannot be negative or zero.")
        elif energy > market_bid.energy:
            raise InvalidTrade(
                "Traded energy cannot be more than the bid energy.")
        elif energy < market_bid.energy:
            # partial bid trade
            accepted_bid, residual_bid = self.split_bid(
                market_bid, energy, orig_price)
            bid = accepted_bid

            # Delete the accepted bid from self.bids:
            try:
                self.bids.pop(accepted_bid.id)
            except KeyError:
                raise BidNotFound(
                    f"Bid {accepted_bid.id} not found in self.bids ({self.name})."
                )
        else:
            # full bid trade, nothing further to do here
            pass

        fee_price, trade_price = self.determine_bid_price(
            trade_offer_info, energy)
        bid = bid._replace(price=trade_price)

        # Do not adapt grid fees when creating the bid_trade_info structure, to mimic
        # the behavior of the forwarded bids which use the source market fee.
        updated_bid_trade_info = self.fee_class.propagate_original_offer_info_on_bid_trade(
            trade_offer_info, ignore_fees=True)

        trade = Trade(str(uuid.uuid4()),
                      self.now,
                      bid,
                      seller,
                      buyer,
                      residual_bid,
                      already_tracked=already_tracked,
                      offer_bid_trade_info=updated_bid_trade_info,
                      buyer_origin=bid.buyer_origin,
                      seller_origin=seller_origin,
                      fee_price=fee_price)

        if already_tracked is False:
            self._update_stats_after_trade(trade, bid, bid.buyer,
                                           already_tracked)
            log.info(
                f"[TRADE][BID] [{self.name}] [{self.time_slot_str}] {trade}")

        self._notify_listeners(MarketEvent.BID_TRADED, bid_trade=trade)
        return trade
Esempio n. 27
0
def _trade(price, buyer, energy=1, seller=None):
    return Trade('id', 0, FakeOffer(price, energy, seller), 'seller', buyer,
                 None)
Esempio n. 28
0
 def accept_offer(self, offer, id, *, energy=None, time=None, price_drop=False):
     if self.raises:
         raise MarketException
     else:
         offer.energy = energy
         return Trade('trade', 0, offer, offer.seller, 'FakeOwner')
 def accept_offer(self, **kwargs):
     return Trade("", "", "", "", "")
Esempio n. 30
0
def test_iaa_event_trade_buys_accepted_offer(iaa2):
    iaa2.event_trade(trade=Trade('trade_id', pendulum.now(tz=TIME_ZONE),
                                 iaa2.higher_market.forwarded_offer, 'owner',
                                 'someone_else'),
                     market_id=iaa2.higher_market.id)
    assert len(iaa2.lower_market.calls_energy) == 1