from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) from captcha.fields import ReCaptchaField from captcha.widgets import ReCaptchaV2Checkbox import random from scipy.special import comb import itertools as it author = 'Your name here' doc = """ Your app description """ def winning_prob_x(N, N_A, p_A, p_B, x): result = 0 for i in range(x+1): res = comb(N_A, x-i)*comb(N-N_A, i) * p_A**(x-i) * (1-p_A)**(N_A-(x-i)) * p_B**i * (1-p_B)**(N-N_A-i) result = result + res return result def winning_prob(N, N_A, p_A, p_B, x): result = 0 for i in range(x, N+1): res = winning_prob_x(N, N_A, p_A, p_B, i) result = result + res return result def winning_prob_prior(N, p_A, p_B, x): result = 0 for N_A in range(N+1): res = comb(N, N_A)/(2**N) * winning_prob(N, N_A, p_A, p_B, x) result = result + res return result class Constants(BaseConstants): name_in_url = 'intro' players_per_group = None num_rounds = 1 N_examples = 7 # Graphics select_row = 'salmon' # Bonuses bonus = {'elicit': 0.5, 'portfolio': 3 } # Industries industries = {'names': ['Eclipse', 'Rosepaw'], 'types': [('Ind_good', 0.5), ('Ind_bad', 0.3)], 'N_news': 4, 'N_news_good': 3 } industries['labels'] = ['Industry_' + str(i + 1) for i in range(len(industries['types']))] industries['prob_good'] = [x[1] for x in industries['types'] if x[0] == 'Ind_good'][0] industries['prob_bad'] = [x[1] for x in industries['types'] if x[0] == 'Ind_bad'][0] # News news = { # 'g1': 'good_1_IndustryName', # 'g2': 'good_2_IndustryName', # 'g3': 'good_3_IndustryName', # 'g4': 'good_4_IndustryName', # 'b1': 'bad_1_IndustryName', # 'b2': 'bad_2_IndustryName', # 'b3': 'bad_3_IndustryName', # 'b4': 'bad_4_IndustryName' 'g1': 'Analysts continue to report a steady improvement in business in the IndustryName industry, ' 'according to the IndustryName Alliance for Productivity and Innovation (EAPI) quarterly business ' 'outlook survey released today.', 'g2': 'Spherecords Inc., the largest company on the IndustryName market, on Tuesday said it remains ' 'positive for the year ahead as it reported double digit profit growth for next year.', 'g3': 'Tax reform generally helped consumers keep more of their income, and that helped spur both new and used ' 'demand last year on the IndustryName industry. Experts predict that the surge continues in the ' 'upcoming years.', 'g4': 'After the volatile end of the previous year, tentative stability has returned to IndustryName ' 'market at the start of the new year, with investors seeing some reversal of the losses experienced in ' 'December. Growth momentum has slowed, but the deceleration phase should end before midyear with ' 'supportive and flexible policy actions.', 'b1': 'Industry experts say: "The production shutdown at some of the leading plants in the IndustryName ' 'industry suggests a long-term plummeting of the market that might exceed the Easter plunge in the ' 'previous year."', 'b2': 'Along with other major companies in the IndustryName industry, Berrycorp can prepare for weeks ' 'of disruption, as it uses 25 separate suppliers; most of them suffering from significant delays in ' 'production.', 'b3': 'Threats of imposing tariffs on imports negatively affect profit outlook for companies in the ' 'IndustryName industry for most parts of the world. European producers, most notably the Boar ' 'Productions, are still exposed to the possibility of import tariffs in a key market and could well be ' 'caught in the crossfire between the US and China.', 'b4': 'Many small firms in the IndustryName industry have collapsed and many more to follow. Stocks ' 'sink after profit outlook is below expectations, negative cash flow view is confirmed.' } # Portfolios portfolios = dict() portfolios[1] = {'N_firms': 6, 'N_firms_pay': 3, 'N_firms_good': {'high': 4, 'medium': 2, 'low': 1 } } portfolios[2] = {'N_firms': 5, 'N_firms_pay': 3, 'N_firms_good': {'high': 4, 'medium': 3, 'low': 2 } } portfolios[3] = {'N_firms': 4, 'N_firms_pay': 2, 'N_firms_good': {'high': 4, 'medium': 3, 'low': 1 } } portfolios[4] = {'N_firms': 3, 'N_firms_pay': 1, 'N_firms_good': {'high': 2, 'medium': 1, 'low': 0 } } N_firms_max = 0 rounds = list(portfolios.keys()) for r in rounds: portfolios[r]['N_firms_bad'] = dict() for i in portfolios[r]['N_firms_good'].keys(): portfolios[r]['N_firms_bad'][i] = portfolios[r]['N_firms'] - portfolios[r]['N_firms_good'][i] portfolios[r]['winning_prob_prior'] = winning_prob_prior( N=portfolios[r]['N_firms'], p_A=industries['prob_good'], p_B=industries['prob_bad'], x=portfolios[r]['N_firms_pay'] )*100 if portfolios[r]['N_firms'] > N_firms_max: N_firms_max = portfolios[r]['N_firms'] portfolios['names'] = ['Portfolio_1', 'Portfolio_2'] portfolios['types'] = {'S_high': {'P_good': 'high', 'P_bad': 'medium' }, 'S_low': {'P_good': 'medium', 'P_bad': 'low' } } # Treatment groups treatment_sequences = {'treatment': ['treatment', 'control'] } P_good_assigned_prob = 0.8 class Subsession(BaseSubsession): def creating_session(self): # Constants self.session.vars['bonus'] = Constants.bonus self.session.vars['N_rounds'] = max(Constants.rounds) self.session.vars['select_row'] = Constants.select_row self.session.vars['P_good_assigned_prob'] = Constants.P_good_assigned_prob self.session.vars['industries'] = Constants.industries self.session.vars['portfolios'] = Constants.portfolios self.session.vars['N_firms_max'] = Constants.N_firms_max # Initialize number of incorrect submissions for the examples for p in self.get_players(): p.incorrect_portfolios_1 = 0 p.incorrect_portfolios_2 = 0 p.incorrect_portfolios_3 = 0 p.incorrect_portfolios_4 = 0 p.incorrect_payoff_1 = 0 p.incorrect_payoff_2 = 0 p.incorrect_payoff_3 = 0 p.incorrect_industries_1 = 0 p.incorrect_industries_2 = 0 p.incorrect_industries_3 = 0 # Treatment groups treatment_groups = [dict(zip(Constants.treatment_sequences.keys(), values)) for values in it.product(*Constants.treatment_sequences.values())] j = 0 for p in self.get_players(): group = j % len(treatment_groups) for key in Constants.treatment_sequences.keys(): p.participant.vars[key] = treatment_groups[group][key] j += 1 p.treatment = p.participant.vars['treatment'] # Industries for p in self.get_players(): p.participant.vars['industries'] = dict() types = Constants.industries['types'].copy() random.shuffle(types) names = Constants.industries['names'].copy() random.shuffle(names) good_news = [x for x in Constants.news.keys() if x[0] == 'g'] random.shuffle(good_news) bad_news = [x for x in Constants.news.keys() if x[0] == 'b'] random.shuffle(bad_news) news_list = {'Ind_good': good_news[:3] + [bad_news[0]], 'Ind_bad': [good_news[-1]] + bad_news[1:] } for i in news_list.keys(): random.shuffle(news_list[i]) for i in range(len(types)): key = 'Industry_' + str(i + 1) p.participant.vars['industries'][key] = {'name': names[i], 'type': types[i][0], 'profit_prob': types[i][1] } p.participant.vars['industries'][key]['news'] = dict() j = 1 ind_type = p.participant.vars['industries'][key]['type'] ind_name = p.participant.vars['industries'][key]['name'] for k in news_list[ind_type]: p.participant.vars['industries'][key]['news'][j] = Constants.news[k].replace( 'IndustryName', ind_name) j += 1 # Portfolios n = self.session.vars['N_rounds'] for p in self.get_players(): set_types = list(Constants.portfolios['types'].keys()).copy() set_types = set_types * n set_types = set_types[:n] random.shuffle(set_types) portfolio_types = list(list(Constants.portfolios['types'].values())[0].keys()).copy() rounds = Constants.rounds.copy() random.shuffle(rounds) p.round_order = str(rounds) R = 0 for r in rounds: R += 1 p.participant.vars[r] = dict() random.shuffle(portfolio_types) for i in range(len(portfolio_types)): key = 'Portfolio_' + str(i+1) p.participant.vars[r][key] = { 'type': portfolio_types[i], 'level': Constants.portfolios['types'][set_types[r-1]][portfolio_types[i]]} for i in p.participant.vars[r].keys(): # Firms in the portfolios portfolio_level = p.participant.vars[r][i]['level'] n_firms_good = Constants.portfolios[R]['N_firms_good'][portfolio_level] n_firms_bad = Constants.portfolios[R]['N_firms_bad'][portfolio_level] p.participant.vars[r][i]['N_firms_good'] = n_firms_good p.participant.vars[r][i]['N_firms'] = Constants.portfolios[R]['N_firms'] p.participant.vars[r][i]['N_firms_pay'] = Constants.portfolios[R]['N_firms_pay'] p.participant.vars[r][i]['winning_prob_prior'] = Constants.portfolios[R]['winning_prob_prior'] if p.participant.vars['industries']['Industry_1']['type'] == 'Ind_good': industry_good = 'Industry_1' industry_bad = 'Industry_2' else: industry_good = 'Industry_2' industry_bad = 'Industry_1' industry_sequence = [industry_good for j in range(n_firms_good)] + \ [industry_bad for j in range(n_firms_bad)] # random.shuffle(industry_sequence) for f in range(Constants.portfolios[R]['N_firms']): p.participant.vars[r][i][f] = { 'industry_type': industry_sequence[f], 'industry_name': p.participant.vars['industries'][industry_sequence[f]]['name'], } rnd = random.randint(0, 100) threshold = p.participant.vars['industries'][industry_sequence[f]]['profit_prob'] * 100 p.participant.vars[r][i][f]['profit'] = rnd <= threshold # Industry composition firms = [p.participant.vars[r][i][f]['industry_name'][0] for f in range( Constants.portfolios[R]['N_firms'])] p.participant.vars[r][i]['industry_composition'] = ''.join(firms) # Portfolio result n_firms_profit = sum([p.participant.vars[r][i][f]['profit'] for f in range( Constants.portfolios[R]['N_firms'])]) p.participant.vars[r][i]['win'] = n_firms_profit >= Constants.portfolios[R]['N_firms_pay'] # Winning probability p.participant.vars[r][i]['winning_prob'] = winning_prob( N=Constants.portfolios[R]['N_firms'], N_A=Constants.portfolios[R]['N_firms_good'][portfolio_level], p_A=Constants.industries['prob_good'], p_B=Constants.industries['prob_bad'], x=Constants.portfolios[R]['N_firms_pay'] )*100 # Winning probability after first firm n_firms_first = Constants.portfolios[R]['N_firms']-1 n_firms_pay_first = Constants.portfolios[R]['N_firms_pay'] - p.participant.vars[r][i][0]['profit'] ind_first = p.participant.vars[r][i][0]['industry_type'] ind_type_first = p.participant.vars['industries'][ind_first]['type'] == 'Ind_good' n_firms_good_first = Constants.portfolios[R]['N_firms_good'][portfolio_level] - ind_type_first p.participant.vars[r][i]['winning_prob_first'] = winning_prob( N=n_firms_first, N_A=n_firms_good_first, p_A=Constants.industries['prob_good'], p_B=Constants.industries['prob_bad'], x=n_firms_pay_first )*100 class Group(BaseGroup): pass class Player(BasePlayer): # Captcha captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox) # Treatment groups treatment = models.StringField() # Examples - portfolios check_portfolios_1 = models.StringField(choices=['Yes', 'No']) check_portfolios_2 = models.StringField(choices=['Yes', 'No']) check_portfolios_3 = models.StringField(choices=['Yes', 'No']) check_portfolios_4 = models.StringField(choices=['Yes', 'No']) incorrect_portfolios_1 = models.IntegerField() incorrect_portfolios_2 = models.IntegerField() incorrect_portfolios_3 = models.IntegerField() incorrect_portfolios_4 = models.IntegerField() # Examples - industries check_payoff_1 = models.StringField(choices=['Yes', 'No']) check_payoff_2 = models.StringField(choices=['Yes', 'No']) check_payoff_3 = models.StringField(choices=['Yes', 'No']) incorrect_payoff_1 = models.IntegerField() incorrect_payoff_2 = models.IntegerField() incorrect_payoff_3 = models.IntegerField() check_industries_1 = models.IntegerField(min=0, max=5) check_industries_2 = models.IntegerField(min=0, max=5) check_industries_3 = models.IntegerField(min=0, max=5) incorrect_industries_1 = models.IntegerField() incorrect_industries_2 = models.IntegerField() incorrect_industries_3 = models.IntegerField() round_order = models.StringField()