from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) import math import random author = 'Nishtha' doc = """ Studying saving behavior during the retirement phase """ class Constants(BaseConstants): name_in_url = 'RST1' players_per_group = None num_rounds = 40 savings_start = 145350 sequence_length = 40 min_age = 62 ssb_benefits_period9 = 26238 min_consumption = 14887 interest_rate = 1.027 task_length = 40 task = 1 RandomList = random.sample(range(1, 100), 40) cutofflist = [96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 91, 91, 90, 89, 88, 87, 86, 85, 83, 82, 80, 79, 77, 75, 73, 71, 70, 68, 67, 0] difflist = [x1 - x2 for (x1, x2) in zip(cutofflist, RandomList)] class Subsession(BaseSubsession): paying_round = models.IntegerField() paying_task = models.IntegerField() cutoff = models.IntegerField() draw = models.IntegerField() def payingTask(self): A = self.in_round(1) if self.round_number == 1: self.paying_round = next(i for i, v in enumerate(Constants.difflist) if v < 0) + 1 print(self.paying_round) if random.random() < 0.5: self.paying_task = 1 else: self.paying_task = 2 else: self.paying_task = A.paying_task self.paying_round = A.paying_round self.cutoff = Constants.cutofflist[self.round_number - 1] self.draw = Constants.RandomList[self.round_number - 1] class Group(BaseGroup): pass class Player(BasePlayer): last_round = models.IntegerField() last_balance = models.FloatField() quiz1 = models.IntegerField() quiz2 = models.IntegerField() quiz3 = models.IntegerField() quiz4 = models.IntegerField() quiz5 = models.FloatField() quiz6 = models.FloatField() quiz7 = models.FloatField() quiz8 = models.IntegerField() quiz9 = models.BooleanField(label="", choices = [True, False], widget=widgets.RadioSelect) quiz10 = models.IntegerField(label="", choices = [[1, 'A. 94, 95, 96, 97, 98, 99, 100'], [2, 'B. 95, 96, 97, 98, 99, 100'], [3, 'C. 96, 97, 98, 99, 100'], [4, 'D. 1, 2,…, 94'], [5, 'E. 1, 2,…, 95']]) quiz11 = models.IntegerField(label="", choices=[[1, 'For those who have claimed Social Security benefits: Savings at the end of current period = Savings at the beginning of the current period + Social Security benefits - consumption'], [2, 'For those who have not yet claimed Social Security benefits: Savings at the end of current period = Savings at the beginning of the current period - consumption'], [3, 'Savings at the beginning of the next period = Savings at the end of current period * (1.027)'], [4, 'Savings at the beginning of the next period = Savings at the end of current period'] ], widget=widgets.RadioSelect ) quiz12 = models.BooleanField(label="", choices = [True, False], widget=widgets.RadioSelect) starting_balance = models.FloatField() ending_balance = models.FloatField() consumption = models.FloatField(min = Constants.min_consumption) ssb_choice = models.BooleanField(label="Do you want to claim benefits this period?", widget=widgets.RadioSelect) is_claimed = models.BooleanField() ssb_benefits_low_balance = models.FloatField() ssb_chosen = models.FloatField() is_balance_low = models.BooleanField() default_consumption = models.FloatField() max_consumption = models.FloatField() ssb = models.FloatField() intermediate_balance = models.FloatField() period_payoff = models.FloatField() cumulative_payoff = models.FloatField() task1_payoff = models.FloatField() def set_claim(self): if self.round_number>1: A = self.in_round(self.round_number-1) else: A = A = self.in_round(self.round_number) if self.round_number==1: self.is_claimed=0 elif self.round_number==2: self.is_claimed=A.ssb_choice elif A.is_claimed==1: self.is_claimed=1 else: self.is_claimed=A.ssb_choice def quiz1_error_message(self, value): if value != 40: return "That answer is incorrect. Try again." def quiz2_error_message(self, value): if value != 0: return "That answer is incorrect. Try again." def quiz3_error_message(self, value): if value != 16938: return "That answer is incorrect. Try again." def quiz4_error_message(self, value): if value != 16938: return "That answer is incorrect. Try again." def quiz5_error_message(self, value): if value < 0.02 - 0.005 or value > 0.02 + 0.005: return "That answer is incorrect. Try again." def quiz6_error_message(self, value): if value < 0.68 - 0.005 or value > 0.68 + 0.005: return "That answer is incorrect. Try again." def quiz7_error_message(self, value): if value < 1.19 - 0.005 or value > 1.19 + 0.005: return "That answer is incorrect. Try again." def quiz8_error_message(self, value): if value != 270: return "That answer is incorrect. Try again." def quiz9_error_message(self, value): if value == False: return "That answer is incorrect. Try again." def quiz10_error_message(self, value): if value != 3: return "That answer is incorrect. Try again." def quiz11_error_message(self, value): if value != 4: return "That answer is incorrect. Try again." def quiz12_error_message(self, value): if value == False: return "That answer is incorrect. Try again." def set_ssb_benefits(self): if self.round_number == 2: self.ssb_benefits_low_balance = 15865 elif self.round_number == 3: self.ssb_benefits_low_balance = 16938 elif self.round_number == 4: self.ssb_benefits_low_balance = 18117 elif self.round_number == 5: self.ssb_benefits_low_balance = 19418 elif self.round_number == 6: self.ssb_benefits_low_balance = 20858 elif self.round_number == 7: self.ssb_benefits_low_balance = 22457 elif self.round_number == 8: self.ssb_benefits_low_balance = 24241 else: self.ssb_benefits_low_balance = 26238 A1 = self.in_round(1) A2 = self.in_round(2) A3 = self.in_round(3) A4 = self.in_round(4) A5 = self.in_round(5) A6 = self.in_round(6) A7 = self.in_round(7) A8 = self.in_round(8) A9 = self.in_round(9) if A1.ssb_choice==1: self.ssb_chosen=14887 elif A2.ssb_choice==1: self.ssb_chosen = 15865 elif A3.ssb_choice==1: self.ssb_chosen = 16938 elif A4.ssb_choice==1: self.ssb_chosen = 18117 elif A5.ssb_choice==1: self.ssb_chosen = 19418 elif A6.ssb_choice==1: self.ssb_chosen = 20858 elif A7.ssb_choice==1: self.ssb_chosen = 22457 elif A8.ssb_choice==1: self.ssb_chosen = 24241 else: self.ssb_chosen = 0 def set_ssb(self): if self.round_number == 1: if self.ssb_choice == 1: self.ssb = 14887 else: self.ssb = 0 else: if self.is_claimed or self.in_round(self.round_number - 1).ssb > 0: self.ssb = self.in_round(self.round_number - 1).ssb elif self.ssb_choice == 1 and self.round_number == 2: self.ssb = 15865 elif self.ssb_choice == 1 and self.round_number == 3: self.ssb = 16938 elif self.ssb_choice == 1 and self.round_number == 4: self.ssb = 18117 elif self.ssb_choice == 1 and self.round_number == 5: self.ssb = 19418 elif self.ssb_choice == 1 and self.round_number == 6: self.ssb = 20858 elif self.ssb_choice == 1 and self.round_number == 7: self.ssb = 22457 elif self.ssb_choice == 1 and self.round_number == 8: self.ssb = 24241 elif self.is_balance_low: self.ssb = self.ssb_benefits_low_balance elif self.round_number == 9: self.ssb = Constants.ssb_benefits_period9 else: self.ssb = 0 def set_balances(self): if self.round_number > 1: A = self.in_round(self.round_number - 1) else: A = A = self.in_round(self.round_number) # Defining starting_balance if self.round_number == 1: self.starting_balance = Constants.savings_start else: self.starting_balance = A.ending_balance * Constants.interest_rate self.starting_balance = round(self.starting_balance, 2) if self.round_number == 1: self.last_balance = 0 else: self.last_balance = self.in_round(self.round_number - 1).ending_balance # If balance is low if self.starting_balance < 14887: self.is_balance_low = 1 else: self.is_balance_low = 0 def set_intermediate_balance(self): if self.round_number > 1: A = self.in_round(self.round_number - 1) else: A = A = self.in_round(self.round_number) # Defining default consumption if self.round_number == 1: self.default_consumption = Constants.min_consumption elif A.consumption < self.starting_balance + self.ssb: self.default_consumption = A.consumption else: self.default_consumption = self.starting_balance + self.ssb self.intermediate_balance = self.starting_balance + self.ssb def set_end_balance(self): self.ending_balance = self.starting_balance + self.ssb - self.consumption self.ending_balance = round(self.ending_balance, 2) # Defining payoff def set_payoffs(self): self.period_payoff = (1 - math.exp(-(self.consumption - 14887) / 20000)) * 3 self.period_payoff = round(self.period_payoff, 2) if self.round_number == 1: self.cumulative_payoff = self.period_payoff else: l = self.in_round(self.round_number - 1) self.cumulative_payoff = l.cumulative_payoff + self.period_payoff self.cumulative_payoff = round(self.cumulative_payoff, 2) self.payoff = 0 x = self.in_round(self.subsession.paying_round) self.task1_payoff = x.cumulative_payoff if Constants.task == self.subsession.paying_task: x.payoff = x.cumulative_payoff else: x.payoff = 0 @property def payoff(self): return self._payoff @payoff.setter def payoff(self, value): if value is None: value = 0 delta = value - self._payoff self._payoff += delta self.participant.payoff += delta # should save it because it may not be obvious that modifying # player.payoff also changes a field on a different model self.participant.save() if delta == 0: return print( f'Setting payoff to: {value}. player:\t{self._payoff}, participant:\t{self.participant.payoff}, participant.code:{self.participant.code} ({Constants.name_in_url}, round {self.round_number})')