import random from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) #import django #from django.db import models as DjangoModels import numpy as np import csv import pandas as pd doc = """ """ ############################################################ # ADDITIONAL METHODS CALLED BY CLASSES ############################################################ def make_invest_field(): return models.IntegerField( choices=[[0, 'Ich möchte meine 500 GE nicht investieren'], [1, 'Ich möchte meine 500 GE investieren']], label='Bitte entscheiden Sie, ob Sie in diese Person investieren würden.' ) def make_risk_field(): return models.IntegerField( choices=[[0, 'Äußerst geringes Ausfallrisiko'], [1, 'Sehr geringes Ausfallrisiko'], [2, 'Geringes Ausfallrisiko'], [3, 'Mittleres Ausfallrisiko'], [4, 'Hohes Ausfallrisiko'], [5, 'Sehr hohes Ausfallrisiko'], [6, 'Äußerst hohes Ausfallrisiko']], label='Bitte stufen Sie das Risiko ein, dass der Kreditnehmer Ihre Investition nicht zurückzahlen kann.' ) # widget=widgets.RadioSelect, ############################################################ # definition of constant variables ############################################################ class Constants(BaseConstants): name_in_url = 'Sparkassen_Experiment_Customers' players_per_group = None num_rounds = 5 payoff_factor = 0.003 show_up = 20 max_pay = 10 n_invest = 10 amount_invest = 100 per_payout = 20 engl = 0 factor_slider = 0.2 max_invest = 5 participation_fee = 1.5 risk_scale = ['Äußerst geringes Ausfallrisiko', 'Sehr geringes Ausfallrisiko', 'Geringes Ausfallrisiko', 'Mittleres Ausfallrisiko', 'Hohes Ausfallrisiko', 'Sehr hohes Ausfallrisiko', 'Äußerst hohes Ausfallrisiko'] invest_scale = ['Ich möchte meine 500 GE nicht investieren', 'Ich möchte meine 500 GE investieren'] # ============================================================================================ # QUESTIONS FOR THE HTML PAGE # ============================================================================================ # features_list = ['loan_amnt', 'term', 'int_rate', 'emp_length', 'home_ownership', # 'annual_inc', 'purpose', 'job_new'] loan_C = 'Kreditsumme' term_C = 'Kreditlaufzeit' int_rate_C = 'Zinsrate' emp_len_C = 'Länge aktueller Anstellung' #home_own_C = 'Wohnverhältnis' annual_inc_C = 'Jährliches Einkommen' purpose_C = 'Kreditzweck' job_C = 'Beruf' feats_ = ['Kreditsumme', 'Kreditlaufzeit in Monaten', 'Kreditzweck', 'Angebotener effektiver Jahreszins', 'Aktueller Beruf', 'Monatl. Ratenzahlung', 'Jährliches Einkommen Kreditnehmer'] # ============================================================================================ # DICTIONARIES TO TRANSLATE FLOAT NUMBERS INTO # ============================================================================================ # HUMAN INTERPRETABLE LABELS OF PARTICIPANTS' TRAITS # RANDOM STATE ON SESSION LEVEL TO DEFINE WHICH TRUSTEES ARE USED random_state_session = random.randint(1,100) # LIST OF 1-70 INTEGERS REPRESENTING THE TRUSTEES # LIST WILL BE USED TO RANDOMIZE AGAINST WHICH # TRUSTEES PARTICIPANTS PLAY # DATA SET OF TRUSTEES LOADED AS NESTED LISTS CONTAINING DICTS # Sparkassen_Experiment_Intro/EXP_DATA.csv # /Users/kev/Desktop/oTree_/Sparkassen_Experiment_Intro/EXP_DATA.csv #with open('Sparkassen_Experiment_Customers/EXP_DATA.csv', mode='r', encoding='utf-8-sig') as trustee_file: # trustee_traits = list(csv.DictReader(trustee_file, delimiter=",")) # ['loan_amnt_shap', 'term_shap', 'int_rate_shap', 'emp_length_shap', # 'annual_inc_shap', 'home_ownership_shap', 'purpose_shap', 'job_shap', # 'loan_amnt', 'term', 'int_rate', 'emp_length', 'home_ownership', # 'annual_inc', 'purpose', 'default', 'job_new', 'default_pred_prob', # 'default_pred_bin', 'default_pred_level'] # 'home_ownership', features_list = ['loan_amnt', 'term', 'purpose', 'int_rate', 'job_new', 'installment', 'annual_inc'] features_list2 = ['loan_amnt', 'term', 'purpose', 'int_rate', 'job_new', 'installment', 'annual_inc', 'default_pred_level', 'default_pred_bin'] prediction_behavior = ['default_pred_prob', 'default_pred_bin', 'default_pred_level'] # values trustee_traits_ = pd.read_csv('Sparkassen_Experiment_Customers/EXP_DATA.csv', sep=';') # NUMBER OF TRUSTEES trustees = np.arange(len(trustee_traits_)).tolist() # PAYOFF ROUND IN BEHAVIORAL PRIOR ROUND payoff_round = random.randint(1, 10) # TREATMENT CONDITIONS RANDOMIZATION treat_boolean = [] for i in range(300): temp_bin = [1, 1, 2, 2, 3, 3] random.shuffle(temp_bin) treat_boolean += temp_bin ############################################################ # Random assignment of subsession level, i.e. ROUND ############################################################ class Subsession(BaseSubsession): def creating_session(self): for p in self.get_players(): # RANDOMLY ASSIGNING TREATMENT CONDITION treat_boolean p.treatment_explain = Constants.treat_boolean[(p.id_in_group - 1)] p.participant.vars['treatment'] = p.treatment_explain p.participant.vars['rst_x'] = random.randint(0, 1) p.participant.vars['rst_y'] = random.randint(0, 3) ############################################################ # GROUP LEVEL VARIABLES AND METHODS ############################################################ class Group(BaseGroup): pass ############################################################ # SUBJECT LEVEL VARIABLES AND METHODS ############################################################ class Player(BasePlayer): #============================================================================================ # FIELDS #============================================================================================ # CONTROLS # ORDER IN WHICH INVESTMENTS OCCUR # NUMBERS REFER TO ROWS IN DATA SET invest_order1 = models.LongStringField(initial=None) # invest_order2 = models.LongStringField(initial=None) # slider_ind = models.LongStringField(initial=None) # TREATMENT CONDITION: # 1: AI + HUMAN # 2: HUMAN # 3: AI treatment_explain = models.IntegerField(initial=-1) # DECISION PARTICIPANT make_invest_field make_risk_field # PRIOR DECISIONS WITHOUT SLIDER invest_pri = make_invest_field() risk_pri = make_risk_field() # POST DECISIONS WITH SLIDER invest_post = make_invest_field() risk_post = make_risk_field() # CONSENT TO PARTICIPATE consent = models.IntegerField(label='', initial=None, choices=[[1, '']], widget=widgets.RadioSelect) # SLIDER TASK PROBABILITY TO OBSERVE ADVICE # slider_ = models.IntegerField(initial=50, # min=0, # max=100) # INCOME inc_ = models.FloatField(initial=0) # PRIOR FOR SLIDER # invest_slider_prior = make_invest_field() # risk_slider_prior = make_risk_field() # POSTERIOR FOR SLIDER # invest_slider_post = make_invest_field() # risk_slider_post = make_risk_field() # help_ = models.IntegerField(initial=-1) ######################################################### # NEW QUESTIONS ######################################################### q1 = models.IntegerField(label='Ich bin von der Investitionsempfehlung für den aktuellen Kreditantrag überzeugt.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) q2 = models.IntegerField( label='Ich habe Vertrauen in die Investitionsempfehlung für den aktuellen Kreditantrag.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) q3 = models.IntegerField( label='Ich glaube, dass die Beratung die Ausfallwahrscheinlichkeit dieses Kreditantrags präzise widerspiegelt.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) q4 = models.IntegerField( label='Die Beraterung ist daran schuld, wenn ich für diesen Kreditantrag eine suboptimale Investitionsentscheidung treffe.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) q5 = models.IntegerField( label='Die Beratung ist dafür verantwortlich, dass ich für diesen Kreditantrag eine optimale Investitionsentscheidung treffe.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) q6 = models.IntegerField( label='Ich würde mich darüber ärgern, wenn ich der aktuellen Investitionsempfehlung folge, sich diese aber als falsch herausstellt.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) q7 = models.IntegerField( label='Ich würde mich nicht wohl damit fühlen, dieser Investitionsempfehlung der Beratung nicht zu folgen.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) q8 = models.IntegerField( label='Ich würde mich schlecht fühlen, wenn ich diese Investitionsempfehlung der Beratung ignoriere, weil es wichtig ist höflich zu sein und Hilfe anzunehmen.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) q9 = models.IntegerField( label='Ich glaube, dass ich eine soziale Verpflichtung dazu habe, dieser Investitionsempfehlung der Beratung zu folgen.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelectHorizontal) ######################################################### # NEW QUESTIONS SLIDER ######################################################### # q1SLIDER = models.IntegerField(label='Ich fühle mich wohl, wenn ich mich für meine Anlageentscheidung auf die Investitionsempfehlung verlasse.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # # q2SLIDER = models.IntegerField( # label='Ich habe Vertrauen in die Investitionsempfehlung bezüglich diesem Kreditantrag.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # # q3SLIDER = models.IntegerField( # label='Ich würde der Beratung die Schuld geben, wenn ich eine falsche Investitionsempfehlung erhalte.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # q4SLIDER = models.IntegerField( # label='Ich würde der Beratung dankbar sein, wenn ich eine richtige Investitionsempfehlung erhalte.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # # q5SLIDER = models.IntegerField( # label='Die Beraterung ist dazu geeignet, mich vor dem Anlagerisiko für diesen Kreditantrag zu schützen.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # q6SLIDER = models.IntegerField( # label='Ich bin von meiner eigenen Investitionsentscheidung für diesen Kreditantrag überzeugt.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # # q7SLIDER = models.IntegerField( # label='Ich halte die Anlageempfehlung für integer, d.h., moralisch einwandfrei.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # # q8SLIDER = models.IntegerField( # label='Es war schwierig, das Risiko/die Wahrscheinlichkeit eines Zahlungsausfalls bei diesem Kreditantrag vorherzusagen.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # # q9SLIDER = models.IntegerField( # label='Ich möchte die Anlageempfehlung der Beratung akzeptieren, auch wenn ich mit der Empfehlung nicht einverstanden bin.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) # # # q10SLIDER = models.IntegerField( # label='Ich habe die volle Kontrolle über meine endgültige Investitionsentscheidung für diesen Kreditantrag.', # initial=None, # choices=[ # [1, '1'], # [2, '2'], # [3, '3'], # [4, '4'], # [5, '5'], # [6, '6'], # [7, '7']], # widget=widgets.RadioSelectHorizontal) ######################################################### # QUESTIONNAIRE ######################################################### q_age = models.IntegerField(label='Wie alt sind Sie?') q_female = models.IntegerField(label='Bitte geben Sie ihr Geschlecht an', choices=[ [0, 'Männlich'], [1, 'Weiblich'], [-1, 'Divers'], ], widget=widgets.RadioSelect ) q_work = models.IntegerField(label='Wie viele Jahre Arbeitserfahrung haben Sie?') q_degree = models.IntegerField(label='Bitte geben Sie Ihren höchsten akademischen Abschluss an.', choices=[ [0, 'Kein Abschluss'], [1, 'Hauptschulabschluss'], [2, 'Realschulabschluss'], [3, 'Abitur'], [4, 'Bachelor'], [5, 'Master'], [6, 'Doktor'], [7, 'Anderer'], ], widget=widgets.RadioSelect ) q_investexp = models.IntegerField( label='Auf einer Skala von 1 bis 7, wie hoch würden Sie Ihre Erfahrung mit Investitionsentscheidungen einschätzen?', initial=None, choices=[ [1, '1 (Äußerst gering)'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7 (Äußerst hoch)']], widget=widgets.RadioSelect) q_risk = models.IntegerField( label='Auf einer Skala von 1 bis 7, wie würden Sie Ihre Risikoneigung beschreiben?', initial=None, choices=[ [1, '1 (Extrem risikoavers)'], [2, '2'], [3, '3'], [4, '4 (Risikoneutral)'], [5, '5'], [6, '6'], [7, '7 (Extrem risikofreudig)']], widget=widgets.RadioSelect) q_ctrust1 = models.IntegerField( label='Auf einer Skala von 1 (stimme überhaupt nicht zu) bis 7 (stimme vollkommen zu), inwiefern stimmten Sie der folgenden Aussage zu: Die Beratung hat eine große Expertise bei den Investitionsempfehlung gezeigt.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) q_ctrust2 = models.IntegerField( label='Auf einer Skala von 1 (stimme überhaupt nicht zu) bis 7 (stimme vollkommen zu), inwiefern stimmten Sie der folgenden Aussage zu: Die Beratung ist sehr gut darin gute Investitionsempfehlung abzugeben.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) q_ctrust3 = models.IntegerField( label='Auf einer Skala von 1 (stimme überhaupt nicht zu) bis 7 (stimme vollkommen zu), inwiefern stimmten Sie der folgenden Aussage zu: Die Beratung liefert eine unvoreingenommene Schätzung des Kreditausfalls.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) q_etrust1 = models.IntegerField( label='Auf einer Skala von 1 (stimme überhaupt nicht zu) bis 7 (stimme vollkommen zu), inwiefern stimmten Sie der folgenden Aussage zu: Ich habe mich sicher gefühlt, wenn ich mich bei meinen Investitionsentscheidungen auf die Beratung verlassen habe.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) q_etrust2 = models.IntegerField( label='Auf einer Skala von 1 (stimme überhaupt nicht zu) bis 7 (stimme vollkommen zu), inwiefern stimmten Sie der folgenden Aussage zu: Ich habe mich wohl gefühlt, wenn ich mich bei meinen Investitionsentscheidungen auf die Beratung verlassen habe.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) q_etrust3 = models.IntegerField( label='Auf einer Skala von 1 (stimme überhaupt nicht zu) bis 7 (stimme vollkommen zu), inwiefern stimmten Sie der folgenden Aussage zu: Ich war zufrieden, wenn ich mich bei meinen Investitionsentscheidungen auf die Beratung verlassen habe.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) respGeneral = models.IntegerField( label='Was glauben Sie: Wer ist dafür verantwortlich, wenn Sie letztlich eine suboptimale Investitionsentscheidung getroffen haben?', initial=None, choices=[ [1, 'Sie selbst'], [2, 'Die Beratung'], [3, 'Sie selbst und die Beratung']], widget=widgets.RadioSelect) FutureNonFollow = models.IntegerField( label='Auf einer Skala von 1 (stimme überhaupt nicht zu) bis 7 (stimme vollkommen zu), inwiefern stimmten Sie der folgenden Aussage zu: Wenn ich erfahren würde, dass eine Investitionsempfehlungen falsch war, würde ich weiteren Investitionsempfehlungen weniger folgen.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) SocialAkw = models.IntegerField( label='Wenn ich die Wahl hätte, mich von einem Menschen oder einer Maschine beraten zu lassen, würde ich die maschinelle Beratung bevorzugen, weil es weniger sozial unangenehm wäre, nicht auf den Rat zu hören.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) Auth1 = models.IntegerField( label='Wenn ich der Empfehlung einer Maschine folge, habe ich weniger das Gefühl, meine Entscheidungsautonomie aufzugeben, als wenn ich der Empfehlung eines Menschen folge.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) Auth2 = models.IntegerField( label='Ich fühle mich wohl, wenn mich die Beratung in meiner Entscheidungsautonomie einschränkt.', initial=None, choices=[ [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7']], widget=widgets.RadioSelect) ManCheck = models.IntegerField( label='Die Beratung die ich für meine Investitionsentscheidungen erhalten habe stammt von', initial=None, choices=[ [1, 'einer Bankberater*in die von einer KI unterstützt wurde.'], [2, 'einer Bankberater*in.'], [3, 'Einer KI.']], widget=widgets.RadioSelect) #============================================================================================ # METHODS #============================================================================================ #============================================================================================ # ON AN INDIVIDUAL LEVEL, SHUFFLE THE OCCURRENCE OF TRUSTEES # SAVE THE ACTUAL ORDER IN WHICH PLAYERS ENCOUNTER TRUSTEES def trustee_(self): # CREATE RANDOM LIST OF TRUSTEES df_ = Constants.trustee_traits_.copy().reset_index() temp = df_.sample(n=Constants.num_rounds) order1 = temp['index'].values.tolist() self.invest_order1 = str(order1).strip('[]') self.participant.vars['trustees1'] = temp[Constants.features_list].values.tolist() self.participant.vars['trustees2'] = temp[Constants.features_list2].values.tolist() for k in range(len(order1)): self.participant.vars[f'Repayment{k + 1}'] = int(temp[temp['index'] == order1[k]]['Repay']) self.participant.vars[f'PossibleIncome{k + 1}'] = int(temp[temp['index'] == order1[k]]['open_acc']) #temp = temp.sample(frac=1) #order2 = temp['index'].values.tolist() #self.invest_order2 = str(order2).strip('[]') #self.participant.vars['trustees2'] = temp[Constants.features_list2].values.tolist() #df_ = df_[~(df_.index.isin(temp.index))] #slider_ = df_.sample(n=1) #self.slider_ind = str(slider_['index'].values.tolist()).strip('[]') #self.participant.vars['RepaymentSlider'] = int(slider_.iloc[0]['Repay']) #self.participant.vars['PossibleIncomeSlider'] = int(slider_.iloc[0]['open_acc']) #self.participant.vars['slider_ind'] = slider_ #============================================================================================ def store_decisions(self): round_ = self.round_number self.participant.vars[f'points_{round_}'] = self.invest_post # RANDOMLY DEFINE WHETHER SUBJECTS PARTICIPATE IN THE BASELINE OR TREATMENT SESSION def payment(self): self.participant.vars['points_'] = 0 x_ = random.randint(1,Constants.num_rounds) rel_dec = self.participant.vars[f'points_{x_}'] self.participant.vars['relevant_round'] = x_ self.participant.vars['relevant_decision'] = rel_dec self.participant.vars['relevantrepayment'] = 0 if rel_dec == 0: self.participant.vars['points_'] += 500 else: self.participant.vars['points_'] += self.participant.vars[f'Repayment{x_}'] print(self.participant.vars[f'Repayment{x_}']) self.participant.vars['relevantrepayment'] =self.participant.vars[f'Repayment{x_}'] #if self.in_round(1).invest_slider_post == 0: # self.participant.vars['points_'] += 500 #else: # self.participant.vars['points_'] += self.participant.vars['RepaymentSlider'] # slider points #self.participant.vars['slider_points'] = 150 - abs(50-self.in_round(1).slider_)*5 #self.participant.vars['points_'] += self.participant.vars['slider_points'] self.inc_ = round(self.participant.vars['points_']*Constants.payoff_factor + Constants.participation_fee,2) #============================================================================================ # DETERMINING WHETHER PARTICIPANTS OBTAIN HELP OR NOT #def slider_comp(self): # if random.randint(1, 100) <= self.slider_: # self.help_ = 1 # else: # self.help_ = 0