def experiment(results_csv_file: str, auction_function: Callable, auction_name: str, recipe: tuple, stocks_prices: list = None, stock_names: list = None): """ Run an experiment similar to McAfee (1992) experiment on the given auction. :param results_csv_file: the experiment result file. :param auction_function: the function for executing the auction under consideration. :param auction_name: title of the experiment, for printouts. :param recipe: can be any vector of ones, e.g. (1,1,1), for our trade-reduction mechanism, or any vector of positive integers for our ascending-auction mechanism. :param stocks_prices: list of prices for each stock and each agent. :param stock_names: list of stocks names which prices are belongs, for naming only. """ if stocks_prices is None: (stocks_prices, stock_names) = getStocksPrices(recipe) results_table = TeeTable(TABLE_COLUMNS, results_csv_file) recipe_str = ":".join(map(str, recipe)) for i in range(len(stocks_prices)): market = Market([ AgentCategory("agent", category) for category in stocks_prices[i] ]) num_of_possible_ps = min([ len(stocks_prices[i][j]) / recipe[j] for j in range(len(stocks_prices[i])) ]) (optimal_trade, _) = market.optimal_trade(recipe) auction_trade = auction_function(market, recipe) optimal_count = optimal_trade.num_of_deals() auction_count = auction_trade.num_of_deals() if (auction_trade.num_of_deals() > optimal_trade.num_of_deals()): print( "Warning!!! the number of deals in action is greater than optimal!" ) print("Optimal num of deals: ", optimal_trade.num_of_deals()) print("Auction num of deals: ", auction_trade.num_of_deals()) optimal_gft = optimal_trade.gain_from_trade() auction_gft = auction_trade.gain_from_trade(including_auctioneer=True) auction_market_gft = auction_trade.gain_from_trade( including_auctioneer=False) results_table.add( OrderedDict(( ("stock_name", stock_names[i]), ("auction_name", auction_name), ("recipe", recipe_str), ("num_possible_trades", round(num_of_possible_ps)), ("optimal_count", round(optimal_count, 2)), ("auction_count", round(auction_count, 2)), ("count_ratio", 0 if optimal_count == 0 else int( (auction_count / optimal_count) * 100000) / 1000), ("optimal_gft", round(optimal_gft, 2)), ("auction_gft", round(auction_gft, 2)), ("auction_gft_ratio", 0 if optimal_gft == 0 else round( auction_gft / optimal_gft * 100, 3)), ("auction_market_gft", round(auction_market_gft, 2)), ("market_gft_ratio", 0 if optimal_gft == 0 else round( auction_market_gft / optimal_gft * 100, 3)), ))) results_table.done()
def start_testing(args): ## instantiate exchanges, update candles, and run strategy profits = defaultdict(dict) for x in args["exchanges"]: new_exchange = ccxtExchange(x) portfolio = [Market(m, new_exchange) for m in args["markets"][x]] new_exchange.markets = { mkt_obj.symbol: mkt_obj for mkt_obj in portfolio } candles_magnitude = new_exchange.get_ccxt_candles_magnitude_format( args["candles_m_size"], args["candles_m_unit"]) ## run real strategy strat_profits = run_model(args["strategy"], x, portfolio, candles_magnitude, args) ## run benchmark strategy (holding) hold_profits = run_model("hold", x, portfolio, candles_magnitude, args) ## run study stats (it does not return anything) run_model("study_stats", x, portfolio, candles_magnitude, args) exchange_profits = {mkt: {args["strategy"]: strat_prf, "hold": hold_profits[mkt]} \ for mkt, strat_prf in \ zip(strat_profits.keys(), strat_profits.values())} profits[x] = exchange_profits return profits
def check_110_101(buyers: List[float], sellersA: List[float], sellersB: List[float], expected_num_of_deals: int, expected_prices: List[float]): market = Market([ AgentCategory("buyer", buyers), AgentCategory("sellerA", sellersA), AgentCategory("sellerB", sellersB), ]) # ps_recipes = [[1, 1, 0], [1, 0, 1]] ps_recipe_struct = [0, [1, None, 2, None]] self._check_market(market, ps_recipe_struct, expected_num_of_deals, expected_prices)
def check_1_1(buyers: List[float], sellers: List[float], expected_num_of_deals: int, expected_prices: List[float]): market = Market([ AgentCategory("buyer", buyers), AgentCategory("seller", sellers), ]) ps_recipe = [1, 1] self._check_market(market, ps_recipe, expected_num_of_deals, expected_prices)
def check_1100_1011(buyers: List[float], sellers: List[float], producers: List[float], movers: List[float], expected_num_of_deals: int, expected_prices: List[float]): market = Market([ AgentCategory("buyer", buyers), AgentCategory("seller", sellers), AgentCategory("producer", producers), AgentCategory("mover", movers), ]) # ps_recipes = [[1, 1, 0, 0], [1, 0, 1, 1]] ps_recipe_struct = [0, [1, None, 2, [3, None]]] self._check_market(market, ps_recipe_struct, expected_num_of_deals, expected_prices)
def check_11100_10011(buyers: List[float], sellers1: List[float], sellers2: List[float], producers1: List[float], producers2: List[float], expected_num_of_deals: int, expected_prices: List[float]): market = Market([ AgentCategory("buyer", buyers), AgentCategory("seller1", sellers1), AgentCategory("seller2", sellers2), AgentCategory("producer1", producers1), AgentCategory("producer2", producers2), ]) ps_recipe_struct = [0, [1, [2, None], 3, [4, None]]] self._check_market(market, ps_recipe_struct, expected_num_of_deals, expected_prices)
def test_trader_pipeline(): hitbtc2 = ExchangeTrader("hitbtc2") eth_btc_market = Market("ETH/BTC", hitbtc2) try: hitbtc2.buy(eth_btc_market, 0.001, 0.00059498) time.sleep(0.1) hitbtc2.sell(eth_btc_market, 0.001, 0.1) time.sleep(0.1) except: raise time.sleep(1) for order in hitbtc2.orders_alive: print(order.status()) time.sleep(0.1) print(order.status()) time.sleep(0.1) print(order.status()) time.sleep(0.1) assert (order.status() == 'open') time.sleep(1) num_orders = len(hitbtc2.orders_alive) print hitbtc2.orders_history for order in hitbtc2.orders_history: order.cancel() time.sleep(0.1) num_orders -= 1 print hitbtc2.orders_alive assert (len(hitbtc2.orders_alive) == num_orders) time.sleep(1) for order in hitbtc2.orders_history: assert (order.status() == 'canceled')
def experiment(results_csv_file: str, auction_functions: list, auction_names: str, recipe: tuple, iterations: int, nums_of_agents: list = None, stocks_prices: list = None, stock_names: list = None): """ Run an experiment similar to McAfee (1992) experiment on the given auction. :param results_csv_file: the experiment result file. :param auction_functions: list of functions for executing the auction under consideration. :param auction_names: titles of the experiment, for printouts. :param recipe: can be any vector of ones, e.g. (1,1,1), for our trade-reduction mechanism, or any vector of positive integers for our ascending-auction mechanism. :param nums_of_agents: list of n(s) for number of possible trades to make the calculations. :param stocks_prices: list of prices for each stock and each agent. :param stock_names: list of stocks names which prices are belongs, for naming only. """ TABLE_COLUMNS = [ "stockname", "recipe", "numpossibletrades", "optimalcount", "optimalcountwithgftzero", "optimalgft" ] AUCTION_COLUMNS = ["auctioncount", "countratio", "gft", "gftratio"] print(recipe) if stocks_prices is None: (stocks_prices, stock_names) = getStocksPricesShuffled() column_names = TABLE_COLUMNS column_names += [ auction_name + column for auction_name in auction_names for column in AUCTION_COLUMNS ] results_table = TeeTable(column_names, results_csv_file) recipe_str = ":".join(map(str, recipe)) recipe_sum = sum(recipe) if nums_of_agents is None: nums_of_agents = [10000000] average_total_results = {} for num_of_agents_per_category in nums_of_agents: average_total_results[str(num_of_agents_per_category)] = [] for i in range(len(stocks_prices)): total_results = {} for num_of_agents_per_category in nums_of_agents: total_results[str(num_of_agents_per_category)] = [] stock_prices = stocks_prices[i] last_iteration = False for num_of_agents_per_category in nums_of_agents: for iteration in range(iterations): num_of_possible_ps = min(num_of_agents_per_category, int(len(stock_prices) / recipe_sum)) if last_iteration and num_of_possible_ps < num_of_agents_per_category: break if num_of_possible_ps < num_of_agents_per_category: if last_iteration: break last_iteration = True categories = [] buyer_agent_count = recipe[0] index = 0 for category in recipe: next_index = index + num_of_possible_ps * category price_value_multiple = -1 * buyer_agent_count if index > 0 else recipe_sum - buyer_agent_count categories.append( AgentCategory("agent", [ int(price * price_value_multiple) for price in stock_prices[index:next_index] ])) index = next_index market = Market(categories) (optimal_trade, _) = market.optimal_trade(ps_recipe=list(recipe), max_iterations=10000000, include_zero_gft_ps=False) optimal_count = optimal_trade.num_of_deals() optimal_gft = optimal_trade.gain_from_trade() (optimal_trade_with_gft_zero, _) = market.optimal_trade(ps_recipe=list(recipe), max_iterations=10000000) optimal_count_with_gft_zero = optimal_trade_with_gft_zero.num_of_deals( ) results = [ ("stockname", stock_names[i]), ("recipe", recipe_str), ("numpossibletrades", round(num_of_possible_ps)), ("optimalcount", optimal_count), ("optimalcountwithgftzero", optimal_count_with_gft_zero), ("optimalgft", optimal_gft) ] for auction_index in range(len(auction_functions)): if 'mcafee' in auction_names[auction_index]: results.append((auction_name + "auctioncount", 0)) results.append((auction_name + "countratio", 0)) results.append((auction_name + "gft", 0)) results.append((auction_name + "gftratio", 0)) auction_trade = auction_functions[auction_index](market, recipe) auction_count = auction_trade.num_of_deals() # for j in range(len(stocks_prices[i])): # print(sorted(stocks_prices[i][j][:num_of_possible_ps*recipe[j]])) if (auction_trade.num_of_deals() > optimal_trade_with_gft_zero.num_of_deals()): # print(sorted(stocks_prices[i][0][:num_of_possible_ps*recipe[0]])) # print(sorted(stocks_prices[i][1][:num_of_possible_ps*recipe[1]])) print( "Warning!!! the number of deals in action is greater than optimal!" ) print("Optimal num of deals: ", optimal_trade.num_of_deals()) print("Auction num of deals: ", auction_trade.num_of_deals()) print("Auction name: ", auction_names[auction_index]) gft = auction_trade.gain_from_trade( including_auctioneer=False) auction_name = auction_names[auction_index] results.append((auction_name + "auctioncount", auction_trade.num_of_deals())) results.append( (auction_name + "countratio", 0 if optimal_count_with_gft_zero == 0 else (auction_count / optimal_count_with_gft_zero) * 100)) results.append((auction_name + "gft", gft)) results.append( (auction_name + "gftratio", 0 if optimal_gft == 0 else gft / optimal_gft * 100)) #results_table.add(OrderedDict(results)) if len(total_results[str(num_of_agents_per_category)]) == 0: total_results[str( num_of_agents_per_category)] = results[0:len(results)] else: sum_result = total_results[str(num_of_agents_per_category)] for index in range(len(results)): if index > 2: sum_result[index] = (results[index][0], sum_result[index][1] + results[index][1]) for num_of_agents_per_category in nums_of_agents: results = total_results[str(num_of_agents_per_category)] if len(results) == 0: continue for index in range(len(results)): if index > 2: if 'ratio' in results[index][0]: results[index] = (results[index][0], results[index][1] / iterations) else: results[index] = (results[index][0], results[index][1] / iterations) #elif index == 0: # results[index] = (results[index][0], 'Average') results_table.add(OrderedDict(results)) if len(average_total_results[str( num_of_agents_per_category)]) == 0: average_total_results[str( num_of_agents_per_category)] = results[0:len(results)] else: sum_result = average_total_results[str( num_of_agents_per_category)] for index in range(len(results)): if index > 2: sum_result[index] = (sum_result[index][0], sum_result[index][1] + results[index][1]) for num_of_agents_per_category in nums_of_agents: results = average_total_results[str(num_of_agents_per_category)] if len(results) == 0: continue for index in range(len(results)): if index > 2: if 'ratio' in results[index][0]: results[index] = ( results[index][0], int(results[index][1] / len(stocks_prices) * 1000) / 1000) else: results[index] = (results[index][0], round( results[index][1] / len(stocks_prices), 1)) elif index == 0: results[index] = (results[index][0], 'Average') results_table.add(OrderedDict(results)) results_table.done()
#!python3 """ Demonstration of a multiple-clock strongly-budget-balanced ascending auction for a multi-lateral market with one buyer per two sellers (recipe: 1,2) Author: Erel Segal-Halevi Since: 2019-08 """ from markets import Market from agents import AgentCategory import ascending_auction_protocol, prices from ascending_auction_protocol import budget_balanced_ascending_auction import logging ascending_auction_protocol.logger.setLevel(logging.INFO) prices.logger.setLevel(logging.INFO) print("\n\n###### EXAMPLE OF PS with GFT=0") market = Market([ AgentCategory("buyer", [1, 1, 1, 1, 1]), AgentCategory("seller", [-1, -1, -1, -1, -1]), ]) print(budget_balanced_ascending_auction(market, [1, 1]))
# market = Market([ # AgentCategory("C0", [400, 300, 200, 100]), # AgentCategory("C1", [-1, -11]), # AgentCategory("C2", [-2, -22]), # AgentCategory("C3", [-3, -33]), # AgentCategory("C4", [-4, -44]), # AgentCategory("C5", [-5, -55]), # AgentCategory("C6", [-6, -66]), # ]) market = Market([ AgentCategory("C0", [400, 300, 200, 100]), AgentCategory("C1", [-1, -11]), AgentCategory("C2", [-2]), AgentCategory("C3", [-3]), AgentCategory("C4", [-4, -44]), AgentCategory("C5", [-5]), AgentCategory("C6", [-6]), ]) recipes_4paths = [0, [1, [2, None, 3, None], 4, [5, None, 6, None]]] # The recipes are: # 1,1,1,0,0,0,0 [C0, C1, C2] # 1,1,0,1,0,0,0 [C0, C1, C3] # 1,0,0,0,1,1,0 [C0, C4, C5] # 1,0,0,0,1,0,1 [C0, C4, C6] print(budget_balanced_ascending_auction(market, recipes_4paths))
def experiment(results_csv_file: str, recipe: list, value_ranges: list, nums_of_agents: list, num_of_iterations: int, agent_counts: list, agent_values: list, recipe_tree_agent_counts: list, num_recipes: int): """ Run an experiment similar to McAfee (1992) experiment on the given auction. :param results_csv_file: the experiment result file. :param auction_functions: list of functions for executing the auction under consideration. :param auction_names: titles of the experiment, for printouts. :param recipe: can be any vector of ones, e.g. (1,1,1), for our trade-reduction mechanism, or any vector of positive integers for our ascending-auction mechanism. :param nums_of_agents: list of n(s) for number of possible trades to make the calculations. :param stocks_prices: list of prices for each stock and each agent. :param stock_names: list of stocks names which prices are belongs, for naming only. """ TABLE_COLUMNS = [ "iterations", "recipe", "numofagents", "optimalcount", "optimalkmin", "optimalkmax", "gftformula", "auctioncount", "auctionkmin", "auctionkmax", "countratio", "optimalgft", "auctiongft", "gftratio" ] print('recipe:', recipe, 'size:', num_recipes) GFT_ROUND = 1 - 1 K_ROUND = 2 - 1 RATIO_ROUND = 3 - 1 results_table = TeeTable(TABLE_COLUMNS, results_csv_file) recipe_str = str(recipe).replace(',', '-') is_binary = len(set(recipe_tree_agent_counts)) == 1 for i in range(len(nums_of_agents)): now = datetime.now() sum_optimal_count = sum_auction_count = sum_optimal_kmin = sum_optimal_kmax = 0 # count the number of deals done in the optimal vs. the actual auction. sum_optimal_gft = sum_auction_total_gft = sum_auction_kmin = sum_auction_kmax = 0 for iteration in range(num_of_iterations): #if iteration % 10000 == 0: # print('iteration:', iteration) agents = [] for category in range(len(recipe_tree_agent_counts)): sign = 0 if category == 0 else 1 agents.append( AgentCategory.uniformly_random( "agent", int(nums_of_agents[i] * agent_counts[category]), value_ranges[sign][0] * agent_values[category], value_ranges[sign][1] * agent_values[category])) #agents.append(AgentCategory.uniformly_random("agent", nums_of_agents[i], value_ranges[sign][0], value_ranges[sign][1])) market = Market(agents) #print(market) #print(agents) recipe_tree = RecipeTree(market.categories, recipe, recipe_tree_agent_counts) optimal_trade, optimal_count, optimal_gft, kmin, kmax, categories_optimal_counters = recipe_tree.optimal_trade_with_counters( ) #print('counters:' + str(categories_counters)) #print('optimal trade:', optimal_trade, optimal_count, optimal_gft) auction_trade = budget_balanced_ascending_auction( market, recipe, recipe_tree_agent_counts) auction_count = auction_trade.num_of_deals() auction_kmin = auction_trade.min_num_of_deals() auction_kmax = auction_trade.max_num_of_deals() path_counters = auction_trade.path_counters gft = auction_trade.gain_from_trade() #print('Compare:', categories_optimal_counters, path_counters) for counter in categories_optimal_counters.keys(): if categories_optimal_counters[ counter] > path_counters[counter] + 1 and False: print(market) print('Compare:', categories_optimal_counters, path_counters) print('Warning counters', str(counter), 'are not in same size!', categories_optimal_counters[counter], '!=', path_counters[counter]) #for i in range(len(path_counters)): # print('Compare:', categories_optimal_counters, path_counters) #if optimal_count > 0 and gft < optimal_gft * (kmin - 1)/(kmin + 2): #the auction count is less more than 1 than the optimal count. # print('Warning GFT!!!', 'optimal_count:', optimal_count, 'auction_count:', auction_count, # 'num_of_possible_ps:', nums_of_agents[i], 'optimal_gft:', optimal_gft, 'gft:', gft, 'lower bound:', optimal_gft * (1 - 1/optimal_count)) # if nums_of_agents[i] < 20: # print(market.categories) sum_optimal_count += optimal_count sum_auction_count += auction_count sum_optimal_kmin += kmin sum_optimal_kmax += kmax sum_auction_kmin += auction_kmin sum_auction_kmax += auction_kmax sum_optimal_gft += optimal_gft sum_auction_total_gft += gft #if auction_count < optimal_count - 2: #the auction count is less more than 1 than the optimal count. #print('Warning!!!', 'optimal_count:', optimal_count, 'auction_count:', auction_count, 'num_of_possible_ps:', nums_of_agents[i]) #if nums_of_agents[i] < 10: # print(market.categories) # print("Num of times {} attains the maximum GFT: {} / {} = {:.2f}%".format(title, count_optimal_gft, num_of_iterations, count_optimal_gft * 100 / num_of_iterations)) # print("GFT of {}: {:.2f} / {:.2f} = {:.2f}%".format(title, sum_auction_gft, sum_optimal_gft, 0 if sum_optimal_gft==0 else sum_auction_gft * 100 / sum_optimal_gft)) kmin_mean = sum_optimal_kmin / num_of_iterations if is_binary: gft_formula = int_round( (kmin_mean - 1) / kmin_mean * 100 if kmin_mean - 1 > 0 else 0, RATIO_ROUND) else: gft_formula = int_round( (kmin_mean - num_recipes) / (kmin_mean + num_recipes) * 100 if kmin_mean - num_recipes > 0 else 0, RATIO_ROUND) results_table.add( OrderedDict([ ("iterations", num_of_iterations), ("recipe", recipe_str), ("numofagents", nums_of_agents[i]), ("optimalcount", int_round(sum_optimal_count / num_of_iterations, K_ROUND)), ("optimalkmin", int_round(kmin_mean, K_ROUND)), ("optimalkmax", int_round(sum_optimal_kmax / num_of_iterations, K_ROUND)), ("gftformula", gft_formula), ("auctioncount", int_round(sum_auction_count / num_of_iterations, K_ROUND)), ("auctionkmin", int_round(sum_auction_kmin / num_of_iterations, K_ROUND)), ("auctionkmax", int_round(sum_auction_kmax / num_of_iterations, K_ROUND)), ("countratio", int_round( 0 if sum_optimal_count == 0 else (sum_auction_count / sum_optimal_count) * 100, RATIO_ROUND)), ("optimalgft", int_round(sum_optimal_gft / num_of_iterations, GFT_ROUND)), ("auctiongft", int_round(sum_auction_total_gft / num_of_iterations, GFT_ROUND)), ("gftratio", '0.00' if sum_optimal_gft == 0 else int_round( sum_auction_total_gft / sum_optimal_gft * 100, RATIO_ROUND)), ])) print('took', (datetime.now() - now).seconds) results_table.done()
def budget_balanced_ascending_auction( market: Market, ps_recipes: List[List]) -> TradeWithMultipleRecipes: """ Calculate the trade and prices using generalized-ascending-auction. Allows multiple recipes, but they must all be binary, and must all start with 1. E.g.: [ [1,1,0,0], [1,0,1,1] ] I.e., there is 1 buyer category and n-1 seller categories. Each buyer may wish to buy a different combination of products. :param market: contains a list of k categories, each containing several agents. :param ps_recipes: a list of lists of integers, one integer per category. Each integer i represents the number of agents of category i that should be in each procurement-set. :return: Trade object, representing the trade and prices. >>> # ONE BUYER, ONE SELLER >>> recipe_11 = [[1,1]] >>> >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0], seller: [-4.0]] No trade >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0, 8.0], seller: [-4.0]] No trade >>> logger.setLevel(logging.WARNING) >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0], seller: [-3.0, -4.0]] seller: [-3.0]: all 1 agents trade and pay -4.0 buyer: [9.0]: all 1 agents trade and pay 4.0 >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0, 8.0], seller: [-3.0, -4.0]] seller: [-3.0, -4.0]: random 1 out of 2 agents trade and pay -8.0 buyer: [9.0]: all 1 agents trade and pay 8.0 """ num_recipes = len(ps_recipes) if num_recipes < 1: raise ValueError("Empty list of recipes") num_categories = market.num_categories for i, ps_recipe in enumerate(ps_recipes): if len(ps_recipe) != num_categories: raise ValueError( "There are {} categories but {} elements in PS recipe #{}". format(num_categories, len(ps_recipe), i)) if any((r != 1 and r != 0) for r in ps_recipe): raise ValueError( "Currently, the multi-recipe protocol supports only recipes of zeros and ones; {} was given" .format(ps_recipe)) logger.info("\n#### Multi-Recipe Budget-Balanced Ascending Auction\n") logger.info(market) logger.info("Procurement-set recipes: {}".format(ps_recipes)) map_category_index_to_recipe_indices, common_category_indices, map_recipe_index_to_unique_category_indices = \ _analyze_recipes(num_categories, ps_recipes) # Calculating the optimal trade with multiple recipes is left for future work. # optimal_trade = market.optimal_trade(ps_recipe)[0] # logger.info("For comparison, the optimal trade is: %s\n", optimal_trade) remaining_market = market.clone() prices = SimultaneousAscendingPriceVectors(ps_recipes, -MAX_VALUE) # Functions for calculating the number of potential PS that can be supported by a category: # Currently we assume a recipe of ones, so the number of potential PS is simply the category size: fractional_potential_ps = lambda category_index: remaining_market.categories[ category_index].size() integral_potential_ps = lambda category_index: remaining_market.categories[ category_index].size() while True: # find a category with a largest number of potential PS, and increase its price largest_common_category_index = max(common_category_indices, key=fractional_potential_ps) \ if len(common_category_indices)>0 \ else None largest_common_category_size = fractional_potential_ps(largest_common_category_index) \ if len(common_category_indices) > 0 \ else 0 logger.info("Largest common category is %d and its size is %d", largest_common_category_index, largest_common_category_size) map_recipe_index_to_largest_unique_category_index = [ max(unique_category_indices, key=fractional_potential_ps) for unique_category_indices in map_recipe_index_to_unique_category_indices ] if len(map_recipe_index_to_largest_unique_category_index) == 0: raise ValueError("No unique categories") unique_categories_size = sum([ fractional_potential_ps(largest_unique_category_index) for largest_unique_category_index in map_recipe_index_to_largest_unique_category_index ]) logger.info( "Largest unique categories are %s and their total size is %d", map_recipe_index_to_largest_unique_category_index, unique_categories_size) if unique_categories_size == 0: logger.info("\nThe unique categories %s became empty - no trade!", map_recipe_index_to_largest_unique_category_index) logger.info(" Final price-per-unit vector: %s", prices) logger.info(remaining_market) return TradeWithMultipleRecipes( remaining_market.categories, ps_recipes, prices.map_category_index_to_price()) if largest_common_category_size >= unique_categories_size: logger.info( "Raising price of the largest common category (%d) in all recipes", largest_common_category_index) main_category_index = largest_common_category_index main_category = remaining_market.categories[main_category_index] logger.info("%s before: %d agents remain", main_category.name, main_category.size()) increases = [ (main_category_index, main_category.lowest_agent_value(), main_category.name) ] * num_recipes else: # largest_common_category_size < unique_categories_size logger.info( "Raising price of the largest unique categories in each recipe: %s", map_recipe_index_to_largest_unique_category_index) increases = [] for recipe_index, main_category_index in enumerate( map_recipe_index_to_largest_unique_category_index): main_category = remaining_market.categories[ main_category_index] logger.info("%s before: %d agents remain", main_category.name, main_category.size()) if main_category.size() == 0: logger.info( "\nThe %s category became empty - no trade in recipe %d", main_category.name, recipe_index) del ps_recipes[recipe_index] if len(ps_recipes) > 0: return budget_balanced_ascending_auction( market, ps_recipes) else: logger.info("\nNo recipes left - no trade!") logger.info(" Final price-per-unit vector: %s", prices) logger.info(remaining_market) return TradeWithMultipleRecipes( remaining_market.categories, ps_recipes, map_category_index_to_price) increases.append( (main_category_index, main_category.lowest_agent_value(), main_category.name)) if len(increases) == 0: raise ValueError("No increases!") logger.info("Planned increases: %s", increases) prices.increase_prices(increases) map_category_index_to_price = prices.map_category_index_to_price() if prices.status == PriceStatus.STOPPED_AT_ZERO_SUM: logger.info("\nPrice crossed zero.") logger.info(" Final price-per-unit vector: %s", map_category_index_to_price) logger.info(remaining_market) return TradeWithMultipleRecipes(remaining_market.categories, ps_recipes, map_category_index_to_price) for category_index in range(num_categories): category = remaining_market.categories[category_index] if map_category_index_to_price[category_index] is not None \ and category.size()>0 \ and category.lowest_agent_value() <= map_category_index_to_price[category_index]: category.remove_lowest_agent() logger.info( "{} after: {} agents remain, {} PS supported".format( category.name, category.size(), integral_potential_ps(category_index)))
for a bilateral market with buyers and sellers. Since: 2019-08 Author: Erel Segal-Halevi """ from markets import Market from agents import AgentCategory import mcafee_protocol from mcafee_protocol import mcafee_trade_reduction import logging mcafee_protocol.logger.setLevel(logging.INFO) recipe = [1, 1] print("\n\n### Example without trade reduction") market = Market([ AgentCategory("buyer", [17, 14, 13, 9, 6]), AgentCategory("seller", [-1, -4, -5, -8, -11]), ]) print(mcafee_trade_reduction(market, recipe)) print("\n\n### Example with trade reduction") market = Market([ AgentCategory("buyer", [17, 14, 13, 9, 6]), AgentCategory("seller", [-1, -4, -5, -8, -13]), ]) print(mcafee_trade_reduction(market, recipe))
def experiment(results_csv_file: str, recipe: list, agent_counts:list, agent_values:list, nums_of_agents:list = None, num_of_iterations:int = 1, stocks_prices=None, stock_names=None): """ Run an experiment similar to McAfee (1992) experiment on the given auction. :param results_csv_file: the experiment result file. :param auction_functions: list of functions for executing the auction under consideration. :param auction_names: titles of the experiment, for printouts. :param recipe: can be any vector of ones, e.g. (1,1,1), for our trade-reduction mechanism, or any vector of positive integers for our ascending-auction mechanism. :param nums_of_agents: list of n(s) for number of possible trades to make the calculations. :param stocks_prices: list of prices for each stock and each agent. :param stock_names: list of stocks names which prices are belongs, for naming only. """ TABLE_COLUMNS = ["stockname", "recipe", "numpossibletrades", "optimalkmin", "optimalkmax","gftformula", "optimalcount", "optimalgft", "auctioncount", "countratio", "gft", "gftratio"] print('recipe:', recipe) results_table = TeeTable(TABLE_COLUMNS, results_csv_file) recipe_str = str(recipe).replace(',', '->') if stocks_prices is None: (stocks_prices, stock_names) = getStocksTreePrices(recipe, agent_counts, agent_values) if nums_of_agents is None: nums_of_agents = [10000000] total_results = {} for num_of_agents_per_category in nums_of_agents: total_results[str(num_of_agents_per_category)] = [] for i in range(len(stock_names)): for _ in range(num_of_iterations): last_iteration = False for j in range(len(stocks_prices[i])): random.shuffle(stocks_prices[i][j]) for num_of_agents_per_category in nums_of_agents: num_of_possible_ps = min(num_of_agents_per_category, len(stocks_prices[i][0])) if last_iteration is True and num_of_possible_ps < num_of_agents_per_category: break if num_of_possible_ps < num_of_agents_per_category: if last_iteration is True: break else: last_iteration = True market = Market([AgentCategory("agent", stocks_prices[i][j]) for j in range(len(stocks_prices[i]))]) else: market = Market([AgentCategory("agent", stocks_prices[i][j][0:int(num_of_possible_ps*agent_counts[j])]) for j in range(len(stocks_prices[i]))]) if num_of_agents_per_category == 6 and _ == 0: print(stock_names[i], market.categories) recipe_tree = RecipeTree(market.categories, recipe) optimal_trade, optimal_count, optimal_gft, kmin, kmax = recipe_tree.optimal_trade_with_counters() #print('optimal trade:', optimal_trade, optimal_count, optimal_gft) auction_trade = budget_balanced_ascending_auction(market, recipe) auction_count = auction_trade.num_of_deals() gft = auction_trade.gain_from_trade() #if auction_count < optimal_count - 1: # #the auction count is less more than 1 than the optimal count. # print('Warning!!!', 'optimal_count:', optimal_count, 'auction_count:', auction_count, 'num_of_possible_ps:', num_of_possible_ps) # if num_of_possible_ps < 10: # print(market.categories) #if optimal_count > 0 and gft < optimal_gft * (1 - 1/optimal_count): # #the auction count is less more than 1 than the optimal count. # print('Warning GFT!!!', 'optimal_count:', optimal_count, 'auction_count:', auction_count, # 'num_of_possible_ps:', num_of_possible_ps, 'optimal_gft:', optimal_gft, 'gft:', gft) # if num_of_possible_ps < 20: # print(market.categories) if optimal_count < auction_count : #the auction count is less more than 1 than the optimal count. print('Warning count!!!', 'optimal_count:', optimal_count, 'auction_count:', auction_count, 'num_of_possible_ps:', num_of_possible_ps, 'optimal_gft:', optimal_gft, 'gft:', gft) if num_of_possible_ps < 20: print(market.categories) results = [("stockname", stock_names[i]), ("recipe", recipe_str), ("numpossibletrades", num_of_possible_ps), ("optimalcount", optimal_count), ("optimalkmin", kmin), ("optimalkmax", kmax), ("optimalgft", optimal_gft), ("auctioncount", auction_count), ("countratio", 0 if optimal_count==0 else auction_count / optimal_count*100), ("gft", gft), ("gftratio", 0 if optimal_gft==0 else gft / optimal_gft*100) ] #results_table.add(OrderedDict(results)) if len(total_results[str(num_of_agents_per_category)]) == 0: total_results[str(num_of_agents_per_category)] = results[0:len(results)] else: sum_result = total_results[str(num_of_agents_per_category)] for index in range(len(results)): if index > 2: sum_result[index] = (results[index][0], sum_result[index][1] + results[index][1]) for num_of_agents_per_category in nums_of_agents: results = total_results[str(num_of_agents_per_category)] kmin = 0 for index in range(len(results)): if index > 2: #if 'ratio' in results[index][0]: # results[index] = (results[index][0], results[index][1]/len(stock_names)) #elif 'count' in results[index][0]: # results[index] = (results[index][0], results[index][1]/len(stock_names)) #else: results[index] = (results[index][0], results[index][1]/len(stock_names)/num_of_iterations) elif index == 0: results[index] = (results[index][0], 'Average') if results[index][0] == 'optimalkmin': kmin = results[index][1] results.append(('gftformula', 0 if kmin <= 1 else (1-1/kmin)*100)) results_table.add(OrderedDict(results)) results_table.done()
-13.75, -13.7, -13.15, -12.9, -12.9, -12.65 ] # # buyers = removeSeconds(buyers, reduce_number) # sellers = removeSeconds(sellers, reduce_number) # mediators = removeSeconds(mediators, reduce_number) # mediatorsB = removeSeconds(mediatorsB, reduce_number) # # random.shuffle(buyers) # random.shuffle(sellers) # random.shuffle(mediators) # random.shuffle(mediatorsB) market = Market([ AgentCategory("buyer", buyers), AgentCategory("seller", sellers), AgentCategory("mediator", mediators), AgentCategory("mediatorB", mediatorsB), ]) without_gft0 = budget_balanced_trade_reduction(market, recipe, False) with_gft0 = budget_balanced_trade_reduction(market, recipe, True) print(without_gft0) print(with_gft0) without_count = without_gft0.num_of_deals() with_count = with_gft0.num_of_deals() without_gft = without_gft0.gain_from_trade() with_gft = with_gft0.gain_from_trade() print('Compare: Without:', without_gft, "With:", with_gft) print('Compare: Without:', without_count, "With:", with_count) break
def experiment(results_csv_file: str, auction_function: Callable, auction_name: str, recipe: tuple, value_ranges: list, nums_of_agents: list, num_of_iterations: int): """ Run an experiment similar to McAfee (1992) experiment on the given auction. :param auction_function: the function for executing the auction under consideration. :param auction_name: title of the experiment, for printouts. :param nums_of_agents: a list of the numbers of agents with which to run the experiment. :param value_ranges: for each category, a pair (min_value,max_value). The value for each agent in this category is selected uniformly at random between min_value and max_value. :param num_of_iterations: how many times to repeat the experiment for each num of agents. """ results_table = TeeTable(TABLE_COLUMNS, results_csv_file) recipe_str = ":".join(map(str, recipe)) num_of_categories = len(recipe) for num_of_agents_per_category in nums_of_agents: sum_optimal_count = sum_auction_count = 0 # count the number of deals done in the optimal vs. the actual auction. sum_optimal_gft = sum_auction_total_gft = sum_auction_market_gft = 0 for _ in range(num_of_iterations): market = Market([ AgentCategory.uniformly_random( "agent", num_of_agents_per_category * recipe[category], value_ranges[category][0], value_ranges[category][1]) for category in range(num_of_categories) ]) (optimal_trade, _) = market.optimal_trade(recipe) auction_trade = auction_function(market, recipe) sum_optimal_count += optimal_trade.num_of_deals() sum_auction_count += auction_trade.num_of_deals() sum_optimal_gft += optimal_trade.gain_from_trade() sum_auction_total_gft += auction_trade.gain_from_trade( including_auctioneer=True) sum_auction_market_gft += auction_trade.gain_from_trade( including_auctioneer=False) # print("Num of times {} attains the maximum GFT: {} / {} = {:.2f}%".format(title, count_optimal_gft, num_of_iterations, count_optimal_gft * 100 / num_of_iterations)) # print("GFT of {}: {:.2f} / {:.2f} = {:.2f}%".format(title, sum_auction_gft, sum_optimal_gft, 0 if sum_optimal_gft==0 else sum_auction_gft * 100 / sum_optimal_gft)) results_table.add( OrderedDict(( ("iterations", num_of_iterations), ("auction_name", auction_name), ("recipe", recipe_str), ("num_of_agents", num_of_agents_per_category), ("mean_optimal_count", round(sum_optimal_count / num_of_iterations, 2)), ("mean_auction_count", round(sum_auction_count / num_of_iterations, 2)), ("count_ratio", 0 if sum_optimal_count == 0 else int( (sum_auction_count / sum_optimal_count) * 10000) / 100), ("mean_optimal_gft", round(sum_optimal_gft / num_of_iterations, 2)), ("mean_auction_total_gft", round(sum_auction_total_gft / num_of_iterations, 2)), ("total_gft_ratio", 0 if sum_optimal_gft == 0 else round( sum_auction_total_gft / sum_optimal_gft * 100, 2)), ("mean_auction_market_gft", round(sum_auction_market_gft / num_of_iterations, 2)), ("market_gft_ratio", 0 if sum_optimal_gft == 0 else round( sum_auction_market_gft / sum_optimal_gft * 100, 2)), ))) results_table.done()
from agents import AgentCategory from old import ascending_auction_multirecipe_protocol from old.ascending_auction_multirecipe_protocol import budget_balanced_ascending_auction import prices import logging ascending_auction_multirecipe_protocol.logger.setLevel(logging.INFO) prices.logger.setLevel(logging.INFO) print( "\n\n###### TEST MULTI RECIPE AUCTION WITH TWO EQUIVALENT RECIPES: [1,0,1] [1,1,0]" ) market = Market([ AgentCategory("buyer", [17, 14, 13, 9, 6]), AgentCategory("sellerA", [-1, -3, -4, -5, -8, -10]), AgentCategory("sellerB", [-2, -7, -11]), ]) # print(budget_balanced_ascending_auction(market, [[1, 1, 0], [1, 0, 1]])) print( "\n\n###### TEST MULTI RECIPE AUCTION WITH TWO DIFFERENT RECIPES: [1,1,0,0] [1,0,1,1]" ) market = Market([ AgentCategory("buyer", [17, 14, 13, 9, 6]), AgentCategory("seller", [-1, -3, -4, -5, -8, -10]), AgentCategory("producerA", [-1, -3, -5]), AgentCategory("producerB", [-1, -4, -6]), ])
def mcafee_trade_reduction(market: Market, ps_recipe: list, price_heuristic=True): """ Calculate the trade and prices using generalized-trade-reduction. :param market: contains a list of k categories, each containing several agents. :param ps_recipe: a list of integers, one integer per category. Each integer i represents the number of agents of category i that should be in each procurement-set. :param price_heuristic: whether to use the heuristic of setting the price to (s_{k+1)+b_{k+1})/2. Default is true, as in the original paper. :return: Trade object, representing the trade and prices. >>> # ONE BUYER, ONE SELLER >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.])]) >>> print(market); print(mcafee_trade_reduction(market, [1,1])) Traders: [buyer: [9.0], seller: [-4.0]] No trade >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.])]) >>> print(market); print(mcafee_trade_reduction(market, [1,1])) Traders: [buyer: [9.0, 8.0], seller: [-4.0]] No trade >>> market = Market([AgentCategory("seller", [-4.]), AgentCategory("buyer", [9.,8.])]) >>> print(market); print(mcafee_trade_reduction(market, [1,1])) Traders: [seller: [-4.0], buyer: [9.0, 8.0]] No trade >>> market = Market([AgentCategory("seller", [-4.,-3.]), AgentCategory("buyer", [9.])]) >>> print(market); print(mcafee_trade_reduction(market, [1,1])) Traders: [seller: [-3.0, -4.0], buyer: [9.0]] No trade >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(mcafee_trade_reduction(market, [1,1])) Traders: [buyer: [9.0], seller: [-3.0, -4.0]] No trade >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(mcafee_trade_reduction(market, [1,1])) Traders: [buyer: [9.0, 8.0], seller: [-3.0, -4.0]] buyer: [9.0]: all 1 agents trade and pay 8.0 seller: [-3.0]: all 1 agents trade and pay -4.0 >>> market = Market([AgentCategory("seller", [-4.,-3.]), AgentCategory("buyer", [9.,8.])]) >>> print(mcafee_trade_reduction(market, [1,1])) seller: [-3.0]: all 1 agents trade and pay -4.0 buyer: [9.0]: all 1 agents trade and pay 8.0 >>> market = Market([AgentCategory("seller", [-8.,-3.]), AgentCategory("buyer", [9.,4.])]) >>> print(mcafee_trade_reduction(market, [1,1])) seller: [-3.0]: all 1 agents trade and pay -6.0 buyer: [9.0]: all 1 agents trade and pay 6.0 # # >>> # >>> # ONE BUYER, ONE SELLER, ONE MEDIATOR # >>> market = Market([AgentCategory("seller", [-4.,-3.]), AgentCategory("buyer", [9.,8.]), AgentCategory("mediator", [-1.,-2.])]) # >>> print(market); print(mcafee_trade_reduction(market, [1,1,1])) # Traders: [seller: [-3.0, -4.0], buyer: [9.0, 8.0], mediator: [-1.0, -2.0]] # seller: [-3.0]: all 1 agents trade and pay -4.0 # buyer: [9.0]: all 1 agents trade and pay 8.0 # mediator: [-1.0, -2.0]: random 1 out of 2 agents trade and pay -4.0 # # >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("mediator", [-1.,-2.]), AgentCategory("seller", [-4.,-3.,-10.])]) # >>> print(market); print(mcafee_trade_reduction(market, [1,1,1])) # Traders: [buyer: [9.0, 8.0], mediator: [-1.0, -2.0], seller: [-3.0, -4.0, -10.0]] # buyer: [9.0]: all 1 agents trade and pay 8.0 # mediator: [-1.0]: all 1 agents trade and pay -2.0 # seller: [-3.0, -4.0]: random 1 out of 2 agents trade and pay -6.0 # # >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("mediator", [-1.,-2.]), AgentCategory("seller", [-4.,-3.,-5.])]) # >>> print(market); print(mcafee_trade_reduction(market, [1,1,1])) # Traders: [buyer: [9.0, 8.0], mediator: [-1.0, -2.0], seller: [-3.0, -4.0, -5.0]] # buyer: [9.0]: all 1 agents trade and pay 8.0 # mediator: [-1.0, -2.0]: random 1 out of 2 agents trade and pay -3.0 # seller: [-3.0, -4.0]: random 1 out of 2 agents trade and pay -5.0 # # >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("mediator", [-1.,-2.]), AgentCategory("seller", [-4.,-3.,-2.])]) # >>> print(market); print(mcafee_trade_reduction(market, [1,1,1])) # Traders: [buyer: [9.0, 8.0], mediator: [-1.0, -2.0], seller: [-2.0, -3.0, -4.0]] # buyer: [9.0]: all 1 agents trade and pay 8.0 # mediator: [-1.0, -2.0]: random 1 out of 2 agents trade and pay -4.0 # seller: [-2.0, -3.0]: random 1 out of 2 agents trade and pay -4.0 # # >>> market = Market([AgentCategory("buyer", [9.,8.,7.]), AgentCategory("mediator", [-1.,-2.,-3.]), AgentCategory("seller", [-4.,-3.,-2.])]) # >>> print(market); print(mcafee_trade_reduction(market, [1,1,1])) # Traders: [buyer: [9.0, 8.0, 7.0], mediator: [-1.0, -2.0, -3.0], seller: [-2.0, -3.0, -4.0]] # buyer: [9.0, 8.0]: all 2 agents trade and pay 7.0 # mediator: [-1.0, -2.0]: all 2 agents trade and pay -3.0 # seller: [-2.0, -3.0]: all 2 agents trade and pay -4.0 # # >>> market = Market([AgentCategory("buyer", [9.,8.,4.]), AgentCategory("mediator", [-1.,-2.,-3.]), AgentCategory("seller", [-4.,-3.,-2.])]) # >>> print(market); print(mcafee_trade_reduction(market, [1,1,1])) # Traders: [buyer: [9.0, 8.0, 4.0], mediator: [-1.0, -2.0, -3.0], seller: [-2.0, -3.0, -4.0]] # buyer: [9.0, 8.0]: all 2 agents trade and pay 7.0 # mediator: [-1.0, -2.0]: all 2 agents trade and pay -3.0 # seller: [-2.0, -3.0]: all 2 agents trade and pay -4.0 """ if len(ps_recipe) != market.num_categories: raise ValueError( "There are {} categories but {} elements in the PS recipe".format( market.num_categories, len(ps_recipe))) if any(r != 1 for r in ps_recipe): raise ValueError( "Currently, the trade-reduction protocol supports only recipes of ones; {} was given" .format(ps_recipe)) logger.info("\n#### McAfee Trade Reduction\n") logger.info(market) (optimal_trade, remaining_market) = market.optimal_trade(ps_recipe) for category in remaining_market.categories: if len(category) == 0: category.append(-MAX_VALUE) logger.info("Optimal trade, by increasing GFT: {}".format(optimal_trade)) first_negative_ps = remaining_market.get_highest_agents(ps_recipe) if price_heuristic: price_candidate = sum([abs(x) for x in first_negative_ps ]) / len(first_negative_ps) logger.info("First negative PS: {}, candidate price: {}".format( first_negative_ps, price_candidate)) actual_traders = market.empty_agent_categories() if optimal_trade.num_of_deals() > 0: last_positive_ps = optimal_trade.procurement_sets[0] if price_heuristic and is_price_good_for_ps(price_candidate, last_positive_ps): # All optimal traders trade in the candidate price - no reduction prices = [ price_candidate * (-1 if last_positive_ps[i] < 0 else +1) for i in range(market.num_categories) ] else: # Trade reduction del optimal_trade.procurement_sets[0] prices = last_positive_ps for ps in optimal_trade.procurement_sets: for i in range(market.num_categories): if ps[i] is not None: actual_traders[i].append(ps[i]) else: prices = [0 for i in range(market.num_categories)] logger.info("\n") return TradeWithSinglePrice(actual_traders, ps_recipe, prices)
""" Demonstration of a multiple-clock strongly-budget-balanced ascending auction for a multi-lateral market with two buyers per three sellers (recipe: 2,3) Author: Erel Segal-Halevi Since: 2019-08 """ import sys, os sys.path.insert(0, os.path.abspath('..')) from markets import Market from agents import AgentCategory import ascending_auction_protocol from ascending_auction_protocol import budget_balanced_ascending_auction import logging ascending_auction_protocol.logger.setLevel(logging.INFO) market = Market([ AgentCategory("buyer", [20., 18., 16., 9., 2., 1.]), AgentCategory("seller", [-2., -4., -6., -8., -10., -12., -14.]), ]) print(budget_balanced_ascending_auction(market, [2, 3])) # Here, k=1, and the final trade involves 2 buyers and 5 sellers # (so 3 sellers should be selected at random). print(budget_balanced_ascending_auction(market, [3, 2])) # Here, k=1, and the final trade involves 5 buyers and 3 sellers # (so 3 buyers and 2 sellers should be selected at random).
def experiment(results_csv_file: str, auction_functions: list, auction_names: str, recipe: tuple, nums_of_agents=None, stocks_prices: list = None, stock_names: list = None, num_of_iterations=1000, run_with_stock_prices=True, report_diff=False): """ Run an experiment similar to McAfee (1992) experiment on the given auction. :param results_csv_file: the experiment result file. :param auction_functions: list of functions for executing the auction under consideration. :param auction_names: titles of the experiment, for printouts. :param recipe: can be any vector of ones, e.g. (1,1,1), for our trade-reduction mechanism, or any vector of positive integers for our ascending-auction mechanism. :param stocks_prices: list of prices for each stock and each agent. :param stock_names: list of stocks names which prices are belongs, for naming only. """ TABLE_COLUMNS = [ "iterations", "stockname", "recipe", "numpossibletrades", "optimalcount", "gftratioformula", "optimalcountwithgftzero", "optimalgft", "optimalgftwithgftzero" ] AUCTION_COLUMNS = [ "count", "countratio", "totalgft", "totalgftratio", "withoutgftzerocountratio", "withoutgftzerototalgft", "withoutgftzerototalgftratio", "marketgft", "marketgftratio" ] if path.exists(results_csv_file): print('The file', results_csv_file, 'already exists, skipping') return else: print('Running for the file', results_csv_file) if stocks_prices is None: (stocks_prices, stock_names) = getStocksPricesShuffled() column_names = TABLE_COLUMNS column_names += [ auction_name + column for auction_name in auction_names for column in AUCTION_COLUMNS ] results_table = TeeTable(column_names, results_csv_file) recipe_str = ":".join(map(str, recipe)) recipe_sum = sum(recipe) recipe_sum_for_buyer = (recipe_sum - recipe[0]) / recipe[0] if nums_of_agents is None: nums_of_agents = [10000000] #print(nums_of_agents) total_results = {} for num_of_agents_per_category in nums_of_agents: total_results[str(num_of_agents_per_category)] = [] #print(total_results) for i in range(len(stocks_prices)): stock_prices = stocks_prices[i] for num_of_possible_ps in nums_of_agents: for iteration in range(num_of_iterations): categories = [] if run_with_stock_prices: while len(stock_prices) < num_of_possible_ps * recipe_sum: stock_prices = stock_prices + stock_prices random.shuffle(stock_prices) index = 0 for category in recipe: next_index = index + num_of_possible_ps * category price_sign = recipe_sum_for_buyer if index == 0 else -1 #price_value_multiple = -1 * buyer_agent_count if index > 0 else recipe_sum - buyer_agent_count categories.append( AgentCategory("agent", [ int(price * price_sign) for price in stock_prices[index:next_index] ])) index = next_index else: #prices from random. for index in range(len(recipe)): #for category in recipe: min_value = -100000 if index > 0 else recipe_sum_for_buyer max_value = -1 if index > 0 else 100000 * recipe_sum_for_buyer categories.append( AgentCategory.uniformly_random( "agent", num_of_possible_ps * recipe[index], min_value, max_value)) market = Market(categories) (optimal_trade, _) = market.optimal_trade(ps_recipe=list(recipe), max_iterations=10000000, include_zero_gft_ps=False) optimal_count = optimal_trade.num_of_deals() optimal_gft = optimal_trade.gain_from_trade() (optimal_trade_with_gft_zero, _) = market.optimal_trade(ps_recipe=list(recipe), max_iterations=10000000) optimal_count_with_gft_zero = optimal_trade_with_gft_zero.num_of_deals( ) optimal_gft_with_gft_zero = optimal_trade_with_gft_zero.gain_from_trade( ) results = [ ("iterations", num_of_iterations), ("stockname", stock_names[i]), ("recipe", recipe_str), ("numpossibletrades", int(num_of_possible_ps)), ("optimalcount", optimal_count), ("gftratioformula", (optimal_count - 1) * 100 / (optimal_count if min(recipe) == max(recipe) and recipe[0] == 1 else optimal_count + 1) if optimal_count > 1 else 0), ("optimalcountwithgftzero", optimal_count_with_gft_zero), ("optimalgft", optimal_gft), ("optimalgftwithgftzero", optimal_gft_with_gft_zero) ] for auction_index in range(len(auction_functions)): auction_trade = auction_functions[auction_index](market, recipe) count = auction_trade.num_of_deals() total_gft = auction_trade.gain_from_trade( including_auctioneer=True) market_gft = auction_trade.gain_from_trade( including_auctioneer=False) auction_name = auction_names[auction_index] results.append( (auction_name + "count", auction_trade.num_of_deals())) results.append( (auction_name + "countratio", 0 if optimal_count == 0 else (count / optimal_count_with_gft_zero) * 100)) results.append((auction_name + "totalgft", total_gft)) results.append((auction_name + "totalgftratio", 0 if optimal_gft == 0 else total_gft / optimal_gft_with_gft_zero * 100)) results.append((auction_name + "marketgft", market_gft)) results.append((auction_name + "marketgftratio", 0 if optimal_gft == 0 else market_gft / optimal_gft_with_gft_zero * 100)) results.append((auction_name + "withoutgftzerocountratio", 0 if optimal_count == 0 else (count / optimal_count) * 100)) results.append( (auction_name + "withoutgftzerototalgft", total_gft)) results.append( (auction_name + "withoutgftzerototalgftratio", 0 if optimal_gft == 0 else total_gft / optimal_gft * 100)) #We check which auction did better and print the market and their results. if report_diff: gft_to_compare = -1 k_to_compare = -1 gft_found = False k_found = False for (label, value) in results: if 'SBB' in label: if gft_found is False and label.endswith( 'totalgft'): if gft_to_compare < 0: gft_to_compare = value elif gft_to_compare != value: with open('diff_in_sbbs_gft.txt', 'a') as f: f.write( 'There is diff in gft between two auctions: ' + str(gft_to_compare) + ' ' + str(value) + '\n') f.write(str(results) + '\n') if num_of_possible_ps < 10: f.write(str(market) + '\n') gft_found = True elif k_found is False and label.endswith('count'): if k_to_compare < 0: k_to_compare = value elif k_to_compare != value: with open('diff_in_sbbs_k.txt', 'a') as f: f.write( 'There is diff in gft between two auctions: ' + str(k_to_compare) + ' ' + str(value) + '\n') f.write(str(results) + '\n') if num_of_possible_ps < 10: f.write(str(market) + '\n') k_found = True compare_sbbs = True if compare_sbbs: gft_to_compare = -1 k_to_compare = -1 gft_found = False k_found = False for (label, value) in results: if 'SBB' in label: if gft_found is False and label.endswith( 'totalgft'): if gft_to_compare < 0: gft_to_compare = value elif gft_to_compare > value: with open('diff_in_sbbs_gft.txt', 'a') as f: f.write( 'There is diff in gft between two auctions: ' + str(gft_to_compare) + ' ' + str(value) + '\n') f.write(str(results) + '\n') if num_of_possible_ps < 10: f.write(str(market) + '\n') gft_found = True elif k_found is False and label.endswith('count'): if k_to_compare < 0: k_to_compare = value elif k_to_compare > value: with open('diff_in_sbbs_k.txt', 'a') as f: f.write( 'There is diff in gft between two auctions: ' + str(k_to_compare) + ' ' + str(value) + '\n') f.write(str(results) + '\n') if num_of_possible_ps < 10: f.write(str(market) + '\n') k_found = True #results_table.add(OrderedDict(results)) #print(results) if len(total_results[str(num_of_possible_ps)]) == 0: total_results[str( num_of_possible_ps)] = results[0:len(results)] else: sum_result = total_results[str(num_of_possible_ps)] for index in range(len(results)): if index > 3: sum_result[index] = (results[index][0], sum_result[index][1] + results[index][1]) #print(total_results) print(stock_names[i], end=',') #break print() division_number = num_of_iterations * len(stocks_prices) #division_number = num_of_iterations for num_of_possible_ps in nums_of_agents: results = total_results[str(num_of_possible_ps)] for index in range(len(results)): if 'gftratio' in results[index][0]: results[index] = (results[index][0], padding_zeroes( results[index][1] / division_number, 3)) elif index > 3: results[index] = (results[index][0], padding_zeroes( results[index][1] / division_number, 2)) elif index == 1: results[index] = (results[index][0], 'Average') #print(results) results_table.add(OrderedDict(results)) results_table.done()
# AgentCategory("sellerB", [-3, -4]), # ]) # print(budget_balanced_ascending_auction_twolevels(market, [1, 1 ,1])) # print("\n\n###### TWO NON-BINARY RECIPES: [1,2,0], [1,0,2]") # market = Market([ # AgentCategory("buyer", [19,18,17,16,15,14,13]), # AgentCategory("sellerA", [-1, -2]), # AgentCategory("sellerB", [-3, -4]), # ]) # print(budget_balanced_ascending_auction_twolevels(market, [1, 2, 2])) print("\n\n###### COUNTER-EXAMPLE FOR CEILING CHILDREN") num_of_seller_categories = 8 market = Market( [AgentCategory("buyer", [101] * 10 * (num_of_seller_categories + 1))] + [AgentCategory("producer", [-50] * 20)] + [AgentCategory("seller", [-100] + [-2] * 21)] * num_of_seller_categories) # print(budget_balanced_ascending_auction_twolevels(market, [1, 2] + [2] * num_of_seller_categories)) print("\n\n###### DVIR'S EXAMPLE BINARY") num_of_seller_categories = 8 market = Market([AgentCategory("buyer", [100] * num_of_seller_categories)] + [ AgentCategory("seller", [-80 + i]) for i in range(num_of_seller_categories) ]) print( budget_balanced_ascending_auction_twolevels( market, [1] + [1] * num_of_seller_categories)) ascending_auction_recipetree_twolevels_protocol.logger.setLevel(logging.INFO) prices.logger.setLevel(logging.INFO) print("\n\n###### DVIR'S EXAMPLE")
from markets import Market if __name__ == '__main__': m = Market() hd = m.full_historic_data() hd['next_price'] = hd['price'].shift(-1) #target hd['increase'] = (hd['next_price'] - hd['price'] ) / hd['price'] > 0.01 #increase at least by 1% features = [ 'date', 'volume24', 'marketCap', 'availableSupplyNumber', 'price', 'next_price', 'increase' ] hd[features].to_csv('~/Data/crypto/ETH.csv', index=False) print "done"
# market = Market([ # AgentCategory("buyer", [20, 19, 18, 17, 16, 15]), # AgentCategory("seller", [-1, -2, -5, -6, -9, -10]), # AgentCategory("producerA", [-4, -8]), # AgentCategory("producerB", [-3, -7]), # ]) # print(budget_balanced_ascending_auction(market, recipes_1100_1011)) import sys, os; sys.path.insert(0, os.path.abspath('..')) print("\n\n###### TEST MULTI RECIPE AUCTION - 4 PATHS") market = Market([ AgentCategory("C0", [400, 300, 200, 100]), AgentCategory("C1", [-1, -11]), AgentCategory("C2", [-2, -22]), AgentCategory("C3", [-3, -33]), AgentCategory("C4", [-4, -44]), AgentCategory("C5", [-5, -55]), AgentCategory("C6", [-6, -66]), ]) recipes_4paths = [0, [1, [2, None, 3, None], 4, [5, None, 6, None]]] # The recipes are: # 1,1,1,0,0,0,0 [C0, C1, C2] # 1,1,0,1,0,0,0 [C0, C1, C3] # 1,0,0,0,1,1,0 [C0, C4, C5] # 1,0,0,0,1,0,1 [C0, C4, C6] print(budget_balanced_ascending_auction(market, recipes_4paths))
buyers = randomArray(2, 20, 10) sellers = randomArray(-10, -1, 20) # # buyers = [135.9, 136.7999, 143.5499, 135.9, 136.7999, 143.5499, 144.0] # sellers = [-135.9, -136.7999, -143.5499, -18.95, -18.9, -17.95, -17.9, -17.7999, -17.7999, -17.2999, -17.0, -16.95, -16.7999, -15.15, -15.0, -15.0, -14.9, -14.2, -14.2, -14.1, -13.95] # # random.shuffle(buyers) # random.shuffle(sellers) # # buyers = removeSeconds(buyers, reduce_number) # sellers = removeSeconds(sellers, reduce_number) market = Market([ AgentCategory("buyer", buyers), AgentCategory("seller", sellers), ]) without_gft0 = budget_balanced_trade_reduction(market, recipe, False) with_gft0 = budget_balanced_trade_reduction(market, recipe, True) print(without_gft0) print(with_gft0) without_count = without_gft0.num_of_deals() with_count = with_gft0.num_of_deals() without_gft = without_gft0.gain_from_trade() with_gft = with_gft0.gain_from_trade() print('Compare: Without:', without_gft, "With:", with_gft) print('Compare: Without:', without_count, "With:", with_count) if without_count != with_count and with_gft != without_gft: print("Reached end")
Since: 2019-11 """ import sys, os sys.path.insert(0, os.path.abspath('..')) from markets import Market from agents import AgentCategory import ascending_auction_protocol from ascending_auction_protocol import budget_balanced_ascending_auction import logging ascending_auction_protocol.logger.setLevel(logging.INFO) print("\n\n###### RUNNING EXAMPLE 3 FROM THE PAPER FOR TYPE (2,2,3)") market = Market([ AgentCategory("buyer", [17, 16, 15, 14, 13, 12, 10, 6]), AgentCategory("mediator", [-3, -4, -5, -6, -7, -8, -9, -10]), AgentCategory("seller", [-1, -2, -3, -4, -5, -6, -7, -8]), ]) # print(budget_balanced_ascending_auction(market, [2,2,3])) print("\n\n###### OTHER EXAMPLES FOR (2,2,3)") market = Market([ AgentCategory("buyer", [17, 16, 15, 14, 13, 12, 10, 6]), AgentCategory("mediator", [-3, -4, -5, -6, -7, -8, -9, -10]), AgentCategory("seller", [-1, -2, -3, -4, -5, -6, -7, -8]), ]) print(budget_balanced_ascending_auction(market, [2, 2, 3]))
def budget_balanced_ascending_auction( market: Market, ps_recipe_struct: List[Any], agent_counts: List[int] = None) -> TradeWithMultipleRecipes: """ Calculate the trade and prices using generalized-ascending-auction. Allows multiple recipes, but they must be represented by a *recipe tree*. :param market: contains a list of k categories, each containing several agents. :param ps_recipe_struct: a nested list of integers. Each integer represents a category-index. The nested list represents a tree, where each path from root to leaf represents a recipe. For example: [0, [1, None]] is a single recipe with categories {0,1}. [0, [1, None, 2, None]] is two recipes with categories {0,1} and {0,2}. :return: Trade object, representing the trade and prices. >>> logger.setLevel(logging.DEBUG) >>> # ONE BUYER, ONE SELLER >>> recipe_11 = [0, [1, None]] >>> agent_counts = [1, 2, 1, 2] >>> recipe_1100_1011 = [0, [1, None, 2, [3, None]]] >>> >>> market = Market([AgentCategory("buyer", [101,101,101,101,101]), AgentCategory("seller1", [-1,-90]), AgentCategory("seller2", [-1,-90]), AgentCategory("seller", [-50, -50, -50, -50, -50, -50])]) >>> RecipeTree(market.categories, [0, [1, None, 2, None, 3, None]], [1,2,2,2]).optimal_trade_with_counters() ([([101, -1, -90], {'counter': 1, 'index': 1}), ([101, -1, -90], {'counter': 1, 'index': 2}), ([101, -50, -50], {'counter': 3, 'index': 3}), ([101, -50, -50], {'counter': 3, 'index': 3}), ([101, -50, -50], {'counter': 3, 'index': 3})], 5, 23, 1, 3, {'1': 1, '2': 1, '3': 3}) >>> print(market); print(budget_balanced_ascending_auction(market, [0, [1, None, 2, None, 3, None]], [1,2,2,2])) Traders: [buyer: [101, 101, 101, 101, 101], seller1: [-1, -90], seller2: [-1, -90], seller: [-50, -50, -50, -50, -50, -50]] seller1: 0 potential deals, price=-50.5 seller2: 0 potential deals, price=-50.5 seller: 3 potential deals, price=-50.5 buyer: all 3 remaining traders selected, price=101.0 (seller1 + seller2 + seller): all 3 remaining deals selected 3 deals overall >>> market = Market([AgentCategory("buyer", [101,101,101]), AgentCategory("seller1", [-1,-90]), AgentCategory("seller2", [-1,-90]), AgentCategory("seller", [-50, -50, -50, -50, -50, -50])]) >>> RecipeTree(market.categories, [0, [1, None, 2, None, 3, None]], [1,2,2,2]).optimal_trade_with_counters() ([([101, -1, -90], {'counter': 1, 'index': 1}), ([101, -1, -90], {'counter': 1, 'index': 2}), ([101, -50, -50], {'counter': 1, 'index': 3})], 3, 21, 1, 1, {'1': 1, '2': 1, '3': 1}) >>> print(market); print(budget_balanced_ascending_auction(market, [0, [1, None, 2, None, 3, None]], [1,2,2,2])) Traders: [buyer: [101, 101, 101], seller1: [-1, -90], seller2: [-1, -90], seller: [-50, -50, -50, -50, -50, -50]] seller1: 0 potential deals, price=-50.0 seller2: 0 potential deals, price=-50.0 seller: 2 potential deals, price=-50.0 buyer: 2 out of 3 remaining traders selected, price=100.0 (seller1 + seller2 + seller): all 2 remaining deals selected 2 deals overall >>> market = Market([AgentCategory("buyer", [27.,21., 17.,11., 3.]), AgentCategory("seller", [-4.0, -5.0, -11.0]), AgentCategory("A", [-2.0, -3.0, -11.0]), AgentCategory("B", [-1.0, -2.0, -8.0])]) >>> RecipeTree(market.categories, recipe_1100_1011, agent_counts).optimal_trade_with_counters() ([([27.0, -2.0, -1.0, -2.0], {'counter': 1, 'index': 3}), ([21.0, -4.0, -5.0], {'counter': 1, 'index': 1})], 2, 34.0, 1, 1, {'3': 1, '1': 1}) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_1100_1011, agent_counts)) Traders: [buyer: [27.0, 21.0, 17.0, 11.0, 3.0], seller: [-4.0, -5.0, -11.0], A: [-2.0, -3.0, -11.0], B: [-1.0, -2.0, -8.0]] seller: 1 potential deals, price=-8.5 B: 1 potential deals, price=-7.0 A: all 1 remaining traders selected, price=-3.0 (B): all 1 remaining deals selected buyer: all 2 remaining traders selected, price=17.0 (seller + A): all 2 remaining deals selected 2 deals overall >>> market = Market([AgentCategory("buyer", [17.,11.]), AgentCategory("seller", [-5.0]), AgentCategory("A", [-3.0]), AgentCategory("B", [-2.0])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_1100_1011)) Traders: [buyer: [17.0, 11.0], seller: [-5.0], A: [-3.0], B: [-2.0]] No trade >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0], seller: [-4.0]] No trade >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0, 8.0], seller: [-4.0]] seller: 1 potential deals, price=-8.0 buyer: all 1 remaining traders selected, price=8.0 (seller): all 1 remaining deals selected 1 deals overall >>> logger.setLevel(logging.WARNING) >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0], seller: [-3.0, -4.0]] No trade >>> logger.setLevel(logging.WARNING) >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0, 8.0], seller: [-3.0, -4.0]] seller: 1 potential deals, price=-4.0 buyer: 1 out of 2 remaining traders selected, price=4.0 (seller): all 1 remaining deals selected 1 deals overall """ logger.info("\n#### Multi-Recipe Budget-Balanced Ascending Auction\n") logger.info(market) logger.info("Procurement-set recipe struct: {}".format(ps_recipe_struct)) logger.info("Procurement-set recipe agent counts: {}".format(agent_counts)) remaining_market = market.clone() recipe_tree = RecipeTree(remaining_market.categories, ps_recipe_struct, agent_counts) logger.info("Tree of recipes: {}".format(recipe_tree.paths_to_leaf())) ps_recipes = recipe_tree.recipes() logger.info("Procurement-set recipes: {}".format(ps_recipes)) optimal_trade, optimal_count, optimal_GFT = recipe_tree.optimal_trade() logger.info("For comparison, the optimal trade has k=%d, GFT=%f: %s\n", optimal_count, optimal_GFT, optimal_trade) # optimal_trade = market.optimal_trade(ps_recipe)[0] #### STOPPED HERE prices = SimultaneousAscendingPriceVectors(ps_recipes, -MAX_VALUE, agent_counts) while True: largest_category_size, combined_category_size, indices_of_prices_to_increase = recipe_tree.largest_categories( indices=True) logger.info("\n") logger.info(remaining_market) logger.info( "Largest category indices are %s. Largest category size = %d, combined category size = %d", indices_of_prices_to_increase, largest_category_size, combined_category_size) if combined_category_size == 0: logger.info("\nCombined category size is 0 - no trade!") logger.info(" Final price-per-unit vector: %s", prices) logger.info(remaining_market) return TradeWithMultipleRecipes( remaining_market.categories, recipe_tree, prices.map_category_index_to_price(), agent_counts) increases = [] for category_index in indices_of_prices_to_increase: category = remaining_market.categories[category_index] target_price = category.lowest_agent_value( ) if category.size() > 0 else MAX_VALUE increases.append((category_index, target_price, category.name)) logger.info("Planned price-increases: %s", increases) prices.increase_prices(increases) map_category_index_to_price = prices.map_category_index_to_price() if prices.status == PriceStatus.STOPPED_AT_ZERO_SUM: logger.info("\nPrice crossed zero.") logger.info(" Final price-per-unit vector: %s", map_category_index_to_price) logger.info(remaining_market) return TradeWithMultipleRecipes(remaining_market.categories, recipe_tree, map_category_index_to_price, agent_counts) remove_lowest_agent(market, remaining_market, map_category_index_to_price, indices_of_prices_to_increase)
def budget_balanced_ascending_auction( market: Market, ps_recipe_struct: List[Any]) -> TradeWithMultipleRecipes: """ Calculate the trade and prices using generalized-ascending-auction. Allows multiple recipes, but they must be represented by a *recipe tree*. :param market: contains a list of k categories, each containing several agents. :param ps_recipe_struct: a nested list of integers. Each integer represents a category-index. The nested list represents a tree, where each path from root to leaf represents a recipe. For example: [0, [1, None]] is a single recipe with categories {0,1}. [0, [1, None, 2, None]] is two recipes with categories {0,1} and {0,2}. :return: Trade object, representing the trade and prices. >>> logger.setLevel(logging.WARNING) >>> # ONE BUYER, ONE SELLER >>> recipe_11 = [0, [1, None]] >>> >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0], seller: [-4.0]] No trade >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0, 8.0], seller: [-4.0]] seller: 1 potential deals, price=-8.0 buyer: all 1 traders selected, price=8.0 seller: all 1 traders selected 1 deals overall >>> logger.setLevel(logging.WARNING) >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0], seller: [-3.0, -4.0]] No trade >>> logger.setLevel(logging.WARNING) >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, recipe_11)) Traders: [buyer: [9.0, 8.0], seller: [-3.0, -4.0]] seller: 1 potential deals, price=-4.0 buyer: 1 out of 2 traders selected, price=4.0 seller: all 1 traders selected 1 deals overall """ logger.info("\n#### Multi-Recipe Budget-Balanced Ascending Auction\n") logger.info(market) logger.info("Procurement-set recipe struct: {}".format(ps_recipe_struct)) remaining_market = market.clone() recipe_tree = RecipeTree(remaining_market.categories, ps_recipe_struct) logger.info("Tree of recipes: {}".format(recipe_tree.paths_to_leaf())) ps_recipes = recipe_tree.recipes() logger.info("Procurement-set recipes: {}".format(ps_recipes)) optimal_trade, optimal_count, optimal_GFT = recipe_tree.optimal_trade() logger.info("For comparison, the optimal trade has k=%d, GFT=%f: %s\n", optimal_count, optimal_GFT, optimal_trade) # optimal_trade = market.optimal_trade(ps_recipe)[0] #### STOPPED HERE prices = SimultaneousAscendingPriceVectors(ps_recipes, -MAX_VALUE) while True: largest_category_size, combined_category_size, indices_of_prices_to_increase = recipe_tree.largest_categories( indices=True) logger.info("\n") logger.info(remaining_market) logger.info( "Largest category indices are %s. Largest category size = %d, combined category size = %d", indices_of_prices_to_increase, largest_category_size, combined_category_size) if combined_category_size == 0: logger.info("\nCombined category size is 0 - no trade!") logger.info(" Final price-per-unit vector: %s", prices) logger.info(remaining_market) return TradeWithMultipleRecipes( remaining_market.categories, recipe_tree, prices.map_category_index_to_price()) increases = [] for category_index in indices_of_prices_to_increase: category = remaining_market.categories[category_index] target_price = category.lowest_agent_value( ) if category.size() > 0 else MAX_VALUE increases.append((category_index, target_price, category.name)) logger.info("Planned price-increases: %s", increases) prices.increase_prices(increases) map_category_index_to_price = prices.map_category_index_to_price() if prices.status == PriceStatus.STOPPED_AT_ZERO_SUM: logger.info("\nPrice crossed zero.") logger.info(" Final price-per-unit vector: %s", map_category_index_to_price) logger.info(remaining_market) return TradeWithMultipleRecipes(remaining_market.categories, recipe_tree, map_category_index_to_price) for category_index in range(market.num_categories): category = remaining_market.categories[category_index] if map_category_index_to_price[category_index] is not None \ and category.size()>0 \ and category.lowest_agent_value() <= map_category_index_to_price[category_index]: category.remove_lowest_agent() logger.info("{} after: {} agents remain".format( category.name, category.size()))
""" from markets import Market from agents import AgentCategory import ascending_auction_protocol from ascending_auction_protocol import budget_balanced_ascending_auction import logging ascending_auction_protocol.logger.setLevel(logging.INFO) ps_recipe = [1,1,1] print("\n\n###### RUNNING EXAMPLE FROM THE PAPER FOR TYPE (1,1,1)") # Price stops between buyers, k=3: market = Market([ AgentCategory("buyer", [17, 14, 13, 9, 6]), AgentCategory("seller", [-1, -4, -5, -8, -11]), AgentCategory("mediator", [-1, -3, -4, -7, -10])]) print(budget_balanced_ascending_auction(market, ps_recipe)) print("\n\n###### SIMILAR EXAMPLE, WHERE PRICE STOPS BETWEEN SELLERS:") market = Market([ AgentCategory("buyer", [17, 14, 13, 9, 6]), AgentCategory("seller", [-1, -4, -5, -8, -11]), AgentCategory("mediator", [-1, -3, -6, -7, -10])]) print(budget_balanced_ascending_auction(market, ps_recipe)) print("\n\n###### SIMILAR EXAMPLE, WHERE PRICE STOPS BETWEEN MEDIATORS:") market = Market([ AgentCategory("buyer", [17, 14, 13, 9, 6]), AgentCategory("seller", [-1, -4, -6.5, -8, -11]),
def experiment(results_csv_file: str, recipe: list, value_ranges: list, nums_of_agents: list, num_of_iterations: int, agent_counts: list, agent_values: list, agent_counts_integer: list): """ Run an experiment similar to McAfee (1992) experiment on the given auction. :param results_csv_file: the experiment result file. :param auction_functions: list of functions for executing the auction under consideration. :param auction_names: titles of the experiment, for printouts. :param recipe: can be any vector of ones, e.g. (1,1,1), for our trade-reduction mechanism, or any vector of positive integers for our ascending-auction mechanism. :param nums_of_agents: list of n(s) for number of possible trades to make the calculations. :param stocks_prices: list of prices for each stock and each agent. :param stock_names: list of stocks names which prices are belongs, for naming only. """ TABLE_COLUMNS = [ "iterations", "recipe", "numofagents", "optimalcount", "optimalkmin", "optimalkmax", "gftformula", "auctioncount", "auctionkmin", "auctionkmax", "countratio", "optimalgft", "auctiongft", "gftratio" ] GFT_ROUND = 1 K_ROUND = 2 RATIO_ROUND = 3 print('recipe:', recipe) results_table = TeeTable(TABLE_COLUMNS, results_csv_file) results_table_integer = TeeTable(TABLE_COLUMNS, results_csv_file[0:-4] + '_integer.csv') recipe_str = str(recipe).replace(',', '-') category_size_list = get_agents_analyze(recipe) for i in range(len(nums_of_agents)): sum_optimal_count = sum_auction_count = sum_optimal_kmin = sum_optimal_kmax = 0 # count the number of deals done in the optimal vs. the actual auction. sum_optimal_gft = sum_auction_total_gft = sum_auction_kmin = sum_auction_kmax = 0 sum_optimal_count_integer = sum_auction_count_integer = sum_optimal_kmin_integer = sum_optimal_kmax_integer = 0 # count the number of deals done in the optimal vs. the actual auction. sum_optimal_gft_integer = sum_auction_total_gft_integer = sum_auction_kmin_integer = sum_auction_kmax_integer = 0 for iteration in range(num_of_iterations): if iteration % 10000 == 0: print('iteration:', iteration) agents = [] agents_integer = [] for category in range(len(category_size_list)): sign = 0 if category == 0 else 1 agent_category = AgentCategory.uniformly_random( "agent", int(nums_of_agents[i] * agent_counts[category]), value_ranges[sign][0] * agent_values[category], value_ranges[sign][1] * agent_values[category]) agents.append(agent_category) agents_integer.append( AgentCategory( agent_category.name, agent_category.values + agent_category.values)) #agents.append(AgentCategory.uniformly_random("agent", nums_of_agents[i], value_ranges[sign][0], value_ranges[sign][1])) market = Market(agents) market_integer = Market(agents_integer) #print(agents) recipe_tree = RecipeTree(market.categories, recipe) recipe_tree_integer = RecipeTree(market_integer.categories, recipe, agent_counts_integer) optimal_trade, optimal_count, optimal_gft, kmin, kmax = recipe_tree.optimal_trade_with_counters( ) optimal_trade_integer, optimal_count_integer, optimal_gft_integer, kmin_integer, kmax_integer = recipe_tree_integer.optimal_trade_with_counters( ) #print('optimal trade:', optimal_trade, optimal_count, optimal_gft) auction_trade = budget_balanced_ascending_auction(market, recipe) auction_trade_integer = budget_balanced_ascending_auction( market_integer, recipe, agent_counts_integer) auction_count = auction_trade.num_of_deals() auction_count_integer = auction_trade_integer.num_of_deals() auction_kmin = auction_trade.min_num_of_deals() auction_kmin_integer = auction_trade_integer.min_num_of_deals() auction_kmax = auction_trade.max_num_of_deals() auction_kmax_integer = auction_trade_integer.max_num_of_deals() gft = auction_trade.gain_from_trade() gft_integer = auction_trade_integer.gain_from_trade() #if optimal_count > 0 and gft < optimal_gft * (1 - 1/optimal_count): #the auction count is less more than 1 than the optimal count. # print('Warning GFT!!!', 'optimal_count:', optimal_count, 'auction_count:', auction_count, # 'num_of_possible_ps:', nums_of_agents[i], 'optimal_gft:', optimal_gft, 'gft:', gft) # if nums_of_agents[i] < 20: # print(market.categories) sum_optimal_count += optimal_count sum_optimal_count_integer += optimal_count_integer sum_auction_count += auction_count sum_auction_count_integer += auction_count_integer sum_optimal_kmin += kmin sum_optimal_kmin_integer += kmin_integer sum_optimal_kmax += kmax sum_optimal_kmax_integer += kmax_integer sum_auction_kmin += auction_kmin sum_auction_kmin_integer += auction_kmin_integer sum_auction_kmax += auction_kmax sum_auction_kmax_integer += auction_kmax_integer sum_optimal_gft += optimal_gft sum_optimal_gft_integer += optimal_gft_integer sum_auction_total_gft += gft sum_auction_total_gft_integer += gft_integer if auction_count < optimal_count - 2: #the auction count is less more than 1 than the optimal count. print('Warning!!!', 'optimal_count:', optimal_count, 'auction_count:', auction_count, 'num_of_possible_ps:', nums_of_agents[i]) if nums_of_agents[i] < 10: print(market.categories) # print("Num of times {} attains the maximum GFT: {} / {} = {:.2f}%".format(title, count_optimal_gft, num_of_iterations, count_optimal_gft * 100 / num_of_iterations)) # print("GFT of {}: {:.2f} / {:.2f} = {:.2f}%".format(title, sum_auction_gft, sum_optimal_gft, 0 if sum_optimal_gft==0 else sum_auction_gft * 100 / sum_optimal_gft)) kmin_mean = sum_optimal_kmin / num_of_iterations kmin_mean_integer = sum_optimal_kmin_integer / num_of_iterations results_table.add( OrderedDict([ ("iterations", num_of_iterations), ("recipe", recipe_str), ("numofagents", nums_of_agents[i]), ("optimalcount", int_round(sum_optimal_count / num_of_iterations, K_ROUND)), ("optimalkmin", int_round(kmin_mean, K_ROUND)), ("optimalkmax", int_round(sum_optimal_kmax / num_of_iterations, K_ROUND)), ("gftformula", int_round((1 - 1 / kmin_mean) * 100 if kmin_mean > 1 else 0, RATIO_ROUND)), ("auctioncount", int_round(sum_auction_count / num_of_iterations, K_ROUND)), ("auctionkmin", int_round(sum_auction_kmin / num_of_iterations, K_ROUND)), ("auctionkmax", int_round(sum_auction_kmax / num_of_iterations, K_ROUND)), ("countratio", int_round( 0 if sum_optimal_count == 0 else (sum_auction_count / sum_optimal_count) * 100, RATIO_ROUND)), ("optimalgft", int_round(sum_optimal_gft / num_of_iterations, GFT_ROUND)), ("auctiongft", int_round(sum_auction_total_gft / num_of_iterations, GFT_ROUND)), ("gftratio", '0.00' if sum_optimal_gft == 0 else int_round( sum_auction_total_gft / sum_optimal_gft * 100, RATIO_ROUND)), ])) results_table_integer.add( OrderedDict([ ("iterations", num_of_iterations), ("recipe", recipe_str), ("numofagents", nums_of_agents[i]), ("optimalcount", int_round(sum_optimal_count_integer / num_of_iterations, K_ROUND)), ("optimalkmin", int_round(kmin_mean_integer, K_ROUND)), ("optimalkmax", int_round(sum_optimal_kmax_integer / num_of_iterations, K_ROUND)), ("gftformula", int_round((1 - 1 / kmin_mean_integer) * 100 if kmin_mean_integer > 1 else 0, RATIO_ROUND)), ("auctioncount", int_round(sum_auction_count_integer / num_of_iterations, K_ROUND)), ("auctionkmin", int_round(sum_auction_kmin_integer / num_of_iterations, K_ROUND)), ("auctionkmax", int_round(sum_auction_kmax_integer / num_of_iterations, K_ROUND)), ("countratio", int_round( 0 if sum_optimal_count_integer == 0 else (sum_auction_count_integer / sum_optimal_count_integer) * 100, RATIO_ROUND)), ("optimalgft", int_round(sum_optimal_gft_integer / num_of_iterations, GFT_ROUND)), ("auctiongft", int_round(sum_auction_total_gft_integer / num_of_iterations, GFT_ROUND)), ("gftratio", '0.00' if sum_optimal_gft_integer == 0 else int_round( sum_auction_total_gft_integer / sum_optimal_gft_integer * 100, RATIO_ROUND)), ])) results_table.done()
def budget_balanced_ascending_auction( market:Market, ps_recipes: list)->TradeWithMultipleRecipes: """ Calculate the trade and prices using generalized-ascending-auction. Allows multiple recipes, but only of the following kind: [ [1,0,0,x], [0,1,0,y], [0,0,1,z] ] (i.e., there are n-1 buyer categories and 1 seller category. One agent of category 1 buys x units; of category 2 buys y units; of category 3 buys z units; etc.) :param market: contains a list of k categories, each containing several agents. :param ps_recipes: a list of lists of integers, one integer per category. Each integer i represents the number of agents of category i that should be in each procurement-set. :return: Trade object, representing the trade and prices. >>> # ONE BUYER, ONE SELLER >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.])]) >>> print(market); print(budget_balanced_ascending_auction(market, [[1,1]])) Traders: [buyer: [9.0], seller: [-4.0]] No trade >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.])]) >>> print(market); print(budget_balanced_ascending_auction(market, [[1,1]])) Traders: [buyer: [9.0, 8.0], seller: [-4.0]] No trade >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, [[1,1]])) Traders: [buyer: [9.0], seller: [-3.0, -4.0]] seller: [-3.0]: all 1 agents trade and pay -4.0 buyer: [9.0]: all 1 agents trade and pay 4.0 >>> market = Market([AgentCategory("buyer", [9.,8.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, [[1,1]])) Traders: [buyer: [9.0, 8.0], seller: [-3.0, -4.0]] seller: [-3.0, -4.0]: random 1 out of 2 agents trade and pay -8.0 buyer: [9.0]: all 1 agents trade and pay 8.0 >>> # ONE BUYER, TWO SELLERS >>> market = Market([AgentCategory("buyer", [9.]), AgentCategory("seller", [-4.,-3.])]) >>> print(market); print(budget_balanced_ascending_auction(market, [[1,2]])) Traders: [buyer: [9.0], seller: [-3.0, -4.0]] No trade >>> market = Market([AgentCategory("buyer", [9., 8., 7., 6.]), AgentCategory("seller", [-6., -5., -4.,-3.,-2.,-1.])]) >>> print(market); print(budget_balanced_ascending_auction(market, [[1,2]])) Traders: [buyer: [9.0, 8.0, 7.0, 6.0], seller: [-1.0, -2.0, -3.0, -4.0, -5.0, -6.0]] seller: [-1.0, -2.0, -3.0, -4.0]: random 2 out of 4 agents trade and pay -4.0 buyer: [9.0]: all 1 agents trade and pay 8.0 """ logger.info("\n#### Budget-Balanced Ascending Auction with Multiple Recipes - n-1 buyer categories\n") logger.info(market) logger.info("Procurement-set recipes: %s", ps_recipes) map_buyer_category_to_seller_count = _convert_recipes_to_seller_counts(ps_recipes, market.num_categories) logger.info("Map buyer category index to seller count: %s", map_buyer_category_to_seller_count) # NOTE: Calculating the optimal trade cannot be done greedily - # it requires solving a restricted instance of Knapsack. # optimal_trade = market.optimal_trade(ps_recipe, max_iterations=max_iterations)[0] # logger.info("For comparison, the optimal trade is: %s\n", optimal_trade) remaining_market = market.clone() buyer_categories = remaining_market.categories[:-1] num_buyer_categories = market.num_categories-1 seller_category = remaining_market.categories[-1] prices = AscendingPriceVector([1, 1], -MAX_VALUE) buyer_price_index = 0 seller_price_index = 1 # prices[0] represents the price for all buyer-categories per single unit. # prices[1] represents the price for all sellers. try: num_units_offered = len(seller_category) num_units_demanded = sum([len(buyer_categories[i])*map_buyer_category_to_seller_count[i] for i in range(num_buyer_categories)]) target_unit_count = min(num_units_demanded, num_units_offered) logger.info("%d units demanded by buyers, %d units offered by sellers, minimum is %d", num_units_demanded, num_units_offered, target_unit_count) while True: logger.info("Prices: %s, Target unit count: %d", prices, target_unit_count) price_index = buyer_price_index while True: num_units_demanded = sum([len(buyer_categories[i]) * map_buyer_category_to_seller_count[i] for i in range(num_buyer_categories)]) logger.info(" Buyers demand %d units", num_units_demanded) if num_units_demanded == 0: raise EmptyCategoryException() if num_units_demanded <= target_unit_count: break map_buyer_category_to_lowest_value = [category.lowest_agent_value() for category in buyer_categories] logger.debug(" map_buyer_category_to_lowest_value=%s", map_buyer_category_to_lowest_value) map_buyer_category_to_lowest_value_per_unit = [value / count for value,count in zip(map_buyer_category_to_lowest_value,map_buyer_category_to_seller_count)] logger.debug(" map_buyer_category_to_lowest_value_per_unit=%s", map_buyer_category_to_lowest_value_per_unit) category_index_with_lowest_value_per_unit = min(range(num_buyer_categories), key=lambda i:map_buyer_category_to_lowest_value_per_unit[i]) category_with_lowest_value_per_unit = buyer_categories[category_index_with_lowest_value_per_unit] lowest_value_per_unit = map_buyer_category_to_lowest_value_per_unit[category_index_with_lowest_value_per_unit] logger.info(" lowest value per unit is %f, of category %d (%s)", lowest_value_per_unit, category_index_with_lowest_value_per_unit, category_with_lowest_value_per_unit.name) prices.increase_price_up_to_balance(price_index, category_with_lowest_value_per_unit.lowest_agent_value()/map_buyer_category_to_seller_count[category_index_with_lowest_value_per_unit], category_with_lowest_value_per_unit.name) category_with_lowest_value_per_unit.remove_lowest_agent() category = seller_category # logger.info("\n### Step 1a: balancing the sellers (%s)", category.name) price_index = seller_price_index while True: num_units_offered = len(category) logger.info(" Sellers offer %d units", num_units_offered) if num_units_offered == 0: raise EmptyCategoryException() if num_units_offered <= target_unit_count: break prices.increase_price_up_to_balance(price_index, category.lowest_agent_value(), category.name) category.remove_lowest_agent() target_unit_count -= 1 except EmptyCategoryException: logger.info("\nOne of the categories became empty. No trade!") logger.info(" Final price-per-unit vector: %s", prices) # Construct the final price-vector: buyer_price_per_unit = prices[buyer_price_index] seller_price_per_unit = prices[seller_price_index] final_prices = \ [buyer_price_per_unit * unit_count for unit_count in map_buyer_category_to_seller_count] + \ [seller_price_per_unit] logger.info(" %s", remaining_market) return TradeWithMultipleRecipes(remaining_market.categories, map_buyer_category_to_seller_count, final_prices)