from channels.generic.websockets import JsonWebsocketConsumer from double_auction.models import Player, Group , BaseRecord , BaseStatement, Ask, Bid, Item from double_auction.exceptions import NotEnoughFunds, NotEnoughItemsToSell from double_auction import channels_checker from django.db import models as djmodels from django.db.models import F, Q, Sum, ExpressionWrapper from django.db.models.signals import post_save, pre_save from otree.models import Participant from otree.models_concrete import ParticipantToPlayerLookup import logging logger = logging.getLogger(__name__) class MarketTracker(JsonWebsocketConsumer): url_pattern = r'^/market_channel/(?P.+)/(?P\d+)$' def clean_kwargs(self): participant = Participant.objects.get(code__exact=self.kwargs['participant_code']) cur_page_index = self.kwargs['page_index'] lookup = ParticipantToPlayerLookup.objects.get(participant=participant, page_index=cur_page_index) self.player_pk = lookup.player_pk def connection_groups(self, **kwargs): group_name = self.get_group().get_channel_group_name() personal_channel = self.get_player().get_personal_channel_name() return [group_name, personal_channel] def get_player(self): self.clean_kwargs() return Player.objects.get(pk=self.player_pk) def get_group(self): player = self.get_player() return Group.objects.get(pk=player.group.pk) def receive(self, text=None, bytes=None, **kwargs): self.clean_kwargs() msg = text player = self.get_player() group = self.get_group() # Some ideas: # Each seller in the beginning has slots (like a deposit cells) filled with goods from his repo. # Each buyer also has empty slots (deposit cells) to fill in. # Each seller slot is associated with a certain cost of production. # Each buyer slot is associated with a certain value of owning the item in it (sounds strange) # buyer costs are associated with increasing cost of production (?) # seller values with diminishing marginal value # when two persons make a contract, an item is moved from seller's cell to buyer's cell. if msg['action'] == 'new_statement': if player.role() == 'buyer': try: #print("HHHHHHHouse type:",msg['house']) bid = player.bids.create(round_statement=msg['round_statement'],price=msg['price'], quantity=msg['quantity'],quantity_initial=msg['quantity'],BTC_Statement=msg['house']) except NotEnoughFunds: logger.warning('not enough funds') else: try: ask = player.asks.create(round_statement=msg['round_statement'],price=msg['price'], quantity=msg['quantity'],quantity_initial=msg['quantity'],BTC_Statement=msg['house']) except NotEnoughItemsToSell: logger.warning('not enough items to sell') if msg['action'] == 'retract_statement': to_del = player.get_last_statement() if to_del: to_del.delete() if msg['action'] == 'retract_statement_btc': to_del = player.get_last_statement_btc() if to_del: to_del.delete() if msg['action'] == 'best_statement': if player.role() == 'buyer': asks = Ask.active_statements.filter(player__group=group, BTC_Statement=False).order_by('-price','created_at') if asks.exists(): ask = asks.last() max_quantity = (player.allocation_dollar-player.allocation_dollar%float(ask.price))/float(ask.price) quantity_for_bid = max(min(ask.quantity,max_quantity),1) try: bid = player.bids.create(round_statement=ask.round_statement,price=ask.price, quantity=quantity_for_bid,quantity_initial=quantity_for_bid,BTC_Statement=False) except: logger.warning('best ask error') elif player.role() == 'seller': print(group) bids = Bid.active_statements.filter(player__group=group, BTC_Statement=False).order_by('price','created_at') if bids.exists(): bid = bids.last() items_available = Item.objects.filter(slot__owner=player, slot__BTC=False) num_items_available = items_available.aggregate(num_items=Sum('quantity')) max_quantity= items_available.count() quantity_for_ask = max(min(bid.quantity,max_quantity),1) try: ask = player.asks.create(round_statement=bid.round_statement,price=bid.price, quantity=quantity_for_ask,quantity_initial=quantity_for_ask,BTC_Statement=False) except: logger.warning('best bid error') if msg['action'] == 'best_statement_btc': if player.role() == 'buyer': asks = Ask.active_statements.filter(player__group=group, BTC_Statement=True).order_by('-price','created_at') if asks.exists(): ask = asks.last() max_quantity = (player.allocation_btc-player.allocation_btc%float(ask.price))/float(ask.price) quantity_for_bid = max(min(ask.quantity,max_quantity),1) try: bid = player.bids.create(round_statement=ask.round_statement,price=ask.price, quantity=quantity_for_bid,quantity_initial=quantity_for_bid,BTC_Statement=True) except: logger.warning('best ask error') elif player.role() == 'seller': bids = Bid.active_statements.filter(player__group=group, BTC_Statement=True).order_by('price','created_at') if bids.exists(): bid = bids.last() items_available = Item.objects.filter(slot__owner=player, slot__BTC=True) num_items_available = items_available.aggregate(num_items=Sum('quantity')) max_quantity = items_available.count() quantity_for_ask = max(min(bid.quantity,max_quantity),1) try: ask = player.asks.create(round_statement=bid.round_statement,price=bid.price, quantity=quantity_for_ask,quantity_initial=quantity_for_ask,BTC_Statement=True) except: logger.warning('best bid error btc') spread = group.get_spread_html(False) spread_btc = group.get_spread_html(True) for p in group.get_players(): self.group_send(p.get_personal_channel_name(), {'asks': p.get_asks_html(False), 'bids': p.get_bids_html(False), 'asks_btc': p.get_asks_html(True), 'bids_btc': p.get_bids_html(True)}) self.group_send(group.get_channel_group_name(), { 'spread': spread, 'spread_btc':spread_btc }) msg = dict() last_statement = player.get_last_statement() if last_statement: msg['last_statement'] = last_statement.as_dict() last_statement_btc = player.get_last_statement_btc() if last_statement_btc: msg['last_statement_btc'] = last_statement_btc.as_dict() msg['form'] = player.get_form_html(False) msg['form_btc'] = player.get_form_html(True) self.send(msg) def connect(self, message, **kwargs): logger.info(f'Connected: {self.kwargs["participant_code"]}')