from otree.api import * import time import random doc = "Single Auction Market" class C(BaseConstants): NAME_IN_URL = 'single_auction' PLAYERS_PER_GROUP = None NUM_ROUNDS = 24 ITEMS_PER_TRADER = 4 ITEMS_PER_MANIPULATOR = 8 TRADER_ENDOWMENT = 400.0 MANIPULATOR_ENDOWMENT = 800.0 FORECASTER_ENDOWMENT = 100.0 TRADING_LENGTH = 180 MM_INIT_ASSET_BALANCE = 32 MM_INIT_CURR_BALANCE = 1600 K = float(MM_INIT_ASSET_BALANCE * MM_INIT_CURR_BALANCE) class Subsession(BaseSubsession): asset_trigger = models.BooleanField() asset_state = models.StringField() manipulator_present = models.BooleanField(initial=False) manipulator_colour1 = models.StringField(initial='None') manipulator_colour2 = models.StringField(initial='None') manipulator_id = models.IntegerField(initial=0) rest_id = models.IntegerField(initial=0) transparent_treatment = models.BooleanField(initial=False) def creating_session(subsession: Subsession): subsession.group_randomly() subsession.asset_trigger = random.choice([True, False]) if subsession.round_number == 1: subsession.manipulator_present = False if subsession.round_number == 2: subsession.manipulator_present = False if subsession.round_number == 3: subsession.manipulator_present = True if subsession.round_number == 4: subsession.manipulator_present = False if subsession.round_number == 5: subsession.manipulator_present = True if subsession.round_number == 6: subsession.manipulator_present = True if subsession.round_number == 7: subsession.manipulator_present = True if subsession.round_number == 8: subsession.manipulator_present = False if subsession.round_number == 9: subsession.manipulator_present = True if subsession.round_number == 10: subsession.manipulator_present = False if subsession.round_number == 11: subsession.manipulator_present = False if subsession.round_number == 12: subsession.manipulator_present = True if subsession.round_number == 13: subsession.manipulator_present = True if subsession.round_number == 14: subsession.manipulator_present = False if subsession.round_number == 15: subsession.manipulator_present = False if subsession.round_number == 16: subsession.manipulator_present = True if subsession.round_number == 17: subsession.manipulator_present = True if subsession.round_number == 18: subsession.manipulator_present = False if subsession.round_number == 19: subsession.manipulator_present = False if subsession.round_number == 20: subsession.manipulator_present = True if subsession.round_number == 21: subsession.manipulator_present = False if subsession.round_number == 22: subsession.manipulator_present = False if subsession.round_number == 23: subsession.manipulator_present = True if subsession.round_number == 24: subsession.manipulator_present = True subsession.manipulator_id = random.randint(1, 4) subsession.rest_id = random.randint(5, 8) if subsession.round_number <= 12: subsession.transparent_treatment = False else: subsession.transparent_treatment = True if subsession.asset_trigger: subsession.asset_state = 'Black' else: subsession.asset_state = 'White' players = subsession.get_players() for p in players: p.is_trader = p.id_in_group <= 8 p.is_forecaster = p.id_in_group > 8 if subsession.manipulator_present: if p.id_in_group == subsession.manipulator_id: p.is_manipulator = True p.is_trader = False if p.id_in_group == subsession.rest_id: p.is_rest = True p.is_trader = False else: p.is_manipulator = False if p.is_trader: p.balance = C.TRADER_ENDOWMENT p.num_shares_in_possession = C.ITEMS_PER_TRADER if p.is_forecaster: p.balance = C.FORECASTER_ENDOWMENT if p.is_manipulator: p.manipulator_balance_colour1 = 400 p.manipulator_balance_colour2 = 400 p.manipulator_asset_balance_colour1 = 4 p.manipulator_asset_balance_colour2 = 4 for p in players: group = p.group group.time_offset = random.randint(0, 60) if p.id_in_group == 1: p.colour = 'Red' if p.id_in_group == 2: p.colour = 'Green' if p.id_in_group == 3: p.colour = 'Blue' if p.id_in_group == 4: p.colour = 'Yellow' if p.id_in_group == 5: p.colour = 'Orange' if p.id_in_group == 6: p.colour = 'Purple' if p.id_in_group == 7: p.colour = 'Brown' if p.id_in_group == 8: p.colour = 'Pink' if p.is_manipulator: subsession.manipulator_colour1 = p.colour if p.is_rest: subsession.manipulator_colour2 = p.colour if p.is_trader: p.player_role = 'Trader' if p.is_forecaster: p.player_role = 'Forecaster' if p.is_manipulator: p.player_role = 'Manipulator' if p.is_rest: p.player_role = 'Spectator' p.signal_trigger = random.randint(0, 2) if subsession.asset_state == 'White': p.asset_state = 'White' if subsession.asset_state == 'Black': p.asset_state = 'Black' if p.is_trader: if not subsession.asset_trigger and p.signal_trigger < 2: p.signal = 'Light Grey' p.asset_state = 'White' if not subsession.asset_trigger and p.signal_trigger == 2: p.signal = 'Dark Grey' p.asset_state = 'White' if subsession.asset_trigger and p.signal_trigger < 2: p.signal = 'Dark Grey' p.asset_state = 'Black' if subsession.asset_trigger and p.signal_trigger == 2: p.signal = 'Light Grey' p.asset_state = 'Black' if p.is_forecaster: p.signal = 'Forecaster' if p.is_manipulator: p.signal = 'Manipulator' if p.is_rest: p.signal = 'Rest' for p in players: p.manipulator_colour1 = subsession.manipulator_colour1 p.manipulator_colour2 = subsession.manipulator_colour2 class Group(BaseGroup): start_timestamp = models.IntegerField() time_offset = models.IntegerField(initial=0) seconds = models.IntegerField(doc="Timestamp (seconds since beginning of trading)") class Player(BasePlayer): is_trader = models.BooleanField(iniitial=False) is_forecaster = models.BooleanField(initial=False) is_manipulator = models.BooleanField(initial=False) is_rest = models.BooleanField(initial=False) manipulator_balance_colour1 = models.CurrencyField(initial=0) manipulator_balance_colour2 = models.CurrencyField(initial=0) manipulator_asset_balance_colour1 = models.IntegerField(initial=0) manipulator_asset_balance_colour2 = models.IntegerField(initial=0) manipulator_escrow_amount1 = models.CurrencyField(initial=0) manipulator_escrow_amount2 = models.CurrencyField(initial=0) player_role = models.StringField() num_shares_in_possession = models.IntegerField(initial=0) player_id = models.StringField() colour = models.StringField(initial='None') balance = models.CurrencyField(initial=0) current_price = models.CurrencyField(initial=50) mm_asset_balance = models.IntegerField(initial=C.MM_INIT_ASSET_BALANCE) mm_curr_balance = models.CurrencyField(initial=C.MM_INIT_CURR_BALANCE) buy_price = models.CurrencyField(initial=53.28) sell_price = models.CurrencyField(initial=47.02) manipulator_colour1 = models.StringField(initial='None') manipulator_colour2 = models.StringField(initial='None') trade_type = models.StringField(initial='Init') escrow_amount = models.CurrencyField(initial=0) signal = models.StringField(inital='Init') signal_trigger = models.IntegerField() asset_state = models.StringField() invalidtrade = models.BooleanField(initial=False) validtrade = models.BooleanField(initial=True) invest = models.IntegerField( min=0, max=100, label="How much do you want to invest in each asset type?" ) def buy_cost(player: Player): player.buy_price = cu(C.K / ((player.mm_asset_balance - 1) ** 2)) def sell_cost(player: Player): player.sell_price = cu(C.K / ((player.mm_asset_balance + 1) ** 2)) def current_price(player: Player): player.current_price = cu(player.mm_curr_balance / player.mm_asset_balance) def buy_one(player: Player): player.trade_type = 'buy' group = player.group if player.validtrade: player.balance -= player.buy_price player.num_shares_in_possession += 1 if player.num_shares_in_possession <= 0 and player.escrow_amount > 0: player.balance += 100 player.escrow_amount -= 100 Transaction.create( group=group, trade_type='buy', trade_price=player.buy_price, colour=player.colour, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance += p.buy_price p.mm_asset_balance -= 1 buy_cost(p) sell_cost(p) current_price(p) def buy_one_manipulator_colour1(player: Player): player.trade_type = 'buy_manip_1' group = player.group if player.validtrade: player.manipulator_balance_colour1 -= player.buy_price player.manipulator_asset_balance_colour1 += 1 if player.manipulator_asset_balance_colour1 <= 0 and player.manipulator_escrow_amount1 > 0: player.manipulator_balance_colour1 += 100 player.manipulator_escrow_amount1 -= 100 Transaction.create( group=group, trade_type='buy', trade_price=player.buy_price, colour=player.manipulator_colour1, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance += p.buy_price p.mm_asset_balance -= 1 buy_cost(p) sell_cost(p) current_price(p) def buy_one_manipulator_colour2(player: Player): player.trade_type = 'buy_manip_2' group = player.group subsession = player.subsession if player.validtrade: player.manipulator_balance_colour2 -= player.buy_price player.manipulator_asset_balance_colour2 += 1 if player.manipulator_asset_balance_colour2 <= 0 and player.manipulator_escrow_amount2 > 0: player.manipulator_balance_colour2 += 100 player.manipulator_escrow_amount2 -= 100 Transaction.create( group=group, trade_type='buy', trade_price=player.buy_price, colour=player.manipulator_colour2, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance += p.buy_price p.mm_asset_balance -= 1 buy_cost(p) sell_cost(p) current_price(p) def sell_one(player: Player): player.trade_type = 'sell' group = player.group if player.validtrade: if player.num_shares_in_possession > 0: player.balance += player.sell_price player.num_shares_in_possession -= 1 Transaction.create( group=group, trade_type='sell', trade_price=player.sell_price, colour=player.colour, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance -= player.sell_price p.mm_asset_balance += 1 sell_cost(p) buy_cost(p) current_price(p) else: player.balance -= (100 - player.sell_price) player.escrow_amount += 100 player.num_shares_in_possession -= 1 Transaction.create( group=group, trade_type='sell', trade_price=player.sell_price, colour=player.colour, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance -= player.sell_price p.mm_asset_balance += 1 sell_cost(p) buy_cost(p) current_price(p) def sell_one_manipulator_colour1(player: Player): player.trade_type = 'sell_manip_1' group = player.group if player.validtrade: if player.manipulator_asset_balance_colour1 > 0: player.manipulator_balance_colour1 += player.sell_price player.manipulator_asset_balance_colour1 -= 1 Transaction.create( group=group, trade_type='sell', trade_price=player.sell_price, colour=player.manipulator_colour1, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance -= player.sell_price p.mm_asset_balance += 1 sell_cost(p) buy_cost(p) current_price(p) else: player.manipulator_balance_colour1 -= (100 - player.sell_price) player.manipulator_escrow_amount1 += 100 player.manipulator_asset_balance_colour1 -= 1 Transaction.create( group=group, trade_type='sell', trade_price=player.sell_price, colour=player.manipulator_colour1, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance -= player.sell_price p.mm_asset_balance += 1 sell_cost(p) buy_cost(p) current_price(p) def sell_one_manipulator_colour2(player: Player): player.trade_type = 'sell_manip_2' group = player.group if player.validtrade: if player.manipulator_asset_balance_colour2 > 0: player.manipulator_balance_colour2 += player.sell_price player.manipulator_asset_balance_colour2 -= 1 Transaction.create( group=group, trade_type='sell', trade_price=player.sell_price, colour=player.manipulator_colour2, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance -= player.sell_price p.mm_asset_balance += 1 sell_cost(p) buy_cost(p) current_price(p) else: player.manipulator_balance_colour2 -= (100 - player.sell_price) player.manipulator_escrow_amount2 += 100 player.manipulator_asset_balance_colour2 -= 1 Transaction.create( group=group, trade_type='sell', trade_price=player.buy_price, colour=player.manipulator_colour2, seconds=int(time.time() - group.start_timestamp) ) for p in player.subsession.get_players(): p.mm_curr_balance -= player.sell_price p.mm_asset_balance += 1 sell_cost(p) buy_cost(p) current_price(p) class Transaction(ExtraModel): group = models.Link(Group) trade_type = models.StringField() trade_price = models.FloatField() colour = models.StringField() seconds = models.IntegerField(doc="Timestamp (seconds since beginning of trading)") def custom_export(players): yield ['player', 'round_number', 'is_manipulator', 'is_trader', 'trade_type', 'trade_price', 'colour', 'seconds'] for p in players: group = p.group transactions = Transaction.filter(group=group) for transaction in transactions: yield [p.id_in_group, p.round_number, p.is_manipulator, p.is_trader, transaction.trade_type, transaction.trade_price, transaction.colour, transaction.seconds] def array2htmltable(data): q = "\n" for i in [(data[0:], 'td')]: q += "\n".join( [ "%s" % str(_mm) for _mm in [ "".join( [ "<%s>%s" % (i[1], str(_q), i[1]) for _q in _m ] ) for _m in i[0] ] ]) + "\n" q += "
" return q # PAGES class IntroductionPage(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class WaitToStart(WaitPage): pass class Transparent(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 13 class AssignmentPage(Page): @staticmethod def vars_for_template(player: Player): return player.role == {} class SignalPage(Page): @staticmethod def vars_for_template(player: Player): return player.asset_state == {} class WaitToTrade(WaitPage): @staticmethod def after_all_players_arrive(group: Group): group.start_timestamp = int(time.time()) class TradingForecasting(Page): form_model = 'player' form_fields = ['invest'] @staticmethod def live_method(player: Player, data): group = player.group highcharts_series = [[tx.seconds, tx.trade_price] for tx in Transaction.filter(group=group)] tape_series = [[tx.trade_type, tx.trade_price, tx.colour, tx.seconds] for tx in Transaction.filter(group=group)] if data == "buy": player.trade_type = 'buy' if data == "buy_manip_1": player.trade_type = 'buy_manip_1' if data == "buy_manip_2": player.trade_type = 'buy_manip_2' if data == "sell": player.trade_type = 'sell' if data == "sell_manip_1": player.trade_type = 'sell_manip_1' if data == "sell_manip_2": player.trade_type = 'sell_manip_2' if player.trade_type == 'sell': if player.num_shares_in_possession > 0: player.validtrade = True else: if player.balance >= 100: player.validtrade = True else: player.validtrade = False if player.trade_type == 'sell_manip_1': if player.manipulator_asset_balance_colour1 > 0: player.validtrade = True else: if player.manipulator_balance_colour1 >= 100: player.validtrade = True else: player.validtrade = False if player.trade_type == 'sell_manip_2': if player.manipulator_asset_balance_colour2 > 0: player.validtrade = True else: if player.manipulator_balance_colour2 >= 100: player.validtrade = True else: player.validtrade = False if player.trade_type == 'buy': if (player.balance - player.buy_price) > 0: player.validtrade = True else: player.validtrade = False if player.trade_type == 'buy_manip_1': if (player.manipulator_balance_colour1 - player.buy_price) > 0: player.validtrade = True else: player.validtrade = False if player.trade_type == 'buy_manip_2': if (player.manipulator_balance_colour2 - player.buy_price) > 0: player.validtrade = True else: player.validtrade = False if player.is_trader: if data["trade_type"] == "buy": buy_one(player) z = array2htmltable(tape_series) return { p.id_in_group: dict( highcharts_series=highcharts_series, tape_series=tape_series, z=z, buy_price=p.buy_price, sell_price=p.sell_price, current_price=p.current_price, num_shares_in_possession=p.num_shares_in_possession, manipulator_balance_colour1=p.manipulator_balance_colour1, manipulator_balance_colour2=p.manipulator_balance_colour2, manipulator_asset_balance_colour1=p.manipulator_asset_balance_colour1, manipulator_asset_balance_colour2=p.manipulator_asset_balance_colour2, manipulator_escrow_amount1=p.manipulator_escrow_amount1, manipulator_escrow_amount2=p.manipulator_escrow_amount2, escrow_amount=p.escrow_amount, balance=p.balance ) for p in player.group.get_players() } if player.is_manipulator: if data["trade_type"] == "buy_manip_1": buy_one_manipulator_colour1(player) z = array2htmltable(tape_series) return { p.id_in_group: dict( highcharts_series=highcharts_series, tape_series=tape_series, z=z, buy_price=p.buy_price, sell_price=p.sell_price, current_price=p.current_price, num_shares_in_possession=p.num_shares_in_possession, manipulator_balance_colour1=p.manipulator_balance_colour1, manipulator_balance_colour2=p.manipulator_balance_colour2, manipulator_asset_balance_colour1=p.manipulator_asset_balance_colour1, manipulator_asset_balance_colour2=p.manipulator_asset_balance_colour2, manipulator_escrow_amount1=p.manipulator_escrow_amount1, manipulator_escrow_amount2=p.manipulator_escrow_amount2, escrow_amount=p.escrow_amount, balance=p.balance ) for p in player.group.get_players() } if data["trade_type"] == "buy_manip_2": buy_one_manipulator_colour2(player) z = array2htmltable(tape_series) return { p.id_in_group: dict( highcharts_series=highcharts_series, tape_series=tape_series, z=z, buy_price=p.buy_price, sell_price=p.sell_price, current_price=p.current_price, num_shares_in_possession=p.num_shares_in_possession, manipulator_balance_colour1=p.manipulator_balance_colour1, manipulator_balance_colour2=p.manipulator_balance_colour2, manipulator_asset_balance_colour1=p.manipulator_asset_balance_colour1, manipulator_asset_balance_colour2=p.manipulator_asset_balance_colour2, manipulator_escrow_amount1=p.manipulator_escrow_amount1, manipulator_escrow_amount2=p.manipulator_escrow_amount2, escrow_amount=p.escrow_amount, balance=p.balance ) for p in player.group.get_players() } if player.is_trader: if data["trade_type"] == "sell": sell_one(player) z = array2htmltable(tape_series) return { p.id_in_group: dict( highcharts_series=highcharts_series, tape_series=tape_series, z=z, buy_price=p.buy_price, sell_price=p.sell_price, current_price=p.current_price, num_shares_in_possession=p.num_shares_in_possession, manipulator_balance_colour1=p.manipulator_balance_colour1, manipulator_balance_colour2=p.manipulator_balance_colour2, manipulator_asset_balance_colour1=p.manipulator_asset_balance_colour1, manipulator_asset_balance_colour2=p.manipulator_asset_balance_colour2, manipulator_escrow_amount1=p.manipulator_escrow_amount1, manipulator_escrow_amount2=p.manipulator_escrow_amount2, escrow_amount=p.escrow_amount, balance=p.balance ) for p in player.subsession.get_players() } if player.is_manipulator: if data["trade_type"] == "sell_manip_1": sell_one_manipulator_colour1(player) z = array2htmltable(tape_series) return { p.id_in_group: dict( highcharts_series=highcharts_series, tape_series=tape_series, z=z, buy_price=p.buy_price, sell_price=p.sell_price, current_price=p.current_price, num_shares_in_possession=p.num_shares_in_possession, manipulator_balance_colour1=p.manipulator_balance_colour1, manipulator_balance_colour2=p.manipulator_balance_colour2, manipulator_asset_balance_colour1=p.manipulator_asset_balance_colour1, manipulator_asset_balance_colour2=p.manipulator_asset_balance_colour2, manipulator_escrow_amount1=p.manipulator_escrow_amount1, manipulator_escrow_amount2=p.manipulator_escrow_amount2, escrow_amount=p.escrow_amount, balance=p.balance ) for p in player.subsession.get_players() } if data["trade_type"] == "sell_manip_2": sell_one_manipulator_colour2(player) z = array2htmltable(tape_series) return { p.id_in_group: dict( highcharts_series=highcharts_series, tape_series=tape_series, z=z, buy_price=p.buy_price, sell_price=p.sell_price, current_price=p.current_price, num_shares_in_possession=p.num_shares_in_possession, manipulator_balance_colour1=p.manipulator_balance_colour1, manipulator_balance_colour2=p.manipulator_balance_colour2, manipulator_asset_balance_colour1=p.manipulator_asset_balance_colour1, manipulator_asset_balance_colour2=p.manipulator_asset_balance_colour2, manipulator_escrow_amount1=p.manipulator_escrow_amount1, manipulator_escrow_amount2=p.manipulator_escrow_amount2, escrow_amount=p.escrow_amount, balance=p.balance ) for p in player.subsession.get_players() } @staticmethod def get_timeout_seconds(player: Player): group = player.group if player.is_forecaster: return C.TRADING_LENGTH - group.time_offset else: return C.TRADING_LENGTH class ResultsWaitPage(WaitPage): pass class Results(Page): pass class CompletionPage(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 24 page_sequence = [IntroductionPage, WaitToStart, Transparent, AssignmentPage, SignalPage, WaitToTrade, TradingForecasting, Results, CompletionPage]