Beispiel #1
0
    def _export_trade_csv_files(self,
                                area: Area,
                                directory: dir,
                                balancing: bool = False,
                                is_first: bool = False):
        """
        Exports files containing individual trades  (*-trades.csv  files)
        return: dict[out_keys]
        """

        if balancing:
            file_path = self._file_path(
                directory, "{}-balancing-trades".format(area.slug))
            labels = ("slot", ) + BalancingTrade._csv_fields()
            past_markets = area.past_balancing_markets
        else:
            file_path = self._file_path(directory,
                                        "{}-trades".format(area.slug))
            labels = ("slot", ) + Trade._csv_fields()
            past_markets = area.past_markets

        try:
            with open(file_path, 'a') as csv_file:
                writer = csv.writer(csv_file)
                if is_first:
                    writer.writerow(labels)
                for market in past_markets:
                    for trade in market.trades:
                        row = (market.time_slot, ) + trade._to_csv()
                        writer.writerow(row)
        except OSError:
            _log.exception("Could not export area trades")
    def accept_offer(self, offer_or_id, buyer, energy=None, time=None,
                     trade_rate: float = None):
        if time is None:
            time = self.time_slot
        offer = offer_or_id
        if (offer.energy > 0 and energy < 0) or (offer.energy < 0 and energy > 0):
            raise InvalidBalancingTradeException("BalancingOffer and energy "
                                                 "are not compatible")

        if abs(energy) < abs(offer.energy):
            residual_energy = offer.energy - energy
            residual = BalancingOffer('res', pendulum.now(), offer.price, residual_energy,
                                      offer.seller)
            traded = BalancingOffer(offer.id, pendulum.now(), offer.price, energy, offer.seller)
            return BalancingTrade('trade_id', time, traded, traded.seller, buyer, residual)
        else:
            return BalancingTrade('trade_id', time, offer, offer.seller, buyer)
Beispiel #3
0
    def accept_offer(self,
                     offer_or_id: Union[str, BalancingOffer],
                     buyer: str,
                     *,
                     energy: int = None,
                     time: DateTime = None,
                     already_tracked: bool = False,
                     trade_rate: float = None,
                     trade_bid_info: float = None,
                     buyer_origin=None) -> BalancingTrade:
        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 (offer.energy > 0 and energy < 0) or (offer.energy < 0
                                                 and energy > 0):
            raise InvalidBalancingTradeException("BalancingOffer and energy "
                                                 "are not compatible")
        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 InvalidBalancingTradeException("Energy can not be zero.")
            elif abs(energy) < abs(offer.energy):
                # partial energy is requested
                assert trade_rate + FLOATING_POINT_TOLERANCE >= (offer.price /
                                                                 offer.energy)

                original_offer = offer

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

                fees, trade_price = self.determine_offer_price(
                    energy / offer.energy, energy, trade_rate, trade_bid_info,
                    orig_offer_price)
                offer = accepted_offer
                offer.update_price(trade_price)

            elif abs(energy) > abs(offer.energy):
                raise InvalidBalancingTradeException(
                    "Energy can't be greater than offered energy")
            else:
                # Requested energy is equal to offer's energy - just proceed normally
                fees, trade_price = self._update_offer_fee_and_calculate_final_price(
                    energy, trade_rate, 1, orig_offer_price
                ) if already_tracked is False else energy * trade_rate
                offer.update_price(trade_price)

        except Exception:
            # Exception happened - restore offer
            self.offers[offer.id] = offer
            raise

        # Delete the accepted offer from self.offers:
        self.offers.pop(offer.id, None)

        trade_id, residual_offer = \
            self.bc_interface.handle_blockchain_trade_event(
                offer, buyer, original_offer, residual_offer
            )
        trade = BalancingTrade(trade_id,
                               time,
                               offer,
                               offer.seller,
                               buyer,
                               residual_offer,
                               seller_origin=offer.seller_origin,
                               buyer_origin=buyer_origin,
                               fee_price=fees)
        self.bc_interface.track_trade_event(trade)

        if already_tracked is False:
            self._update_stats_after_trade(trade, offer, buyer)
            log.info(f"[BALANCING_TRADE] [{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.BALANCING_TRADE, trade=trade)
        return trade
Beispiel #4
0
    def accept_offer(self,
                     offer_or_id: Union[str, BalancingOffer],
                     buyer: str,
                     *,
                     energy: int = None,
                     time: DateTime = None,
                     already_tracked: bool = False,
                     trade_rate: float = None,
                     trade_bid_info: float = None,
                     buyer_origin=None) -> BalancingTrade:
        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 (offer.energy > 0 and energy < 0) or (offer.energy < 0
                                                 and energy > 0):
            raise InvalidBalancingTradeException("BalancingOffer and energy "
                                                 "are not compatible")
        if energy is None:
            energy = offer.energy

        original_offer = offer
        residual_offer = None

        if trade_rate is None:
            trade_rate = offer.price / offer.energy

        orig_offer_price = self._calculate_original_prices(offer)

        self._sorted_offers = sorted(self.offers.values(),
                                     key=lambda o: o.price / o.energy)
        try:
            if time is None:
                time = self._now

            energy_portion = energy / offer.energy
            if energy == 0:
                raise InvalidBalancingTradeException("Energy can not be zero.")
            # partial energy is requested
            elif abs(energy) < abs(offer.energy):
                original_offer = offer
                accepted_offer_id = offer.id \
                    if self.area is None or self.area.bc is None \
                    else offer.real_id

                assert trade_rate + FLOATING_POINT_TOLERANCE >= (offer.price /
                                                                 offer.energy)

                final_price = self._update_offer_fee_and_calculate_final_price(
                    energy, trade_rate, energy_portion, orig_offer_price
                ) if already_tracked is False else energy * trade_rate

                accepted_offer = Offer(accepted_offer_id,
                                       abs(final_price),
                                       energy,
                                       offer.seller,
                                       seller_origin=offer.seller_origin)

                residual_price = (1 - energy_portion) * offer.price
                residual_energy = offer.energy - energy
                original_residual_price = \
                    ((offer.energy - energy) / offer.energy) * orig_offer_price

                residual_offer = Offer(
                    str(uuid.uuid4()),
                    abs(residual_price),
                    residual_energy,
                    offer.seller,
                    original_offer_price=original_residual_price,
                    seller_origin=offer.seller_origin)
                self.offers[residual_offer.id] = residual_offer
                log.debug(f"[BALANCING_OFFER][CHANGED][{self.time_slot_str}] "
                          f"{original_offer} -> {residual_offer}")
                offer = accepted_offer

                self.bc_interface.change_offer(offer, original_offer,
                                               residual_offer)
                self._sorted_offers = sorted(self.offers.values(),
                                             key=lambda o: o.price / o.energy)
                self._notify_listeners(MarketEvent.BALANCING_OFFER_CHANGED,
                                       existing_offer=original_offer,
                                       new_offer=residual_offer)
            elif abs(energy) > abs(offer.energy):
                raise InvalidBalancingTradeException(
                    "Energy can't be greater than offered energy")
            else:
                # Requested energy is equal to offer's energy - just proceed normally
                offer.price = self._update_offer_fee_and_calculate_final_price(
                    energy, trade_rate, 1, orig_offer_price
                ) if already_tracked is False else energy * trade_rate

        except Exception:
            # Exception happened - restore offer
            self.offers[offer.id] = offer
            self._sorted_offers = sorted(self.offers.values(),
                                         key=lambda o: o.price / o.energy)
            raise

        trade_id, residual_offer = \
            self.bc_interface.handle_blockchain_trade_event(
                offer, buyer, original_offer, residual_offer
            )
        trade = BalancingTrade(trade_id,
                               time,
                               offer,
                               offer.seller,
                               buyer,
                               residual_offer,
                               seller_origin=offer.seller_origin,
                               buyer_origin=buyer_origin)
        self.bc_interface.track_trade_event(trade)

        if already_tracked is False:
            self._update_stats_after_trade(trade, offer, buyer)
            log.info(f"[BALANCING_TRADE] [{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.BALANCING_TRADE, trade=trade)
        return trade
Beispiel #5
0
 def accept_offer(self,
                  offer_or_id: Union[str, BalancingOffer],
                  buyer: str,
                  *,
                  energy: int = None,
                  time: DateTime = None,
                  price_drop: bool = False) -> BalancingTrade:
     if self.readonly:
         raise MarketReadOnlyException()
     if isinstance(offer_or_id, Offer):
         offer_or_id = offer_or_id.id
     residual_offer = None
     offer = self.offers.pop(offer_or_id, None)
     self._sorted_offers = sorted(self.offers.values(),
                                  key=lambda o: o.price / o.energy)
     if offer is None:
         raise OfferNotFoundException()
     if (offer.energy > 0 and energy < 0) or (offer.energy < 0
                                              and energy > 0):
         raise InvalidBalancingTradeException("BalancingOffer and energy "
                                              "are not compatible")
     try:
         if time is None:
             time = self._now
         if energy is not None:
             # Partial trade
             if energy == 0:
                 raise InvalidBalancingTradeException(
                     "Energy can not be zero.")
             elif abs(energy) < abs(offer.energy):
                 original_offer = offer
                 accepted_offer = Offer(
                     offer.id, abs((offer.price / offer.energy) * energy),
                     energy, offer.seller, offer.market)
                 residual_energy = (offer.energy - energy)
                 residual_offer = Offer(
                     str(uuid.uuid4()),
                     abs((offer.price / offer.energy) * residual_energy),
                     residual_energy, offer.seller, offer.market)
                 self.offers[residual_offer.id] = residual_offer
                 log.info(
                     f"[BALANCING_OFFER][CHANGED][{self.time_slot_str}] "
                     f"{original_offer} -> {residual_offer}")
                 offer = accepted_offer
                 self._sorted_offers = sorted(
                     self.offers.values(), key=lambda o: o.price / o.energy)
                 self._notify_listeners(MarketEvent.BALANCING_OFFER_CHANGED,
                                        existing_offer=original_offer,
                                        new_offer=residual_offer)
             elif abs(energy) > abs(offer.energy):
                 raise InvalidBalancingTradeException(
                     f"Energy {energy} can't be greater than offered energy {offer}"
                 )
             else:
                 # Requested partial is equal to offered energy - just proceed normally
                 pass
     except Exception:
         # Exception happened - restore offer
         self.offers[offer.id] = offer
         self._sorted_offers = sorted(self.offers.values(),
                                      key=lambda o: o.price / o.energy)
         raise
     trade = BalancingTrade(id=str(uuid.uuid4()),
                            time=time,
                            offer=offer,
                            seller=offer.seller,
                            buyer=buyer,
                            residual=residual_offer,
                            price_drop=price_drop)
     self.trades.append(trade)
     self._update_accumulated_trade_price_energy(trade)
     log.warning(f"[BALANCING_TRADE][{self.time_slot_str}] {trade}")
     self.traded_energy[offer.seller] += offer.energy
     self.traded_energy[buyer] -= offer.energy
     self.ious[buyer][offer.seller] += offer.price
     self._update_min_max_avg_trade_prices(offer.price / offer.energy)
     # Recalculate offer min/max price since offer was removed
     self._update_min_max_avg_offer_prices()
     offer._traded(trade, self)
     self._notify_listeners(MarketEvent.BALANCING_TRADE, trade=trade)
     return trade