import csv import numpy as np import random from otree.api import * doc = """ This oTree program can run private value, common value, and mixed value auction
The first treatment add an additional seller (auctioner) role.
""" class C(BaseConstants): NAME_IN_URL = 'ambiguity_auction' PLAYERS_PER_GROUP = 3 NUM_ROUNDS = 40 # set the number big enough than the potential rounds used in the experiments. INSTRUCTIONS_TEMPLATE = 'ambiguity_auction/instructions.html' class Subsession(BaseSubsession): # define the parameters as the config file is_practice_round = models.BooleanField(initial=False, doc="""set the practice round, default is False""") # set the rounds number total_rounds = models.IntegerField(initial=0) real_rounds = models.IntegerField(initial=0) # set the grouping method switch_group = models.BooleanField(initial=False) switch_type = models.BooleanField(initial=False) # set distrbution parameters p = models.FloatField(initial=0) a = models.IntegerField(initial=0) b = models.IntegerField(initial=0) c = models.IntegerField(initial=0) d = models.IntegerField(initial=0) info = models.IntegerField(initial=0) class Group(BaseGroup): highest_bid = models.IntegerField() reserve_price = models.IntegerField() info_purchase = models.BooleanField( label='Will you purchase the information of p?', widget=widgets.RadioSelect ) info_will = models.IntegerField( label='What is your maximum willingness to pay for p in points?', min=0 ) guess_p = models.IntegerField( label='What do you think p% might be in percentage term (between 0 and 100)?', min=0, max=100 ) class Player(BasePlayer): role_set = models.StringField() bid_amount = models.IntegerField( label="Please enter your bid.", min=1, max=100 ) is_winner = models.BooleanField( initial=False ) select_distribution = models.StringField() item_private_value = models.IntegerField( initial=0 ) money_earned = models.FloatField(initial=0) # FUNCTIONS def creating_session(subsession: Subsession): # set the total rounds subsession.total_rounds, subsession.real_rounds = get_rounds(subsession) if subsession.round_number > subsession.total_rounds: return parameters = get_parameters(subsession) # specify the practice round subsession.is_practice_round = parameters['practice'] # define group switch subsession.switch_group = parameters['switch_group'] subsession.switch_type = parameters['switch_type'] # set group size, because the group size varies, we can not use the otree built-in method players = subsession.get_players() group_size = C.PLAYERS_PER_GROUP # set the grouping method subsession.group_randomly(fixed_id_in_group=True) # get distribution parameters subsession.p = parameters['p'] subsession.a = parameters['a'] subsession.b = parameters['b'] subsession.c = parameters['c'] subsession.d = parameters['d'] subsession.info = parameters['info'] for g in subsession.get_groups(): group_players = g.get_players() # set the id1 player in a group as auctioneer, and other players as bidders, only relevant if red_bid=True for p in group_players: if p.id_in_group == 1: p.role_set = 'auctioneer' else: p.role_set = 'bidder' # set the private value of the object for players random_prob = random.random() random_prob2 = random.random() if random_prob <= subsession.p: select_distribution = 'A' if random_prob2 <= 0.75: item_private_value = random.randint(subsession.a, subsession.b) else: item_private_value = random.randint(subsession.c, subsession.d) else: select_distribution = 'B' if random_prob2 <= 0.25: item_private_value = random.randint(subsession.a, subsession.b) else: item_private_value = random.randint(subsession.c, subsession.d) p.select_distribution = select_distribution p.item_private_value = item_private_value def get_parameters(subsession: Subsession): return parse_config(subsession.session.config['config_file'])[subsession.round_number - 1] def get_rounds(subsession: Subsession): # this function returns the total rounds and the total non-practice rounds rounds = parse_config(subsession.session.config['config_file']) total_rounds = len(rounds) practice_rounds = sum([rounds[i]['practice'] for i in range(len(rounds))]) real_rounds = total_rounds - practice_rounds return total_rounds, real_rounds def parse_config(config_file): with open('ambiguity_auction/configs/' + config_file) as f: rows = list(csv.DictReader(f)) rounds = [] for row in rows: rounds.append({ 'round_number': int(row['round_number']), 'switch_group': row['switch_group'] == 'Yes', 'switch_type': row['switch_type'] == 'Yes', 'p': float(row['p']), 'a': int(row['a']), 'b': int(row['b']), 'c': int(row['c']), 'd': int(row['d']), 'info': int(row['info']), 'practice': row['practice'] == 'Yes', }) return rounds def set_winner(group: Group): players = group.get_players() passed_bids = [p.bid_amount for p in players if p.id_in_group > 1 and p.bid_amount >= group.reserve_price] if len(passed_bids) > 0: group.highest_bid = max(passed_bids) players_with_highest_bid = [p for p in players if p.id_in_group > 1 and p.bid_amount == group.highest_bid] winner = random.choice( players_with_highest_bid ) # if there is a tie, winner is chosen at random winner.is_winner = True else: group.highest_bid = 0 for p in players: set_payoff(p) def set_payoff(player: Player): subsession = player.subsession if subsession.is_practice_round: player.payoff = 0 else: if player.role_set == 'auctioneer': player.payoff = player.group.highest_bid - player.group.info_purchase * player.subsession.info else: if player.is_winner: player.payoff = player.item_private_value - player.bid_amount else: player.payoff = 0 # PAGES class Welcome(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Introduction(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class SellerWaitPage(WaitPage): wait_for_all_groups = True class Seller(Page): #timeout_seconds = 60 @staticmethod def vars_for_template(player: Player): group = player.group return { 'probability_a': player.subsession.p*100, 'probability_b': 100-player.subsession.p*100 } @staticmethod def get_form_fields(player: Player): if player.role_set == 'bidder': return [] else: return ['info_purchase', 'info_will', 'guess_p'] form_model = 'group' class SellerInfoWaitPage(WaitPage): wait_for_all_groups = True class SellerInfo(Page): #timeout_seconds = 30 @staticmethod def vars_for_template(player: Player): group = player.group return { 'probability_a': player.subsession.p*100, 'probability_b': 100-player.subsession.p*100 } @staticmethod def get_form_fields(player: Player): if player.role_set == 'bidder': return [] else: return ['reserve_price'] form_model = 'group' class BidWaitPage(WaitPage): wait_for_all_groups = True class Bid(Page): #timeout_seconds = 60 @staticmethod def vars_for_template(player: Player): return { 'probability_a': player.subsession.p*100, 'probability_b': 100-player.subsession.p*100 } @staticmethod def get_form_fields(player: Player): if player.role_set == 'bidder': return ['bid_amount'] else: return [] form_model = 'player' class ResultsWaitPage(WaitPage): after_all_players_arrive = set_winner class Results(Page): timeout_seconds = 20 @staticmethod def vars_for_template(player: Player): group = player.group # player_id = player.id_in_group group_players = group.get_players() if player.role_set == 'auctioneer': current_round_payoff = player.group.highest_bid - player.group.info_purchase * player.subsession.info else: if player.is_winner: current_round_payoff = player.item_private_value - player.bid_amount else: current_round_payoff = 0 return dict(group_bids=[p.bid_amount for p in group_players if p.id_in_group > 1], group_size=len(group_players), current_round_payoff=current_round_payoff) @staticmethod def js_vars(player): group = player.group group_players = group.get_players() return dict( group_bids_js=[p.bid_amount for p in group_players if p.id_in_group > 1], ) class FinalPage(Page): @staticmethod def is_displayed(player: Player): last_round = player.subsession.total_rounds return player.round_number == last_round @staticmethod def vars_for_template(player: Player): participant = player.participant real_rounds = player.subsession.real_rounds participant.payoff = participant.payoff # adjust for payoff as the total payoff of non-practice rounds money_earned = participant.payoff_plus_participation_fee() player.money_earned = float(money_earned) return { 'cumulative_payoff': participant.payoff, 'money_earned': money_earned } # page sequence page_sequence = [Welcome, Introduction, SellerWaitPage, Seller, SellerInfoWaitPage, SellerInfo, BidWaitPage, Bid, ResultsWaitPage, Results, FinalPage]