from otree.api import * import time import random doc = "Double auction market" class C(BaseConstants): NAME_IN_URL = 'double_auction' PLAYERS_PER_GROUP = None NUM_ROUNDS = 10 # Set the number of rounds to 10 ITEMS_PER_PLAYER = random.randint(0, 4) CASH_PER_PLAYER = 10000 DIVIDEND_PER_PLAYER = random.randint(1, 4) * 10 class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): players = subsession.get_players() for p in players: p.num_items = C.ITEMS_PER_PLAYER p.current_offer_buyer = 0 p.current_offer_seller = 0 p.cash = C.CASH_PER_PLAYER p.dividend = C.DIVIDEND_PER_PLAYER class Group(BaseGroup): start_timestamp = models.IntegerField() class Player(BasePlayer): num_items = models.IntegerField() current_offer_buyer = models.CurrencyField() current_offer_seller = models.CurrencyField() cash = models.CurrencyField() dividend = models.CurrencyField() transactions_buyer = models.CurrencyField(initial=0) transactions_seller = models.CurrencyField(initial=0) 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(players,buyers,sellers): for p in players: p.is_buyer = 0 < p.current_offer_buyer < p.cash p.is_seller = p.current_offer_seller > 0 and p.num_items > 0 for buyer in buyers: for seller in sellers: if seller.num_items > 0 and seller.current_price <= buyer.current_offer: # return as soon as we find a match (the rest of the loop will be skipped) return [buyer, seller] def live_method(player: Player, data): print('live_method called with data:', 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 p.is_seller] if match: [buyer, seller] = match price = buyer.current_offer_buyer 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.transactions_buyer += buyer.break_even_point_buyer - price seller.transactions_seller += price - seller.break_even_point_seller buyer.current_offer_buyer = 0 seller.current_offer_seller = C.VALUATION_MAX + 1 bids_buyer = sorted([p.current_offer_buyer for p in players if p.current_offer_buyer > 0], reverse=True) asks_seller = sorted([p.current_offer_seller for p in players if p.current_offer_seller <= C.VALUATION_MAX]) highcharts_series = [[tx.seconds, tx.price] for tx in Transaction.filter(group=group)] return { p.id_in_group: dict( num_items=p.num_items, current_offer_buyer=p.current_offer_buyer, current_offer_seller=p.current_offer_seller, transactions_buyer=p.transactions_buyer, transactions_seller=p.transactions_seller, bids_buyer=bids_buyer, asks_seller=asks_seller, highcharts_series=highcharts_series, ) for p in players } # PAGES 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 js_vars(player: Player): return dict( id_in_group=player.id_in_group, num_items=player.num_items, is_buyer=player.num_items > 0, ) @staticmethod def get_timeout_seconds(player: Player): group = player.group return (group.start_timestamp + 2 * 60) - time.time() class ResultsWaitPage(WaitPage): pass class Results(Page): pass page_sequence = [WaitToStart, Trading, ResultsWaitPage, Results]