from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) from mpl.config import * import random from random import randrange author = 'Felix Holzmeister' doc = """ Multiple price list task as proposed by Holt/Laury (2002), American Economic Review 92(5). """ # from otree.api import Currency as c from otree.constants import BaseConstants # # ******************************************************************************************************************** # # *** CLASS CONSTANTS *** # # ******************************************************************************************************************** # class Constants(BaseConstants): # ---------------------------------------------------------------------------------------------------------------- # # --- Task-specific Settings --- # # ---------------------------------------------------------------------------------------------------------------- # # lottery payoffs # "high" and "low" outcomes (in currency units set in settings.py) of "lottery A" and "lottery B" # note that payoffs are identical for all choices and only probabilities of "high" and "low" outcomes change lottery_a_hi = 2.00 lottery_a_lo = 1.60 lottery_b_hi = 3.85 lottery_b_lo = 0.10 # number of binary choices between "lottery A" and "lottery B" # note that the number of choices determines the probabilities of high and low outcomes of lotteries "A" and "B" # for , the probability of outcome "high" is 1/X for the first choice, 2/X for the second, etc. num_choices = [11,11] # include 'certain' choice (** only applies if **) # if , the binary choice with probability of the outcome "high" being equal to 1 is included # if , the list only contains ( - 1) binary decision pairs # note, however, that the probability of outcome "high" is set by , not ( - 1), though # i.e., if , the last choice implies a probability of (X - 1)/X (given ) certain_choice = True # ---------------------------------------------------------------------------------------------------------------- # # --- Overall Settings and Appearance --- # # ---------------------------------------------------------------------------------------------------------------- # # show each lottery pair on a separate page # if , each single binary choice between lottery "A" and "B" is shown on a separate page # if , all choices are displayed in a table on one page one_choice_per_page = False # order choices between lottery pairs randomly # if , the ordering of binary decisions is randomized for display # if , binary choices are listed in ascending order of the probability of the "high" outcome random_order = False # enforce consistency, i.e. only allow for a single switching point # if , all options "A" above a selected option "A" are automatically selected # similarly, all options "B" below a selected option "B" are automatically checked, implying consistent choices # note that is only implemented if and enforce_consistency = False # depict probabilities as percentage numbers # if , the probability of outcome "high" will be displayed as percentage number # if , the probabilities will be displayed as fractions, i.e. "1/X", "2/X", etc. percentage = False # show small pie charts for each lottery # if , a pie chart depicting the probabilities of outcomes is rendered next to each lottery # if , no graphical representation of probabilities is displayed small_pies = True # display lotteries in terms of large pie charts # if , lotteries are depicted as pie charts; if lotteries are list items # note that only affects the task's appearance if large_pies = True # show progress bar # if and , a progress bar is rendered # if , no information with respect to the advance within the task is displayed # the progress bar graphically depicts the advance within the task in terms of how many decision have been made # further, information in terms of "page x out of " (with x denoting the current choice) is provided progress_bar = True # show instructions page # if , a separate template "Instructions.html" is rendered prior to the task # if , the task starts immediately (e.g. in case of printed instructions) instructions = True # show results page summarizing the task's outcome including payoff information # if , a separate page containing all relevant information is displayed after finishing the task # if , the template "Decision.html" will not be rendered results = True # ---------------------------------------------------------------------------------------------------------------- # # --- oTree Settings (Don't Modify) --- # # ---------------------------------------------------------------------------------------------------------------- # name_in_url = 'mpl' players_per_group = 2 # Defines how many players there are in the group in this subsession or game if one_choice_per_page: if certain_choice: num_rounds = num_choices[1] else: num_rounds = num_choices[1] - 1 else: num_rounds = 1 # ******************************************************************************************************************** # # *** CLASS SUBSESSION # ******************************************************************************************************************** # class Subsession(BaseSubsession): def creating_session(self): if self.round_number == 1: n = Constants.num_choices[1] for p in self.get_players(): # create list of lottery indices # ---------------------------------------------------------------------------------------------------- indices = [j for j in range(1, n)] indices.append(n) if Constants.certain_choice else None # create list of probabilities # ---------------------------------------------------------------------------------------------------- if Constants.percentage: probabilities = [ "{0:.2f}".format(k / n * 100) + "%" for k in indices ] else: probabilities = [ str(k) + "/" + str(n) for k in indices ] # create list corresponding to form_field variables including all choices # ---------------------------------------------------------------------------------------------------- form_fields = ['choice_' + str(k) for k in indices] # create list of choices # ---------------------------------------------------------------------------------------------------- p.participant.vars['mpl_choices'] = list( zip(indices, form_fields, probabilities) ) # randomly determine index/choice of binary decision to pay # ---------------------------------------------------------------------------------------------------- p.participant.vars['mpl_index_to_pay'] = random.choice(indices) p.participant.vars['mpl_choice_to_pay'] = 'choice_' + str(p.participant.vars['mpl_index_to_pay']) # randomize order of lotteries if # ---------------------------------------------------------------------------------------------------- if Constants.random_order: random.shuffle(p.participant.vars['mpl_choices']) # initiate list for choices made # ---------------------------------------------------------------------------------------------------- p.participant.vars['mpl_choices_made'] = [None for j in range(1, n + 1)] # generate random switching point for PlayerBot in tests.py # -------------------------------------------------------------------------------------------------------- for participant in self.session.get_participants(): participant.vars['mpl_switching_point'] = random.randint(1, n) # ******************************************************************************************************************** # # *** CLASS GROUP # ******************************************************************************************************************** # class Group(BaseGroup): pass # ******************************************************************************************************************** # # *** CLASS PLAYER # ******************************************************************************************************************** # class Player(BasePlayer): # add model fields to class player # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: if Constants.certain_choice: for j in range(1, Constants.num_choices[1] + 1): locals()['choice_' + str(j)] = models.StringField() del j else: for j in range(1, Constants.num_choices[1]): locals()['choice_' + str(j)] = models.StringField() del j random_draw = models.IntegerField() choice_to_pay = models.StringField() option_to_pay = models.StringField() inconsistent = models.IntegerField() switching_row = models.IntegerField() # set player's payoff # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: def set_payoffs(self): # random draw to determine whether to pay the "high" or "low" outcome of the randomly picked lottery # ------------------------------------------------------------------------------------------------------------ self.random_draw = randrange(1, len(self.participant.vars['mpl_choices'])) # set to participant.var['choice_to_pay'] determined creating_session # ------------------------------------------------------------------------------------------------------------ self.choice_to_pay = self.participant.vars['mpl_choice_to_pay'] # elicit whether lottery "A" or "B" was chosen for the respective choice # ------------------------------------------------------------------------------------------------------------ self.option_to_pay = getattr(self, self.choice_to_pay) # set player's payoff # ------------------------------------------------------------------------------------------------------------ if self.option_to_pay == 'A': if self.random_draw <= self.participant.vars['mpl_index_to_pay']: self.payoff = Constants.lottery_a_hi else: self.payoff = Constants.lottery_a_lo else: if self.random_draw <= self.participant.vars['mpl_index_to_pay']: self.payoff = Constants.lottery_b_hi else: self.payoff = Constants.lottery_b_lo # set payoff as global variable # ------------------------------------------------------------------------------------------------------------ self.participant.vars['mpl_payoff'] = self.payoff # determine consistency # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: def set_consistency(self): n = Constants.num_choices[1] # replace A's by 1's and B's by 0's self.participant.vars['mpl_choices_made'] = [ 1 if j == 'A' else 0 for j in self.participant.vars['mpl_choices_made'] ] # check for multiple switching behavior for j in range(1, n): choices = self.participant.vars['mpl_choices_made'] self.inconsistent = 1 if choices[j] > choices[j - 1] else 0 if self.inconsistent == 1: break # determine switching row # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: def set_switching_row(self): # set switching point to row number of first 'B' choice if self.inconsistent == 0: self.switching_row = sum(self.participant.vars['mpl_choices_made']) + 1