import random from otree.api import * doc = """ Replica of the Thaler/Khaneman experiment where subjects are given a role (buyer or seller) and have to value an item in order to trade for it """ class C(BaseConstants): PLAYERS_PER_GROUP = None NUM_ROUNDS = 3 NAME_IN_URL = 'market_for_tokens' JACKPOT = cu(100) GUESS_MAX = 100 class Subsession(BaseSubsession): pass class Group(BaseGroup): two_thirds_avg = models.FloatField() best_guess = models.IntegerField() num_winners = models.IntegerField() current_market_price = models.FloatField() market_price_in_chosen_round = models.FloatField() winner_round = models.IntegerField() class Player(BasePlayer): guess = models.IntegerField( min=0, max=C.GUESS_MAX, label="Please pick a number from 0 to 100:" ) token_payoff = models.FloatField() coin_value = models.FloatField() is_winner = models.BooleanField(initial=False) has_item = models.BooleanField(initial=False, choices=[ [False, 'No, I did not receive a token.'], [True, 'Yes, I received a token!'], ], label="Please indicate whether you received a token:") won_in_chosen_round = models.BooleanField() has_item_new = models.BooleanField() coin_value_in_chosen_round = models.FloatField() had_item_in_chosen_round = models.BooleanField() difference_to_average = models.FloatField() payoff_in_chosen_round = models.FloatField() f25 = models.BooleanField(label="At a price of 0,25€...") f75 = models.BooleanField(label="At a price of 0,75€...") f125 = models.BooleanField(label="At a price of 1,25€...") f175 = models.BooleanField(label="At a price of 1,75€...") f225 = models.BooleanField(label="At a price of 2,25€...") f275 = models.BooleanField(label="At a price of 2,75€...") f325 = models.BooleanField(label="At a price of 3,25€...") f375 = models.BooleanField(label="At a price of 3,75€...") f425 = models.BooleanField(label="At a price of 4,25€...") f475 = models.BooleanField(label="At a price of 4,75€...") f525 = models.BooleanField(label="At a price of 5,25€...") f575 = models.BooleanField(label="At a price of 5,75€...") f625 = models.BooleanField(label="At a price of 6,25€...") f675 = models.BooleanField(label="At a price of 6,75€...") f725 = models.BooleanField(label="At a price of 7,25€...") f775 = models.BooleanField(label="At a price of 7,75€...") f825 = models.BooleanField(label="At a price of 8,25€...") f875 = models.BooleanField(label="At a price of 8,75€...") def getIsSet(self): return getattr(self, self.getWantedFormat(self.group.current_market_price)) # current market price has style x.xx, but i want fxxx def getWantedFormat(self, price: float): priceAsString = price*100 return f'f{int(priceAsString)}' def getIsSetFromWinnerRound(self): prev_player = self.in_round(self.group.winner_round) return getattr(prev_player, self.getWantedFormat(prev_player.group.current_market_price)) def f25_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f75_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f125_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f175_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f225_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f275_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f325_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f375_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f425_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f475_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f525_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f575_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f625_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f675_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f725_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f775_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f825_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices def f875_choices(player): choices = [ [False, f'I will not {"buy" if player.has_item == 0 else "sell"}.'], [True, f'I will {"buy" if player.has_item == 0 else "sell"}.'], ] return choices # FUNCTIONS def set_payoffs(group: Group): players = group.get_players() guesses = [p.guess for p in players] two_thirds_avg = (2 / 3) * sum(guesses) / len(players) group.two_thirds_avg = round(two_thirds_avg, 2) group.best_guess = min(guesses, key=lambda guess: abs(guess - group.two_thirds_avg)) #winners = [p for p in players if p.guess == group.best_guess] #for p in winners: # p.is_winner = True # p.payoff = C.JACKPOT / group.num_winners for player in players: getDifferenceToAverage(player, group.two_thirds_avg) player.difference_to_average = getDifferenceToAverage(player, group.two_thirds_avg) if player.has_item == 0: player.token_payoff = player.coin_value - player.group.current_market_price elif player.has_item == 1: if player.getIsSet() == 0: player.token_payoff = player.coin_value if player.getIsSet() == 1: player.token_payoff = player.group.current_market_price else: player.token_payoff = 0 # ToDo: determine whether everyone is a winner or not seller_winners = choose_winners_of_sellers(group) buyer_winners = choose_winners_of_buyers(group) for player in seller_winners: player.is_winner = True for player in buyer_winners: player.is_winner = True group.num_winners = len(seller_winners) + len(buyer_winners) def choose_winner_round(): return random.randint(1,3) def choose_winners_of_sellers(group: Group): average = group.two_thirds_avg players = group.get_players() amount_of_winners = 3 all_sellers_unsorted = [p for p in players if p.has_item == 1] all_sellers_sorted = sorted(all_sellers_unsorted, key=lambda x: x.difference_to_average, reverse=False) winners = all_sellers_sorted[:3] return winners def choose_winners_of_buyers(group: Group): average = group.two_thirds_avg players = group.get_players() amount_of_winners = 3 all_sellers_unsorted = [p for p in players if p.has_item == 0] all_sellers_sorted = sorted(all_sellers_unsorted, key=lambda x: x.difference_to_average, reverse=False) winners = all_sellers_sorted[:3] return winners def getDifferenceToAverage(player: Player, average: float): return abs(player.guess - average) def two_thirds_avg_history(group: Group): return [g.two_thirds_avg for g in group.in_previous_rounds()] # PAGES class instructions(Page): @staticmethod def vars_for_template(player: Player): return dict(tokenvalue=player.coin_value) class Introduction(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class InitialWaitingRoom(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class StartExperimentWaitingRoom(WaitPage): pass class AfterValueSetWaitingRoom(WaitPage): pass class SetValue(Page): form_model = 'player' form_fields = ['f25', 'f75', 'f125', 'f175', 'f225', 'f275', 'f325', 'f375', 'f425', 'f475', 'f525', 'f575', 'f625', 'f675', 'f725', 'f775', 'f825', 'f875'] @staticmethod def vars_for_template(player: Player): return dict( choicetrue='I will {}'.format("buy" if player.has_item == 0 else "sell"), choicefalse='I will not {}'.format("buy" if player.has_item == 0 else "sell") ) class OwnerOrChooser(Page): form_model = 'player' form_fields = ['has_item'] @staticmethod def getValueFromPot(): all_market_prices = [0.25, 0.75, 1.25, 1.75, 2.25, 2.75, 3.25, 3.75, 4.25, 4.75, 5.25, 5.75, 6.25, 6.75, 7.25, 7.75, 8.25, 8.75] pot = [0.25, 0.75, 1.25, 1.75, 2.25, 2.75, 3.25, 3.75, 4.25, 4.75, 5.25, 5.75, 6.25, 6.75, 7.25, 7.75, 8.25, 8.75] if len(pot) > 0: return pot.pop(random.randint(0, len(pot)-1)) else: pot = all_market_prices.copy() return pot.pop(random.randint(0, len(pot)-1)) @staticmethod def before_next_page(player, timeout_happened): player.coin_value = OwnerOrChooser.getValueFromPot() all_market_prices = [0.25, 0.75, 1.25, 1.75, 2.25, 2.75, 3.25, 3.75, 4.25, 4.75, 5.25, 5.75, 6.25, 6.75, 7.25, 7.75, 8.25, 8.75] player.group.current_market_price = random.choice(all_market_prices) player.has_item_new = 0 class ResultsWaitPage(WaitPage): after_all_players_arrive = set_payoffs class Guess(Page): form_model = 'player' form_fields = ['guess'] class Results(Page): @staticmethod def vars_for_template(player: Player): group = player.group sorted_guesses = sorted(p.guess for p in group.get_players()) return dict(sorted_guesses=sorted_guesses) class WaitingRoomAfterGuess(Page): @staticmethod def is_displayed(player: Player): return player.round_number < 3 class DisplayChosenRoundResults(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 3 @staticmethod def vars_for_template(player: Player): if player.round_number == C.NUM_ROUNDS: winner_round = choose_winner_round() player.group.winner_round = winner_round prev_player = player.in_round(winner_round) player.won_in_chosen_round = prev_player.is_winner player.coin_value_in_chosen_round = prev_player.coin_value player.had_item_in_chosen_round = prev_player.has_item player.payoff_in_chosen_round = prev_player.token_payoff group = player.group winner_group = prev_player.group group.market_price_in_chosen_round = winner_group.current_market_price return dict(winner_round=winner_round) # Sequence: Anfangswarteraum, Besitzer oder Wähler, Wert Setzen, Warteraum mit Erklärung, Instruction für Beauty Contest, Beauty Contest, Ergebnis, Trade page_sequence = [StartExperimentWaitingRoom, InitialWaitingRoom, OwnerOrChooser, SetValue, AfterValueSetWaitingRoom, Introduction, Guess, ResultsWaitPage, Results, WaitingRoomAfterGuess,DisplayChosenRoundResults]