from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random doc = """ Edited for EC312 Kent by Yidan Chai. The principal offers a contract to the agent, who can decide if to reject or accept. The agent then chooses an effort level. The implementation is based on Gaechter and Koenigstein (2006) . """ class Constants(BaseConstants): name_in_url = 'week5_principal_agent_2' players_per_group = 2 num_rounds = 3 instructions_template = 'week5_principal_agent_2/Instructions.html' # """wage = w""" min_fixed_payment = c(100) max_fixed_payment = c(1000) # # """desired effort level = de""" # min_desired_effort = c(1) # max_desired_effort = c(10) # """fine level = F""" min_fine_level = c(0) max_fine_level = c(1000) # """Amount principal gets if contract is rejected""" reject_principal_pay = c(0) reject_agent_pay = c(0) EFFORT_TO_COST = { 1: 1, 2: 10, 3: 20, 4: 40, 5: 60, 6: 80, 7: 100, 8: 130, 9: 160, 10: 200} def cost_from_effort(effort): return c(Constants.EFFORT_TO_COST[effort]) class Subsession(BaseSubsession): pass class Group(BaseGroup): total_return = models.CurrencyField( doc="""Total return from agent's effort = [Return for single unit of agent's work effort] * [Agent's work effort]""" ) agent_fixed_pay = models.CurrencyField( doc="""Amount offered as fixed pay to agent""", min=Constants.min_fixed_payment, max=Constants.max_fixed_payment, ) agent_return_share = models.IntegerField( choices=range(1, 10 + 1), widget=widgets.RadioSelectHorizontal, # choices=Constants.agent_return_share_choices, doc="""de""", ) agent_fine_level = models.CurrencyField( doc="""f""", min=Constants.min_fine_level, max=Constants.max_fine_level, ) agent_work_effort = models.IntegerField( choices=range(1, 10 + 1), doc="""Agent's work effort, [1, 10]""", widget=widgets.RadioSelectHorizontal, blank=True # if they reject, they don't have to submit work effort ) agent_work_cost = models.CurrencyField( doc="""Agent's cost of work effort""" ) contract_accepted = models.BooleanField( doc="""Whether agent accepts proposal""", widget=widgets.RadioSelect, choices=[ [True, 'Accept'], [False, 'Reject'], ] ) fined = models.BooleanField( initial=False, doc="""Indicates whether the player is fined""" ) ap1 = models.CurrencyField() pp1 = models.CurrencyField() ap2 = models.CurrencyField() pp2 = models.CurrencyField() def set_payoffs(self): principal = self.get_player_by_role('principal') agent = self.get_player_by_role('agent') if self.contract_accepted: self.agent_work_cost = cost_from_effort(self.agent_work_effort) ap1 = self.agent_fixed_pay - self.agent_work_cost pp1 = 200 * self.agent_work_effort - self.agent_fixed_pay ap2 = self.agent_fixed_pay - self.agent_work_cost - self.agent_fine_level pp2 = 200 * self.agent_work_effort - self.agent_fixed_pay + self.agent_fine_level if self.agent_work_effort >= self.agent_return_share: agent.payoff = ap1 principal.payoff = pp1 elif random.randint(0, 1) == 0: self.fined = True agent.payoff = ap2 principal.payoff = pp2 else: agent.payoff = ap1 principal.payoff = pp1 else: principal.payoff = Constants.reject_principal_pay agent.payoff = Constants.reject_agent_pay # def return_share_as_percentage(self): # return int(self.agent_return_share * 100) class Player(BasePlayer): def role(self): if self.id_in_group == 1: return 'principal' if self.id_in_group == 2: return 'agent'