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()