コード例 #1
0
def test_iaa_event_bid_split_and_trade_correctly_populate_forwarded_bid_entries(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

    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

    if partial:
        residual_energy = 0.2
        residual_id = "resid"
        original_bid = Bid(*low_to_high_engine.markets.target._bids[0])
        accepted_bid = original_bid._replace(
            price=(original_bid.energy-residual_energy) * (original_bid.price/original_bid.energy),
            energy=original_bid.energy-residual_energy)

        residual_bid = original_bid._replace(
            id=residual_id,
            price=residual_energy * (original_bid.price / original_bid.energy),
            energy=residual_energy)

        low_to_high_engine.event_bid_split(market_id=low_to_high_engine.markets.target,
                                           original_bid=original_bid,
                                           accepted_bid=accepted_bid,
                                           residual_bid=residual_bid)
        assert set(low_to_high_engine.forwarded_bids.keys()) == \
            {original_bid.id, accepted_bid.id, residual_bid.id, "uuid", "id3", "id2"}
    else:
        original_bid = low_to_high_engine.markets.target._bids[0]
        accepted_bid = deepcopy(original_bid)
        residual_bid = None

    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=residual_bid))

    if partial:
        # "id" gets traded in the target market, "id2" gets split in the source market, too
        assert set(low_to_high_engine.forwarded_bids.keys()) == {residual_bid.id, "uuid", "id3"}
    else:
        # "id" and "id2" get traded in both target and source,
        # left over is id3 and its forwarded instance uuid
        assert set(low_to_high_engine.forwarded_bids.keys()) == {"uuid", "id3"}
コード例 #2
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
コード例 #3
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
コード例 #4
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
        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