from otree.api import * import random import time c = Currency doc = """ This is a compensation game with 4 players. """ class Constants(BaseConstants): name_in_url = 'compensation_random' players_per_group = 4 num_rounds = 10 funds = 40 endowment = 20 fine_rate = 2 audit_cost = 10 total_audits = 1 internal_audit = 1 # decide whether we have internal audit rule# # ---------------------------------------------------------------------------------------------------------------- # # --- Task-specific Settings --- # # ---------------------------------------------------------------------------------------------------------------- # # number (N) of lotteries with num_lotteries = 5 # sure_payoff; "low" and "high" payoff of the initial lottery (in currency units set in settings.py) # determines the 'starting point' for all lotteries in the single choice list (safe option in i = 1) # outcomes of subsequent lotteries are defined relative to the 'initial value' by the options below sure_payoff = 6 # increments of lottery outcomes (refer to the documentation for more detailed information) # defines the (negative) increment of the low outcome over the number of choices () # similarly, defines the (positive) increment of the high outcome for the choices delta_lo = 1 delta_hi = 2 # probability of lottery outcome "high" (in percent) # refers to the likelihood of outcome denoted in percent (as Integer or Float) # i.e. implies an x%-chance to win the "high" and a (1-x)%-chance to win the "low" payoff # note that the probability in this task is constant for each of the and only outcomes change probability = 50 # additional lottery to separate risk-loving from risk-neutral preferences # if , a lottery with the same expected value but a higher st. dev. as lottery is added # note that implies that the overall number of lotteries rendered will be + 1 risk_loving = True # ---------------------------------------------------------------------------------------------------------------- # # --- Overall Settings and Appearance --- # # ---------------------------------------------------------------------------------------------------------------- # # 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 # 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 class Subsession(BaseSubsession): pass class Group(BaseGroup): checked_person = models.IntegerField() random_round = models.FloatField() p1damage = models.IntegerField() p2damage = models.IntegerField() p3damage = models.IntegerField() p4damage = models.IntegerField() p1report_damage = models.IntegerField() p2report_damage = models.IntegerField() p3report_damage = models.IntegerField() p4report_damage = models.IntegerField() sumreport_p1 = models.IntegerField() sumreport_p2 = models.IntegerField() sumreport_p3 = models.IntegerField() sumreport_p4 = models.IntegerField() total_audit_cost = models.IntegerField() remain_fund = models.IntegerField() class Player(BasePlayer): internal_audit = models.IntegerField() income = models.FloatField(initial=0) caught = models.IntegerField() random_round = models.FloatField() report_damage = models.IntegerField(label="请输入你的上报金额", max=Constants.endowment, min=0) damage = models.IntegerField() after_endowment = models.IntegerField() confirmed_damage = models.IntegerField() received_compensation = models.FloatField() payoff1 = models.FloatField() report_p1 = models.IntegerField(choices=([1, "举报"], [0, '不举报']), initial=-1, label = "请选择是否举报受试1") report_p2 = models.IntegerField(choices=([1, "举报"], [0, '不举报']), initial=-1, label = "请选择是否举报受试2") report_p3 = models.IntegerField(choices=([1, "举报"], [0, '不举报']), initial=-1, label = "请选择是否举报受试3") report_p4 = models.IntegerField(choices=([1, "举报"], [0, '不举报']), initial=-1, label = "请选择是否举报受试4") check_p1 = models.IntegerField() check_p2 = models.IntegerField() check_p3 = models.IntegerField() check_p4 = models.IntegerField() income_p1 = models.FloatField() income_p2 = models.FloatField() income_p3 = models.FloatField() income_p4 = models.FloatField() lottery_choice = models.IntegerField() outcome_to_pay = models.StringField() outcome_lo = models.CurrencyField() outcome_hi = models.CurrencyField() prob_hi = models.FloatField() prob_lo = models.FloatField() Selectedlottery = models.CurrencyField() payoffscl = models.CurrencyField() dis_payoff = models.FloatField() s1 = models.StringField( label='Q1. 一个球拍和一个球总共卖 1.10 元,球拍比球多卖 1.00 元,请问球的价格是多少(单位:分)?' ) s2 = models.StringField( label='Q2. 如果 5 个机器耗时 5 分钟制作了 5 个装饰品,请问 100 个机器制作 100 个装饰品要耗时多久(单位:分钟)?' ) s3 = models.StringField( label='Q3. 湖面上有一片睡莲,它们每天以两倍的数量增加着。如果睡莲铺满整个湖面需要 48 天,那么它覆盖湖面的一半区域需要几天?' ) s4 = models.StringField( label='Q4. 假设您投掷一枚公平的硬币 1000 次,您认为有多少次这枚硬币会反面朝上?' ) s5 = models.StringField( label='Q5. 小镇上的1000人中有500人是合唱团成员。500名合唱团成员中有100名是男性,500名不是合唱团的居民中有300名是男性。请问随机抽取一名男性是合唱团成员的概率有多大?(请用百分率的形式表示)' ) s6 = models.StringField( choices=[['是', '是'], ['否', '否']], label='Q6. 您是否曾经看到过或者回答过上述问题?', widget=widgets.RadioSelect, ) s7 = models.StringField( choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], label='Q7. 请评价自己:您是愿意承担风险的人还是试图规避风险的人?(1代表着完全不愿意承担风险,10代表着非常愿意承担风险))', widget=widgets.RadioSelect, ) s8 = models.StringField( choices=[['1050或以下', '1050或以下'], ['1100', '1100'], ['1150', '1150'], ['1200', '1200'], ['1250', '1250'], ['1300', '1300'], ['1350', '1350'], ['1400', '1400'], ['1450', '1450'], ['1500或以上', '1500或以上']], label='Q8. 对您来说现在的1000元相当于一年后的多少钱?', widget=widgets.RadioSelect, ) s9 = models.FloatField( min=0, max=1000, label='Q9. 如果您有1000元,您愿意对一个利率为0.9%,违约率为1%,违约后收益率为0%的债券投资多少?' ) s10 = models.FloatField( min=0, max=1000, label='Q10. 如果您有1000元,您愿意对一个利率为1.6%,违约率为5%,违约后收益率为-10%的债券投资多少?' ) s11 = models.FloatField( min=0, max=1000, label='Q11. 如果您有1000元,您愿意对一个利率为2.5%,违约率为10%,违约后收益率为-25%的债券投资多少?' ) s12 = models.StringField( label='Q12. 请问您的年龄是?' ) s13 = models.StringField( choices=[['男性', '男性'], ['女性', '女性'], ['其他', '其他']], label='Q13. 请问您的性别是?', widget=widgets.RadioSelect, ) s13_1 = models.StringField( label='如果选择“其他”,请具体说明:', blank=True ) s14 = models.StringField( choices=[['是', '是'], ['否', '否']], label='Q13. 请问您是否已婚?', widget=widgets.RadioSelect, ) s15 = models.IntegerField( min=0, max=10, label='Q12. 请问您有几个孩子?' ) s16 = models.StringField( choices=[ ['会计', '会计'], ['经济', '经济'], ['金融', '金融'], [ '工商管理(除会计、经济、金融的其他商科专业)', '工商管理(除会计、经济、金融的其他商科专业)'], ['教育', '教育'], ['工程学', '工程学'], ['医疗卫生', '医疗卫生'], ['社会科学或历史学', '社会科学或历史学'], [ '数学,计算机或物理学', '数学,计算机或物理学'], ['生物学', '生物学'], ['人文科学', '人文科学'], ['公共事务管理或社会服务类', '公共事务管理或社会服务类'], ['心理学', '心理学'], [ '其他(如果您不是学生,请选择“其他”并说明您的职业)', '其他(如果您不是学生,请选择“其他”并说明您的职业)'] ], label='Q14. 请问您的专业是?', widget=widgets.RadioSelect, ) s16_1 = models.StringField( label='如果选择“其他”,请具体说明:', blank=True ) s17 = models.StringField( choices=[['本科', '本科'], ['硕士', '硕士'], ['博士', '博士'], ['不适用', '不适用']], label='Q15. 请问您的学历层次是?', widget=widgets.RadioSelect, ) s18 = models.StringField( choices=[['少于10,000元', '少于10,000元'], ['10,000-15,000元', '10,000-15,000元'], ['15,000-20,000元', '15,000-20,000元'], ['20,000-30,000元', '20,000-30,000元'], ['30,000-40,000元', '30,000-40,000元'], ['40,000-50,000元', '40,000-50,000元'], ['50,000元及以上', '50,000元及以上']], label='Q16. 请问您的总家庭月收入是?', widget=widgets.RadioSelect, ) s19 = models.FloatField( min=0, max=50, label='Q17. 您在金融行业工作多少年了?' ) s20 = models.StringField( label='Q18. 您当前从事的职业是?' ) s21 = models.StringField( label='Q19. 您考取过哪些金融领域相关的证书?' ) s22 = models.StringField( choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], label='Q20. 您认为我国债券市场评级准确么?(1代表着完全不准确,10代表着十分准确))', widget=widgets.RadioSelect, ) s23 = models.StringField( choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], label='Q21. 您认为我国债券市场存在评级虚高现象么?(1代表着完全不存在,10代表着存在且很普遍))', widget=widgets.RadioSelect, ) s24 = models.StringField( choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], label='Q20. 您认为本次实验中评级准确么?(1代表着完全不准确,10代表着十分准确))', widget=widgets.RadioSelect, ) s25 = models.StringField( choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], label='Q21. 您认为本次实验中存在评级虚高现象么?(1代表着完全不存在,10代表着存在且很普遍))', widget=widgets.RadioSelect, ) # 通过违约概率计算实际利率 def creating_session(subsession): subsession.group_randomly(fixed_id_in_group=False) for p in subsession.get_players(): if p.round_number <= Constants.num_rounds/2: p.internal_audit = 0 if p.round_number > Constants.num_rounds/2: p.internal_audit = 1 n = Constants.num_lotteries # create list of lottery indices # -------------------------------------------------------------------------------------------------------- indices = [j for j in range(1, n + 1)] # create list of low and high outcomes (matched by index) # -------------------------------------------------------------------------------------------------------- outcomes_lo = [c(Constants.sure_payoff - Constants.delta_lo * j) for j in range(0, n)] outcomes_hi = [c(Constants.sure_payoff + Constants.delta_hi * j) for j in range(0, n)] choice_num = [j + 1 for j in range(0, n)] prob_lo = [c(Constants.probability) for j in range(0, n)] prob_hi = [c(Constants.sure_payoff) for j in range(0, n)] # append indices and outcomes by "risk loving" lottery if # -------------------------------------------------------------------------------------------------------- if Constants.risk_loving: indices.append(n + 1) outcomes_lo.append(c(outcomes_lo[-1] - Constants.delta_hi)) outcomes_hi.append(c(outcomes_hi[-1] + Constants.delta_hi)) # create list of lotteries # -------------------------------------------------------------------------------------------------------- p.participant.vars['scl_lotteries'] = list( zip(indices, outcomes_lo, outcomes_hi) ) # randomize order of lotteries if # -------------------------------------------------------------------------------------------------------- if Constants.random_order: random.shuffle( p.participant.vars['scl_lotteries'] ) def intro(group: Group): n = Constants.num_lotteries # create list of lottery indices # -------------------------------------------------------------------------------------------------------- indices = [j for j in range(1, n + 1)] # create list of low and high outcomes (matched by index) # -------------------------------------------------------------------------------------------------------- outcomes_lo = [c(Constants.sure_payoff - Constants.delta_lo * j) for j in range(0, n)] outcomes_hi = [c(Constants.sure_payoff + Constants.delta_hi * j) for j in range(0, n)] prob_lo = [c(Constants.probability) for j in range(0, n)] prob_hi = [c(Constants.sure_payoff) for j in range(0, n)] if Constants.risk_loving: indices.append(n + 1) outcomes_lo.append(c(outcomes_lo[-1] - Constants.delta_hi)) outcomes_hi.append(c(outcomes_hi[-1] + Constants.delta_hi)) for player in group.get_players(): if player.round_number == Constants.num_rounds: player.participant.vars['scl_lotteries'] = list( zip(indices, outcomes_lo, outcomes_hi) ) p = Constants.probability rnd = random.randint(1, 100) player.outcome_to_pay = "high" if rnd <= p else "low" # select lottery choice out of list of lotteries # ------------------------------------------------------------------------------------------------------------ lottery_selected = [i for i in player.participant.vars['scl_lotteries'] if i[0] == player.in_round(Constants.num_rounds).lottery_choice] lottery_selected = lottery_selected[0] # store payoffs of chosen lottery in the model # ------------------------------------------------------------------------------------------------------------ player.outcome_lo = lottery_selected[1] player.outcome_hi = lottery_selected[2] # set player's payoff # ------------------------------------------------------------------------------------------------------------ if player.outcome_to_pay == "high": player.payoffscl = player.outcome_hi else: player.payoffscl = player.outcome_lo player.dis_payoff = float(player.payoff) + float(player.payoffscl) # 概率 def damage_and_check(group: Group): p1 = group.get_player_by_id(1) if group.round_number == 1: group.random_round = round(random.random() * (Constants.num_rounds - 1), 0) + 1 else: group.random_round = group.in_round(1).random_round for p in group.get_players(): p.damage = round( Constants.endowment * 0.25) p.after_endowment = Constants.endowment - p.damage if p1.internal_audit == 0: group.checked_person = round(random.random() * 3) + 1 group.p1damage = group.get_player_by_id(1).damage group.p2damage = group.get_player_by_id(2).damage group.p3damage = group.get_player_by_id(3).damage group.p4damage = group.get_player_by_id(4).damage group.total_audit_cost = Constants.audit_cost*Constants.total_audits group.remain_fund = Constants.funds-group.total_audit_cost def record_report(group: Group): group.p1report_damage = group.get_player_by_id(1).report_damage group.p2report_damage = group.get_player_by_id(2).report_damage group.p3report_damage = group.get_player_by_id(3).report_damage group.p4report_damage = group.get_player_by_id(4).report_damage def after_audit_income(group: Group): players = group.get_players() p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) p3 = group.get_player_by_id(3) p4 = group.get_player_by_id(4) for p in group.get_players(): if p.id_in_group != group.checked_person: p.confirmed_damage = p.report_damage p.caught = 0 if p.id_in_group == group.checked_person and p.report_damage <= p.damage: p.confirmed_damage = p.report_damage p.caught = 1 if p.id_in_group == group.checked_person and p.report_damage > p.damage: p.confirmed_damage = 0 p.caught = 1 for p in group.get_players(): p.check_p1 = p1.caught p.check_p2 = p2.caught p.check_p3 = p3.caught p.check_p4 = p4.caught total_confirm = sum(p.confirmed_damage for p in players) for p in group.get_players(): p.received_compensation = round((p.confirmed_damage / total_confirm) * ( Constants.funds - Constants.total_audits * Constants.audit_cost), 2) p.income = Constants.endowment - p.damage + p.received_compensation if p.round_number == Constants.num_rounds: p.payoff = p.in_round(group.random_round).income for p in group.get_players(): p.income_p1 = p1.income p.income_p2 = p2.income p.income_p3 = p3.income p.income_p4 = p4.income def internal_audit_income(group: Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) p3 = group.get_player_by_id(3) p4 = group.get_player_by_id(4) players = group.get_players() for p in group.get_players(): group.sumreport_p1 = p1.report_p1 + p2.report_p1 + p3.report_p1 + p4.report_p1 group.sumreport_p2 = p1.report_p2 + p2.report_p2 + p3.report_p2 + p4.report_p2 group.sumreport_p3 = p1.report_p3 + p2.report_p3 + p3.report_p3 + p4.report_p3 group.sumreport_p4 = p1.report_p4 + p2.report_p4 + p3.report_p4 + p4.report_p4 if group.sumreport_p1 == -1: p1.caught = 0 p1.confirmed_damage = p1.report_damage if group.sumreport_p1 > -1 and p1.report_damage <= p1.damage: p1.caught = 1 p1.confirmed_damage = p1.report_damage if group.sumreport_p1 > -1 and p1.report_damage > p1.damage: p1.caught = 1 p1.confirmed_damage = 0 if group.sumreport_p2 == -1: p2.caught = 0 p2.confirmed_damage = p2.report_damage if group.sumreport_p2 > -1 and p2.report_damage <= p2.damage: p2.caught = 1 p2.confirmed_damage = p2.report_damage if group.sumreport_p2 > -1 and p2.report_damage > p2.damage: p2.caught = 1 p2.confirmed_damage = 0 if group.sumreport_p3 == -1: p3.caught = 0 p3.confirmed_damage = p3.report_damage if group.sumreport_p3 > -1 and p3.report_damage <= p3.damage: p3.caught = 1 p3.confirmed_damage = p3.report_damage if group.sumreport_p3 > -1 and p3.report_damage > p3.damage: p3.caught = 1 p3.confirmed_damage = 0 if group.sumreport_p4 == -1: p4.caught = 0 p4.confirmed_damage = p4.report_damage if group.sumreport_p4 > -1 and p4.report_damage <= p4.damage: p4.caught = 1 p4.confirmed_damage = p4.report_damage if group.sumreport_p4 > -1 and p4.report_damage > p4.damage: p4.caught = 1 p4.confirmed_damage = 0 for p in group.get_players(): p.check_p1 = p1.caught p.check_p2 = p2.caught p.check_p3 = p3.caught p.check_p4 = p4.caught total_confirm = sum(p.confirmed_damage for p in players) total_audit = sum(p.caught for p in players) if total_confirm == 0: total_confirm = 1 for p in group.get_players(): p.received_compensation = round((p.confirmed_damage / total_confirm) * ( Constants.funds - total_audit * Constants.audit_cost), 2) p.income = Constants.endowment - p.damage + p.received_compensation if p.round_number == Constants.num_rounds: p.payoff = p.in_round(group.random_round).income group.total_audit_cost = total_audit * Constants.audit_cost group.remain_fund = Constants.funds-group.total_audit_cost for p in group.get_players(): p.income_p1 = p1.income p.income_p2 = p2.income p.income_p3 = p3.income p.income_p4 = p4.income class Introduction(Page): def is_displayed(player): return player.round_number == 1 class Introduction2(Page): def is_displayed(player): return player.round_number == Constants.num_rounds/2+1 class Role(Page): pass class WaitPage1(WaitPage): after_all_players_arrive = damage_and_check class Receive_damage_and_report(Page): form_model = 'player' form_fields = ['report_damage'] class WaitPage2(WaitPage): after_all_players_arrive = record_report class Report_other1(Page): form_model = 'player' form_fields = ['report_p2', 'report_p3', 'report_p4'] def is_displayed(player:Player): return player.internal_audit == 1 and player.id_in_group == 1 class Report_other2(Page): form_model = 'player' form_fields = ['report_p1', 'report_p3', 'report_p4'] def is_displayed(player:Player): return player.internal_audit == 1 and player.id_in_group == 2 class Report_other3(Page): form_model = 'player' form_fields = ['report_p1', 'report_p2', 'report_p4'] def is_displayed(player:Player): return player.internal_audit == 1 and player.id_in_group == 3 class Report_other4(Page): form_model = 'player' form_fields = ['report_p1', 'report_p2', 'report_p3'] def is_displayed(player:Player): return player.internal_audit == 1 and player.id_in_group == 4 class WaitPage3(WaitPage): def is_displayed(player: Player): return player.internal_audit == 0 after_all_players_arrive = after_audit_income class WaitPage4(WaitPage): def is_displayed(player: Player): return player.internal_audit == 1 after_all_players_arrive = internal_audit_income class ResultPage(Page): pass class Decision1(Page): # form model # ---------------------------------------------------------------------------------------------------------------- form_model = 'player' # form fields # ---------------------------------------------------------------------------------------------------------------- form_fields = ['lottery_choice'] # variables for template # ---------------------------------------------------------------------------------------------------------------- def is_displayed(player): return player.round_number == Constants.num_rounds def vars_for_template(self): return { 'n': Constants.num_lotteries, 'lotteries': self.participant.vars['scl_lotteries'], 'prob_hi': "{0:.1f}".format(Constants.probability) + "%", 'prob_lo': "{0:.1f}".format(100 - Constants.probability) + "%" } class Survey1(Page): def is_displayed(player): return player.round_number == Constants.num_rounds class Survey2(Page): form_model = 'player' form_fields = ['s1', 's2', 's3', 's4', 's5', 's6'] def is_displayed(player): return player.round_number == Constants.num_rounds class Survey3(Page): form_model = 'player' form_fields = ['s7', 's8', 's9', 's10', 's11'] def is_displayed(player): return player.round_number == Constants.num_rounds class Survey4(Page): form_model = 'player' form_fields = ['s12', 's13', 's13_1', 's14', 's15', 's16', 's16_1', 's17', 's18', 's19', 's20', 's21', 's22', 's23', 's24', 's25'] def is_displayed(player): return player.round_number == Constants.num_rounds class Waitforintro(WaitPage): after_all_players_arrive = intro body_text = "请耐心等待实验继续" def is_displayed(player): return player.round_number == Constants.num_rounds class Decision1(Page): # form model # ---------------------------------------------------------------------------------------------------------------- form_model = 'player' # form fields # ---------------------------------------------------------------------------------------------------------------- form_fields = ['lottery_choice'] # variables for template # ---------------------------------------------------------------------------------------------------------------- def is_displayed(player): return player.round_number == Constants.num_rounds def vars_for_template(self): return { 'n': Constants.num_lotteries, 'lotteries': self.participant.vars['scl_lotteries'], 'prob_hi': "{0:.1f}".format(Constants.probability) + "%", 'prob_lo': "{0:.1f}".format(100 - Constants.probability) + "%" } class final_results(Page): def is_displayed(player): return player.round_number == Constants.num_rounds page_sequence = [Introduction,Introduction2, Role, WaitPage1, Receive_damage_and_report, WaitPage2, Report_other1, Report_other2, Report_other3, Report_other4, WaitPage3, WaitPage4, ResultPage,Decision1, Waitforintro, final_results]