Creating a new mechanism

[1]:
import numpy as np
import pandas as pd
import pymarket as pm
import matplotlib.pyplot as plt
from pprint import pprint

One of the advantages of PyMarket is the ability to easily implement and test a new idea for a mechanism. Here we will show how to implement a new mechanism and use it.

The uniform price mechanism

We are going to implement a uniform price mechanism that charges every trading player the clearing price.

As a reference we are going to be implement the example Here

We can begin by adding the corresponding bids to a new market

[2]:
mar = pm.Market()

buyers_names = ['CleanRetail', 'El4You', 'EVcharge', 'QualiWatt', 'IntelliWatt']

mar.accept_bid(250, 200, 0, True) # CleanRetail  0
mar.accept_bid(300, 110, 1, True) # El4You       1
mar.accept_bid(120, 100, 2, True) # EVcharge     2
mar.accept_bid( 80,  90, 3, True) # QualiWatt    3
mar.accept_bid( 40,  85, 4, True) # IntelliWatt  4
mar.accept_bid( 70,  75, 1, True) # El4You       5
mar.accept_bid( 60,  65, 0, True) # CleanRetail  6
mar.accept_bid( 45,  40, 4, True) # IntelliWatt  7
mar.accept_bid( 30,  38, 3, True) # QualiWatt    8
mar.accept_bid( 35,  31, 4, True) # IntelliWatt  9
mar.accept_bid( 25,  24, 0, True) # CleanRetail  10
mar.accept_bid( 10,  21, 1, True) # El4You       11

sellers_names = ['RT', 'WeTrustInWind', 'BlueHydro', 'KøbenhavnCHP', 'DirtyPower', 'SafePeak']

mar.accept_bid(120,   0, 5, False) # RT             12
mar.accept_bid(50,    0, 6, False) # WeTrustInWind  13
mar.accept_bid(200,  15, 7, False) # BlueHydro      14
mar.accept_bid(400,  30, 5, False) # RT             15
mar.accept_bid(60, 32.5, 8, False) # KøbenhavnCHP   16
mar.accept_bid(50,   34, 8, False) # KøbenhavnCHP   17
mar.accept_bid(60,   36, 8, False) # KøbenhavnCHP   18
mar.accept_bid(100,37.5, 9, False) # DirtyPower     19
mar.accept_bid(70,   39, 9, False) # DirtyPower     20
mar.accept_bid(50,   40, 9, False) # DirtyPower     21
mar.accept_bid(70,   60, 5, False) # RT             22
mar.accept_bid(45,   70, 5, False) # RT             23
mar.accept_bid(50,  100, 10, False) # SafePeak      24
mar.accept_bid(60,  150, 10, False) # SafePeak      25
mar.accept_bid(50,  200, 10, False) # SafePeak      26
[2]:
26
[3]:
12, 15, 22, 23
[3]:
(12, 15, 22, 23)
[4]:
mar.plot()
_images/new_mechanism_8_0.png

Implementing the mechanism

All market mechanisms take as arguements a bids dataframe (as well as possibly extra parameters) and returns a TransactionManager and an extras dictionary.

[13]:
def uniform_price_mechanism(bids: pd.DataFrame) -> (pm.TransactionManager, dict):

    trans = pm.TransactionManager()

    buy, _ = pm.bids.demand_curve_from_bids(bids) # Creates demand curve from bids
    sell, _ = pm.bids.supply_curve_from_bids(bids) # Creates supply curve from bids

    # q_ is the quantity at which supply and demand meet
    # price is the price at which that happens
    # b_ is the index of the buyer in that position
    # s_ is the index of the seller in that position
    q_, b_, s_, price = pm.bids.intersect_stepwise(buy, sell)

    buying_bids  = bids.loc[bids['buying']].sort_values('price', ascending=False)
    selling_bids = bids.loc[~bids['buying']].sort_values('price', ascending=True)

    ## Filter only the trading bids.
    buying_bids = buying_bids.iloc[: b_ + 1, :]
    selling_bids = selling_bids.iloc[: s_ + 1, :]

    # Find the long side of the market
    buying_quantity = buying_bids.quantity.sum()
    selling_quantity = selling_bids.quantity.sum()


    if buying_quantity > selling_quantity:
        long_side = buying_bids
        short_side = selling_bids
    else:
        long_side = selling_bids
        short_side = buying_bids

    traded_quantity = short_side.quantity.sum()

    ## All the short side will trade at `price`
    ## The -1 is there because there is no clear 1 to 1 trade.
    for i, x in short_side.iterrows():
        t = (i, x.quantity, price, -1, False)
        trans.add_transaction(*t)

    ## The long side has to trade only up to the short side
    quantity_added = 0
    for i, x in long_side.iterrows():

        if x.quantity + quantity_added <= traded_quantity:
            x_quantity = x.quantity
        else:
            x_quantity = traded_quantity - quantity_added
        t = (i, x_quantity, price, -1, False)
        trans.add_transaction(*t)
        quantity_added += x.quantity

    extra = {
        'clearing quantity': q_,
        'clearing price': price
    }



    return trans, extra

Wrapping the algorithm as a mechanism

[14]:
# Observe that we add as the second argument of init the algorithm just coded
class UniformPrice(pm.Mechanism):
    """
    Interface for our new uniform price mechanism.

    Parameters
    -----------
    bids
        Collection of bids to run the mechanism
        with.
    """

    def __init__(self, bids, *args, **kwargs):
        """TODO: to be defined1. """
        pm.Mechanism.__init__(self, uniform_price_mechanism, bids, *args, **kwargs)

Adding the new mechanism to the list of available mechanism of the market

[15]:
pm.market.MECHANISM['uniform'] = UniformPrice

Running the new mechanism and comparing it with Huang’s and P2P

[24]:
stats = {}
for mec in ['uniform', 'huang', 'p2p']:
    t, e = mar.run(mec)
    stat = mar.statistics()
    stats[mec] = stat

Profits for the players in the different mechanism

[33]:
profits = pd.DataFrame([v['profits']['player_bid'] for k, v in stats.items()]).T
profits.columns = stats.keys()
profits
[33]:
uniform huang p2p
0 42275.0 41529.375 22890.0
1 24375.0 23849.375 12980.0
2 7500.0 7246.250 3150.0
3 4215.0 3997.500 2630.0
4 2012.5 1816.875 1162.5
5 7500.0 7500.000 14647.5
6 1875.0 1875.000 810.0
7 4500.0 4500.000 18500.0
8 565.0 565.000 3910.0
9 0.0 0.000 4570.0
10 0.0 0.000 375.0

Percentage traded by mechanism

[36]:
traded = pd.DataFrame([v['percentage_traded'].round(3) for k, v in stats.items()]).T
traded.columns = stats.keys()
traded
[36]:
uniform huang p2p
0 0.934 0.883 0.995

Percentage of the maximum social welfare achieved by mechanism

[38]:
welfare = pd.DataFrame([v['percentage_welfare'].round(3) for k, v in stats.items()]).T
welfare.columns = stats.keys()
welfare
[38]:
uniform huang p2p
0 1.0 0.98 0.903
[ ]: