#This experiment, unlike many others, has been properly commented. Thus, you should read this carefully to help you understand, how to work your first OTree projects. #More information about Pages, Models, and templates can be found on the OTree documentaitons. #Please note that this experiment uses the older version of Otree and thus might look different to other examples on the internet. #------------------------------------- # #------------------------------------- from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random import math import openpyxl import pandas as pd import numpy as np import statistics from math import isnan author = 'Michael' doc = """ This is a study on investor behavior. """ #------------------------------------- # Basic inputs (parameters, distribution) #------------------------------------- class Constants(BaseConstants): name_in_url = 'FR1' players_per_group = None #Usually use None if there isn't a special reason you need to use groups. You can group participants with treatment keys num_rounds = 2 #Number of the rounds in the experiment num_page = 1 # this is useless round numbers start from 1. begin_row = 1 # in the csv/excel sheet n_of_distributions = 8 #Number of unique distributions in the excel/csv sheet. Usually the same number as the number of "treatments" num_rows = 101 #number of rows in the csv/excel file # n_participants = 16 # read the samples from return distributions sheet = pd.read_excel('FR1/r_distributions.xlsx', engine='openpyxl') sheet= sheet.dropna(axis=0, how="all", inplace=False) sheet= sheet.dropna(axis=1, how="all", inplace=False) #Creates a dictionary from the xlsx file distributions = {} for distr in range(n_of_distributions): distributions[distr] = [] for i in range(begin_row, num_rows + begin_row): if ([sheet.iloc[i, [2*distr]].iloc[0], sheet.iloc[i, [2*distr+1]].iloc[0]][0] <900): distributions[distr].append([sheet.iloc[i, [2*distr]].iloc[0], sheet.iloc[i, [2*distr+1]].iloc[0]]) #For this experiment we wanted to have quite mixed up treatment orderings. This randomly draws from a combination of two treatments and then mixes their order. a = [0,random.choice([2,6])]; # random.shuffle(a) b = [1,random.choice([3,7])]; # random.shuffle(b) c = [2,random.choice([0,4])]; # random.shuffle(c) d = [3,random.choice([1,5])]; # random.shuffle(d) e = [4,random.choice([2,6])]; # random.shuffle(e) f = [5,random.choice([3,7])]; # random.shuffle(f) g = [6,random.choice([0,4])]; # random.shuffle(g) h = [7,random.choice([1,5])]; # random.shuffle(h) treat_orderings = {0: a, 1: b, 2: c, 3: d, 4: e, 5: f, 6: g, 7: h} #This then finally lists the treatment orderes so we can refer back to it. Not that for example the treatment order 7 matches the 7 in the distributions #------------------------------------- # Allocate treatments #------------------------------------- class Subsession(BaseSubsession): def creating_session(self): print('creating session') # Allocate the treatment groups ---------------------------------------------------------- # grouping takes place only in the first round if self.round_number == 1: def apply_arrangement(lst, a): return [lst[i] for i in a] for p in self.get_players(): p.paid_subsection = random.randint(1,2) # Also, this would be better saved as participant. Currently saved as round 1 variable #Assigns treatmentkeys in away that they are grouped in three particpant with ids next to each other. Probably a smarter way to do this exists, but it works heh. One tip is to use '%' to deal the groups but in this experiment we wanted to have them grouped. if p.participant.id_in_session %24 == 1: p.participant.vars['treatment_key'] = 0 elif p.participant.id_in_session %24== 2: p.participant.vars['treatment_key'] = 0 elif p.participant.id_in_session %24== 3: p.participant.vars['treatment_key'] = 0 elif p.participant.id_in_session %24== 4: p.participant.vars['treatment_key'] = 1 elif p.participant.id_in_session %24== 5: p.participant.vars['treatment_key'] = 1 elif p.participant.id_in_session %24== 6: p.participant.vars['treatment_key'] = 1 elif p.participant.id_in_session %24== 7: p.participant.vars['treatment_key'] = 2 elif p.participant.id_in_session%24 == 8: p.participant.vars['treatment_key'] = 2 elif p.participant.id_in_session%24 == 9: p.participant.vars['treatment_key'] = 2 elif p.participant.id_in_session %24== 10: p.participant.vars['treatment_key'] = 3 elif p.participant.id_in_session %24== 11: p.participant.vars['treatment_key'] = 3 elif p.participant.id_in_session%24 == 12: p.participant.vars['treatment_key'] = 3 elif p.participant.id_in_session%24 == 13: p.participant.vars['treatment_key'] = 4 elif p.participant.id_in_session%24 == 14: p.participant.vars['treatment_key'] = 4 elif p.participant.id_in_session%24 == 15: p.participant.vars['treatment_key'] = 4 elif p.participant.id_in_session%24 == 16: p.participant.vars['treatment_key'] = 5 elif p.participant.id_in_session%24 == 17: p.participant.vars['treatment_key'] = 5 elif p.participant.id_in_session%24 == 18: p.participant.vars['treatment_key'] = 5 elif p.participant.id_in_session%24 == 19: p.participant.vars['treatment_key'] = 6 elif p.participant.id_in_session%24 == 20: p.participant.vars['treatment_key'] = 6 elif p.participant.id_in_session%24 == 21: p.participant.vars['treatment_key'] = 6 elif p.participant.id_in_session %24== 22: p.participant.vars['treatment_key'] = 7 elif p.participant.id_in_session %24== 23: p.participant.vars['treatment_key'] = 7 elif p.participant.id_in_session %24== 0: p.participant.vars['treatment_key'] = 7 # Lists containing player's treatment distributions [round_1_dist, round_2_dist], sample oreder (row order), and pairs order (col order A = 0, B = 1) p.participant.vars['treatment_dists'] = [] p.participant.vars['sample_order'] = [] p.participant.vars['pair_order'] = [] p.participant.vars['randomized_order_data'] = [] p.participant.vars['randomized_bulletPoints_Order'] = [] if p.participant.id_in_session%3 == 0: #in {3 ,6,9, 12 , 15, 18 ,21, 24, 27, 30, 33, 36, 39, 42, 45, 48}: number = random.choice([0,1]) if number == 1: number2 = 0 else: number2 = 1 p.participant.vars['randomized_order'] = [number, number2] p.participant.vars['randomized_order_data'].append([number, number2]) for distr in Constants.treat_orderings[p.participant.vars['treatment_key']]: so = list(range(len(Constants.distributions[distr]))) if p.participant.id_in_session % 3 == 1: #in {1, 4 , 7 , 10 , 13 , 16 , 19 , 22, 25, 28, 31, 34, 37, 40, 43, 46}: #so= [9,6,7,8,10,1,0,2,4,5,3] random.shuffle(so) elif p.participant.id_in_session % 3 == 2: a = random.choice([0,1]) p.participant.vars['randomized_bulletPoints_Order'].append(a) if a == 0 and len(Constants.distributions[distr]) > 50: so = [100, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] elif a == 0 and len(Constants.distributions[distr]) < 50: so = [10, 0 ,1, 2, 3, 4, 5, 6, 7, 8, 9] elif a == 1 and len(Constants.distributions[distr]) > 50: so = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100] else: so = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # so.sort() po = [0,1]; #You could also random shuffle this if you dind't want to have the pairs always in the same order p.participant.vars['sample_order'].append(so) p.participant.vars['pair_order'].append(po) # apply the ordering to returns round_i_distr = [] for pair in apply_arrangement(Constants.distributions[distr], so): round_i_distr.append(apply_arrangement(pair,po)) p.participant.vars['treatment_dists'].append(round_i_distr) p.participant.vars['distribution_n'] = Constants.treat_orderings[p.participant.vars['treatment_key']] # Save treatment value, sampling pairs, order of sample etc. to fields in database--------------------- for p in self.get_players(): dataArray = p.participant.vars['treatment_dists'][self.round_number - 1] if p.participant.id_in_session%24 in { 4 , 5,6 ,10 ,11,12, 16 ,17,18, 22,23,0}: for i in p.participant.vars['treatment_dists'][self.round_number - 1]: i.append(i[0] + i[1]) i[2] = round(i[2], 2) p.asset_1_weight = random.randint(0, 10)*10/100 p.shown_distribution_id = p.participant.vars['distribution_n'][self.round_number-1] this_round_distr = p.participant.vars['treatment_dists'][self.round_number-1] p.shown_distribution = ' | '.join([str(pair).replace(',',' ') for pair in this_round_distr]) round_sample_order = p.participant.vars['sample_order'][self.round_number-1] p.shown_sample_order = ' '.join([str(i) for i in round_sample_order]) round_pair_order = p.participant.vars['pair_order'][self.round_number-1] p.shown_pair_order = ' '.join([str(i) for i in round_pair_order]) p.treatment_key = p.participant.vars['treatment_key'] #round_randomized_bulletPoints_Order = p.participant.vars['randomized_bulletPoints_Order'] #round_randomized_order = p.participant.vars['randomized_order_data'] #p.randomized_bulletPoints_Order = ' '.join([str(i) for i in round_randomized_bulletPoints_Order]) #p.randomized_order = ' '.join([str(i) for i in round_randomized_order]) class Group(BaseGroup): pass class Player(BasePlayer): random_question_index = models.IntegerField() short_q = models.CharField() asset_1_weight = models.FloatField() #with_infoScreen = models.IntegerField() invest_first = models.IntegerField() #with_hundred = models.IntegerField() #with_A = models.IntegerField() # Treatment related --------------------------------- treatment_key = models.IntegerField() # which permutation in treat_orderings is selected shown_distribution_id = models.IntegerField() shown_distribution = models.LongStringField() shown_pair_order = models.LongStringField() shown_sample_order = models.LongStringField() clicks = models.LongStringField() randomized_order = models.IntegerField() randomized_bulletPoints_Order = models.IntegerField() # Payment related ----------------------------------- paid_subsection = models.PositiveIntegerField() # 1 or 2 (to be paid) #paid_rendite1 = models.FloatField() #paid_rendite2 = models.FloatField() #payoff_100 = models.CurrencyField() chosen_alternative = models.LongStringField() payoff_other = models.CurrencyField() # Other information -------------------------- browser_first = models.CharField() browser_last = models.CharField() #------------------------------------- # Tasks - Investment decision(s) #------------------------------------- InvestmentEitherOr = models.CharField(initial=None, choices=['Alternative 1', 'Alternative 2'], verbose_name='Based on the outcomes you have just seen, which alternative do you chose?', widget=widgets.RadioSelectHorizontal() ) #------------------------------------- # Directly after two rounds - Stuff related to payoff #------------------------------------- # to show the realization to be used for payoff NB: Not used (?) def realize(self): return "{0:.2f}".format(self.realization) # access the number of the payoff round def pay_round_num(self): return self.in_round(1).paid_subsection paid_input1 = models.CurrencyField() paid_input2 = models.CurrencyField() # Payoff def set_payoffs(self): payoff_distribution = self.participant.vars['treatment_dists'][self.in_round(1).paid_subsection - 1] if self.in_round(self.in_round(1).paid_subsection).InvestmentEitherOr == "Alternative 2": if self.participant.id_in_session%24 in { 4 , 5,6 ,10 ,11,12, 16 ,17,18, 22,23,0}: random_number = random.choice(range(len(payoff_distribution))) self.payoff= (payoff_distribution)[random_number][2] self.participant.vars['payoff_other'] = (payoff_distribution)[random_number][0] else: random_number = random.choice(range(len(payoff_distribution))) self.payoff= (payoff_distribution)[random_number][1] self.participant.vars['payoff_other'] = (payoff_distribution)[random_number][0] else: if self.participant.id_in_session%24 in { 4 , 5,6 ,10 ,11,12, 16 ,17,18, 22,23,0}:#alterantive a same for everyone random_number = random.choice(range(len(payoff_distribution))) self.payoff = (payoff_distribution)[random_number][0] self.participant.vars['payoff_other'] = (payoff_distribution)[random_number][2] else: random_number = random.choice(range(len(payoff_distribution))) self.payoff = (payoff_distribution)[random_number][0] self.participant.vars['payoff_other'] = (payoff_distribution)[random_number][1] self.chosen_alternative = self.in_round(self.in_round(1).paid_subsection).InvestmentEitherOr #----------------------------------------- # Directly after two rounds - Comparison of two rounds #----------------------------------------- ComparisonRounds_Risk = models.IntegerField(initial=None, choices=[ [1, 'My first alternative is riskeir.'], [2, 'My two alternatives are equally risky.'], [3, 'My second alternative is riskier.'], ], verbose_name='Which of the two alternatives you selected in your first and second round do you assess as riskier?', widget=widgets.RadioSelect()) ComparisonRounds_Return = models.IntegerField(initial=None, choices=[ [1, 'My first alternative has a higher expected return.'], [2, 'My two alternatives have equally high expected returns.'], [3, 'My second alternative has a higher expected return.'], ], verbose_name='Which of the two alternatives you selected in your first and second round exhibits higher expected returns?', widget=widgets.RadioSelect()) #----------------------------------------- # Directly after two rounds - Reasons for investment decision independent of the particular round #----------------------------------------- How_Risky_A = models.PositiveIntegerField( label='How risky is Alternative 1 on a scale between 1 ("risk-free") and 7 ("very risky")?', choices=range(1, 8), initial=None, widget=widgets.RadioSelectHorizontal() ) How_Risky_B = models.PositiveIntegerField( label='How risky is Alternative 2 on a scale between 1 ("risk-free") and 7 ("very risky")?', choices=range(1, 8), initial=None, widget=widgets.RadioSelectHorizontal() ) Average_of_A = models.CurrencyField( initial=None, label='What outcome do you expect for Alternative 1 on average?', min=0, max=2, doc=""" Amount invested by this player. """ ) Average_of_B = models.CurrencyField( initial=None, label='What outcome do you expect for Alternative 2 on average?', min=0, max=2, doc=""" Amount invested by this player. """ ) GenReasons_Mu_Consider = models.IntegerField( initial=None, verbose_name='Did you consider the expected outcomes of Alternative 1 and Alternative 2?', choices=[ [1, 'No'], [2, 'Yes'],], widget=widgets.RadioSelectHorizontal()) GenReasons_Mu_Direction = models.IntegerField( initial=None, verbose_name='You considered expected outcomes of the alternatives: Would you choose the alternative with the lower or higher expected outcome?', choices=[ [1, 'Lower'], [2, 'Higher'], [3, 'I do not know'],], widget=widgets.RadioSelectHorizontal()) GenReasons_SigmaPL_Consider = models.IntegerField( initial=None, verbose_name='Did you consider the volatility (fluctuation) of the outcomes of Alternative 1 and Alternative 2?', choices=[ [1, 'No'], [2, 'Yes'],], widget=widgets.RadioSelectHorizontal()) GenReasons_SigmaPL_Direction = models.IntegerField( initial=None, verbose_name='You considered the volatility (fluctuation) in outcomes of the alternatives: Would you choose the alternative with the lower or higher volatility?', choices=[ [1, 'Lower'], [2, 'Higher'], [3, 'I do not know'],], widget=widgets.RadioSelectHorizontal()) GenReasons_PD_Consider = models.IntegerField( initial=None, verbose_name='Did you consider the frequency of better outcomes (e.g., the likelihood that the outcome of Alternative 2 is higher than the outcome of Alternative 1)?', choices=[ [1, 'No'], [2, 'Yes'],], widget=widgets.RadioSelectHorizontal()) GenReasons_PD_Direction = models.IntegerField( initial=None, verbose_name='You considered the frequency of better outcomes: Would you choose the alternative with the lower or higher frequency of better outcomes?', choices=[ [1, 'Lower'], [2, 'Higher'], [3, 'I do not know'],], widget=widgets.RadioSelectHorizontal()) #----------------------------------------- # Demographics / Questionnaire - Can partially already be asked before the start of treatments (helps analyze attrition later on) #----------------------------------------- Demographics_Age = models.PositiveIntegerField( label='What is your age?', min=1, max=130 ) Demographics_Sex = models.IntegerField(initial=None, choices=[ [1, 'Male'], [2, 'Female'], ], verbose_name='Are you male or female?', widget=widgets.RadioSelectHorizontal()) Demographics_RiskAffinity = models.PositiveIntegerField( verbose_name='Please assess your willingness to take risks. Select a category between 1 ("Not willing to take risks") and 5 ("Willing to take large risks").', choices=range(1, 6), initial=None, widget=widgets.RadioSelectHorizontal()) Demographics_Statistics = models.PositiveIntegerField( verbose_name='How would you describe your skills in statistics? Please select a category between 1 ("Poor") and 5 ("Excellent").', choices=range(1, 6), initial=None, widget=widgets.RadioSelectHorizontal()) #----------------------------------------- # Final question (satisfaction, feedback...) #----------------------------------------- Satisfaction = models.PositiveIntegerField( verbose_name='How satisfied are you with your result? Please select a category between 1 ("Very unsatisfied") and 5 ("Very satisfied")?', choices=range(1, 6), initial=None, widget=widgets.RadioSelectHorizontal()) OpenFeedback = models.LongStringField( blank=True, label='Please describe in short any feedback you might have on this experiment.', ) #----------------------------------------- # Stuff related to Prolific, mTurk, or other platform used to run the experiment #----------------------------------------- prolific_id = models.StringField( blank=True, label='Your Prolific ID')