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