# This is adapted from https://s3.amazonaws.com/otreehub/browsable_source/d7188187-cdba-4c61-ae3a-d7cc3d6fcde9/double_auction/index.html # programming assistance from Kevin James from otree.api import * import time import random import math from sqlalchemy.orm import session doc = "OneLemonMarket" class Constants(BaseConstants): name_in_url = 'OneLemonMarket' players_per_group = None num_rounds = 5 # session.vars = session.config['num_rounds'] # num_rounds = 15 peach = 100 lemon = 0 pr_lemon = .2 class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): import random session = subsession.session num_rounds = session.config['num_rounds'] for p in subsession.get_players(): p.num_rounds = num_rounds # for more buyers, change the 2 to 3 p.is_buyer = p.id_in_group % 2 > 0 if p.is_buyer: p.num_items = 0 p.current_offer = 0 p.nLemon = 0 p.nPeach = 0 else: p.num_items = 1 p.current_offer = 101 p.nPeach = math.ceil(random.uniform(0,1)-Constants.pr_lemon) p.nLemon = 1 - p.nPeach p.sell_peach = round(Constants.peach * (1-Constants.pr_lemon))+1 p.StartPeach = p.nPeach class Group(BaseGroup): start_timestamp = models.IntegerField() class Player(BasePlayer): is_buyer = models.BooleanField() current_offer = models.CurrencyField() break_even_point = models.CurrencyField() num_items = models.IntegerField() nPeach = models.IntegerField() nLemon = models.IntegerField() profit = models.CurrencyField() sell_peach = models.IntegerField() StartPeach = models.IntegerField() num_rounds = models.IntegerField() class Transaction(ExtraModel): group = models.Link(Group) buyer = models.Link(Player) seller = models.Link(Player) price = models.CurrencyField() seconds = models.IntegerField(doc="Timestamp (seconds since beginning of trading)") def find_match(buyers, sellers): for buyer in buyers: for seller in sellers: if seller.num_items > 0 and seller.current_offer <= buyer.current_offer: return [buyer, seller] def live_method(player: Player, data): group = player.group players = group.get_players() buyers = [p for p in players if p.is_buyer] sellers = [p for p in players if not p.is_buyer] news = None if data: try: type = data['type'] except Exception: print('Invalid message received', data) return if type == 'Offer': offer = int(data['offer']) player.current_offer = offer if type == 'Buy': seller_id = int(data['seller']) buyer = player seller = [p for p in sellers if p.id_in_group==seller_id][0] price = seller.current_offer Transaction.create( group=group, buyer=buyer, seller=seller, price=price, seconds=int(time.time() - group.start_timestamp), ) buyer.num_items += 1 seller.num_items -= 1 buyer.nLemon = seller.nLemon buyer.nPeach = seller.nPeach seller.nLemon = 0 seller.nPeach = 0 buyer.payoff = buyer.nLemon*Constants.lemon + buyer.nPeach*Constants.peach - price seller.payoff = price - buyer.nLemon*Constants.lemon - buyer.nPeach*seller.sell_peach buyer.current_offer = 0 seller.current_offer = 101 news = dict(buyer=buyer.id_in_group, seller=seller.id_in_group, price=price) bids = sorted([p.current_offer for p in buyers if p.current_offer > 0], reverse=True) asks = [dict(offer=p.current_offer, seller=p.id_in_group) for p in sellers if p.current_offer <= 100] highcharts_series = [[tx.seconds, tx.price] for tx in Transaction.filter(group=group)] return { p.id_in_group: dict( bids=bids, asks=asks, highcharts_series=highcharts_series, num_items=p.num_items, current_offer=p.current_offer, nLemon=p.nLemon, nPeach=p.nPeach, news=news, payoff=p.payoff, ) for p in players } # PAGES class Instructions(Page): @staticmethod def is_displayed(player): return player.round_number==1 @staticmethod def vars_for_template(player): return dict( pct_lemon = round(100 * Constants.pr_lemon), pct_peach = round(100 * (1 - Constants.pr_lemon)), ) class WaitToStart(WaitPage): @staticmethod def after_all_players_arrive(group: Group): group.start_timestamp = int(time.time()) class Trading(Page): live_method = live_method @staticmethod def is_displayed(player: Player): return player.round_number <= player.num_rounds @staticmethod def js_vars(player: Player): return dict(id_in_group=player.id_in_group, is_buyer=player.is_buyer) @staticmethod def get_timeout_seconds(player: Player): import time group = player.group return (group.start_timestamp + 1.5 * 60) - time.time() @staticmethod def vars_for_template(player): return dict( pct_lemon = round(100 * Constants.pr_lemon), pct_peach = round(100 * (1 - Constants.pr_lemon)), ) page_sequence = [Instructions, WaitToStart, Trading]