def test_match_recommendations_one_offer_multiple_bids(self, market):
        """Test match_recommendations() method of TwoSidedMarket using 1 offer N bids."""
        bid1 = Bid("bid_id1", pendulum.now(), price=1, energy=1, buyer="Buyer")
        bid2 = Bid("bid_id2", pendulum.now(), price=1, energy=1, buyer="Buyer")
        offer1 = Offer("offer_id1",
                       pendulum.now(),
                       price=2,
                       energy=2,
                       seller="Seller")

        market.bids = {"bid_id1": bid1, "bid_id2": bid2}
        market.offers = {"offer_id1": offer1}

        recommendations = [
            BidOfferMatch(
                bids=[bid.serializable_dict() for bid in market.bids.values()],
                offers=[
                    offer.serializable_dict()
                    for offer in market.offers.values()
                ],
                trade_rate=1,
                selected_energy=1,
                market_id=market.id).serializable_dict()
        ]
        market.match_recommendations(recommendations)
        assert len(market.trades) == 2
    def test_match_recommendations_fake_offer_bid(self, market,
                                                  two_sided_market_matching):
        """Test the case when an offer or bid which don't belong to market is sent."""
        bid = Bid("bid_id1", pendulum.now(), price=2, energy=1, buyer="B")
        offer = Offer("id", pendulum.now(), price=2, energy=1, seller="other")

        market.bids = {"bid_id1": bid}

        recommendations = [
            BidOfferMatch(bids=[bid.serializable_dict()],
                          offers=[offer.serializable_dict()],
                          trade_rate=2,
                          selected_energy=1,
                          market_id=market.id).serializable_dict()
        ]
        # The sent offer isn't in market offers, should be skipped
        market.match_recommendations(recommendations)
        assert len(market.trades) == 0
        assert not market.validate_bid_offer_match.called
        assert not market.accept_bid_offer_pair.called
        assert not market._replace_offers_bids_with_residual_in_recommendations_list.called

        market.offers = {offer.id: offer}
        market.bids = {}
        # The sent bid isn't in market offers, should be skipped
        market.match_recommendations(recommendations)
        assert len(market.trades) == 0
        assert not market.validate_bid_offer_match.called
        assert not market.accept_bid_offer_pair.called
        assert not market._replace_offers_bids_with_residual_in_recommendations_list.called
 def test_matching_list_gets_updated_with_residual_offers(self):
     matches = [
         BidOfferMatch(offers=[
             Offer("offer_id", pendulum.now(), 1, 1,
                   "S").serializable_dict()
         ],
                       selected_energy=1,
                       bids=[
                           Bid("bid_id", pendulum.now(), 1, 1,
                               "B").serializable_dict()
                       ],
                       trade_rate=1,
                       market_id="").serializable_dict(),
         BidOfferMatch(offers=[
             Offer("offer_id2", pendulum.now(), 2, 2,
                   "S").serializable_dict()
         ],
                       selected_energy=2,
                       bids=[
                           Bid("bid_id2", pendulum.now(), 2, 2,
                               "B").serializable_dict()
                       ],
                       trade_rate=1,
                       market_id="").serializable_dict()
     ]
     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"),
                       "S",
                       "B",
                       residual=Bid("residual_bid_2", pendulum.now(), 1, 1,
                                    "S"))
     matches = TwoSidedMarket._replace_offers_bids_with_residual_in_recommendations_list(
         matches, offer_trade, bid_trade)
     assert len(matches) == 2
     assert matches[0]["offers"][0]["id"] == "residual_offer"
     assert matches[1]["bids"][0]["id"] == "residual_bid_2"
Example #4
0
def perform_pay_as_bid_match(market_offers_bids_list_mapping):
    """Performs pay as bid matching algorithm.

    There are 2 simplistic approaches to the problem
        1. Match the cheapest offer with the most expensive bid. This will favor the sellers
        2. Match the cheapest offer with the cheapest bid. This will favor the buyers,
           since the most affordable offers will be allocated for the most aggressive buyers.
    Args:
        market_offers_bids_list_mapping: dict {market_uuid: {"offers": [...], "bids": [...]}, }

    Returns: List[BidOfferMatch.serializable_dict()]
    """
    bid_offer_pairs = []
    for market_id, data in market_offers_bids_list_mapping.items():
        bids = data.get("bids")
        offers = data.get("offers")
        # Sorted bids in descending order
        sorted_bids = sort_list_of_dicts_by_attribute(bids, "energy_rate",
                                                      True)
        # Sorted offers in descending order
        sorted_offers = sort_list_of_dicts_by_attribute(
            offers, "energy_rate", True)
        already_selected_bids = set()
        for offer in sorted_offers:
            for bid in sorted_bids:
                if bid.get("id") in already_selected_bids or\
                        offer.get("seller") == bid.get("buyer"):
                    continue
                if (offer.get("energy_rate") -
                        bid.get("energy_rate")) <= FLOATING_POINT_TOLERANCE:
                    already_selected_bids.add(bid.get("id"))
                    selected_energy = min(bid.get("energy"),
                                          offer.get("energy"))
                    bid_offer_pairs.append(
                        BidOfferMatch(market_id=market_id,
                                      bid=bid,
                                      offer=offer,
                                      selected_energy=selected_energy,
                                      trade_rate=bid.get(
                                          "energy_rate")).serializable_dict())
                    break
    return bid_offer_pairs
    def test_match_recommendations(self, market):
        """Test match_recommendations() method of TwoSidedMarket."""
        bid = Bid("bid_id1", pendulum.now(), price=2, energy=1, buyer="Buyer")
        offer = Offer("offer_id1",
                      pendulum.now(),
                      price=2,
                      energy=1,
                      seller="Seller")

        market.bids = {"bid_id1": bid}
        market.offers = {"offer_id1": offer}

        recommendations = [
            BidOfferMatch(bids=[bid.serializable_dict()],
                          offers=[offer.serializable_dict()],
                          trade_rate=2,
                          selected_energy=1,
                          market_id=market.id).serializable_dict()
        ]
        market.match_recommendations(recommendations)
        assert len(market.trades) == 1