def getLastTrade(self, symbol): self.sendMessage( self.exchangeID, Message({ "msg": "QUERY_LAST_TRADE", "sender": self.id, "symbol": symbol }))
def wakeup(self, currentTime): # Allow the base Agent to do whatever it needs to. super().wakeup(currentTime) # This agent only needs one wakeup call at simulation start. At this time, # each client agent will send a number to each agent in its peer list. # Each number will be sampled independently. That is, client agent 1 will # send n2 to agent 2, n3 to agent 3, and so forth. # Once a client agent has received these initial random numbers from all # agents in the peer list, it will make its first request from the sum # service. Afterwards, it will simply request new sums when answers are # delivered to previous queries. # At the first wakeup, initiate peer exchange. if not self.peer_exchange_complete: n = [self.random_state.randint(low=0, high=100) for i in range(len(self.peer_list))] log_print("agent {} peer list: {}", self.id, self.peer_list) log_print("agent {} numbers to exchange: {}", self.id, n) for idx, peer in enumerate(self.peer_list): self.sendMessage(peer, Message({"msg": "PEER_EXCHANGE", "sender": self.id, "n": n[idx]})) else: # For subsequent (self-induced) wakeups, place a sum query. n1, n2 = [self.random_state.randint(low=0, high=100) for i in range(2)] log_print("agent {} transmitting numbers {} and {} with peer sum {}", self.id, n1, n2, self.peer_sum) # Add the sum of the peer exchange values to both numbers. n1 += self.peer_sum n2 += self.peer_sum self.sendMessage(self.serviceAgentID, Message({"msg": "SUM_QUERY", "sender": self.id, "n1": n1, "n2": n2})) return
def processSum(self): current_sum = sum([x[0] + x[1] for x in self.numbers.values()]) self.total += current_sum log_print("Agent {} computed sum: {}", self.id, current_sum) for sender in self.numbers.keys(): self.sendMessage( sender, Message({ "msg": "SUM_QUERY_RESPONSE", "sender": self.id, "sum": current_sum }))
def wakeup(self, currentTime): super().wakeup(currentTime) if self.mkt_close is None: # Ask our exchange when it opens and closes. self.sendMessage( self.exchangeID, Message({ "msg": "WHEN_MKT_CLOSE", "sender": self.id })) else: # Get close price of ETF/nav self.getLastTrade(self.symbol)
def wakeup(self, currentTime): # Parent class handles discovery of exchange times and market_open wakeup call. super().wakeup(currentTime) # Only if the superclass leaves the state as ACTIVE should we proceed with our # trading strategy. if self.state != 'ACTIVE': return if not self.prime_open: # Ask our primary when it opens and closes, exchange is handled in TradingAgent self.sendMessage(self.primeID, Message({"msg": "WHEN_PRIME_OPEN", "sender": self.id})) self.sendMessage(self.primeID, Message({"msg": "WHEN_PRIME_CLOSE", "sender": self.id})) # Steady state wakeup behavior starts here. if not self.mkt_closed and self.prime_closed: print('The prime closed before the exchange') sys.exit() elif self.mkt_closed and self.prime_closed: return # If we've been told the market has closed for the day, we will only request # final price information, then stop. # If the market has closed and we haven't obtained the daily close price yet, # do that before we cease activity for the day. Don't do any other behavior # after market close. elif self.mkt_closed and not self.prime_closed: if self.switched_mkt and self.currentTime >= self.prime_open: self.getEtfNav() self.state = 'AWAITING_NAV' elif not self.switched_mkt: for i, s in enumerate(self.portfolio): if s not in self.daily_close_price: self.getLastTrade(s) self.state = 'AWAITING_LAST_TRADE' if 'ETF' not in self.daily_close_price: self.getLastTrade('ETF') self.state = 'AWAITING_LAST_TRADE' print('holdings before primary: ' + str(self.holdings)) self.setWakeup(self.prime_open) self.switched_mkt = True else: self.setWakeup(self.prime_open) return # Schedule a wakeup for the next time this agent should arrive at the market # (following the conclusion of its current activity cycle). # We do this early in case some of our expected message responses don't arrive. # Agents should arrive according to a Poisson process. This is equivalent to # each agent independently sampling its next arrival time from an exponential # distribution in alternate Beta formation with Beta = 1 / lambda, where lambda # is the mean arrival rate of the Poisson process. else: delta_time = self.random_state.exponential(scale=1.0 / self.lambda_a) self.setWakeup(currentTime + pd.Timedelta('{}ns'.format(int(round(delta_time))))) # Issue cancel requests for any open orders. Don't wait for confirmation, as presently # the only reason it could fail is that the order already executed. (But requests won't # be generated for those, anyway, unless something strange has happened.) self.cancelOrders() # The ETF arb agent DOES try to maintain a zero position, so there IS need to exit positions # as some "active trading" agents might. It might exit a position based on its order logic, # but this will be as a natural consequence of its beliefs... but it submits marketable orders so... # If the calling agent is a subclass, don't initiate the strategy section of wakeup(), as it # may want to do something different. # FIGURE OUT WHAT TO DO WITH MULTIPLE SPREADS... for i, s in enumerate(self.portfolio): self.getCurrentSpread(s) self.getCurrentSpread('ETF') self.state = 'AWAITING_SPREAD'
def placeBasketOrder(self, quantity, is_create_order): order = BasketOrder(self.id, self.currentTime, 'ETF', quantity, is_create_order) print('BASKET ORDER PLACED: ' + str(order)) self.sendMessage(self.primeID, Message({"msg": "BASKET_ORDER", "sender": self.id, "order": order})) self.state = 'AWAITING_BASKET'
def getEtfNav(self): self.sendMessage(self.primeID, Message({"msg": "QUERY_NAV", "sender": self.id}))
def receiveMessage(self, currentTime, msg): super().receiveMessage(currentTime, msg) # Unless the intent of an experiment is to examine computational issues within an Exchange, # it will typically have either 1 ns delay (near instant but cannot process multiple orders # in the same atomic time unit) or 0 ns delay (can process any number of orders, always in # the atomic time unit in which they are received). This is separate from, and additional # to, any parallel pipeline delay imposed for order book activity. # Note that computation delay MUST be updated before any calls to sendMessage. self.setComputationDelay(self.computation_delay) # Is the exchange closed? (This block only affects post-close, not pre-open.) if currentTime > self.prime_close: # Most messages after close will receive a 'PRIME_CLOSED' message in response. log_print("{} received {}, discarded: prime is closed.", self.name, msg.body['msg']) self.sendMessage(msg.body['sender'], Message({"msg": "PRIME_CLOSED"})) # Don't do any further processing on these messages! return if msg.body['msg'] == "WHEN_MKT_CLOSE": self.mkt_close = msg.body['data'] log_print("Recorded market close: {}", self.kernel.fmtTime(self.mkt_close)) self.setWakeup(self.mkt_close) return elif msg.body['msg'] == 'QUERY_LAST_TRADE': # Call the processQueryLastTrade method. self.queryLastTrade(msg.body['symbol'], msg.body['data']) return self.logEvent(msg.body['msg'], msg.body['sender']) # Handle all message types understood by this exchange. if msg.body['msg'] == "WHEN_PRIME_OPEN": log_print("{} received WHEN_PRIME_OPEN request from agent {}", self.name, msg.body['sender']) # The exchange is permitted to respond to requests for simple immutable data (like "what are your # hours?") instantly. This does NOT include anything that queries mutable data, like equity # quotes or trades. self.setComputationDelay(0) self.sendMessage( msg.body['sender'], Message({ "msg": "WHEN_PRIME_OPEN", "data": self.prime_open })) elif msg.body['msg'] == "WHEN_PRIME_CLOSE": log_print("{} received WHEN_PRIME_CLOSE request from agent {}", self.name, msg.body['sender']) # The exchange is permitted to respond to requests for simple immutable data (like "what are your # hours?") instantly. This does NOT include anything that queries mutable data, like equity # quotes or trades. self.setComputationDelay(0) self.sendMessage( msg.body['sender'], Message({ "msg": "WHEN_PRIME_CLOSE", "data": self.prime_close })) elif msg.body['msg'] == "QUERY_NAV": log_print("{} received QUERY_NAV ({}) request from agent {}", self.name, msg.body['sender']) # Return the NAV for the requested symbol. self.sendMessage( msg.body['sender'], Message({ "msg": "QUERY_NAV", "nav": self.nav, "prime_closed": True if currentTime > self.prime_close else False })) elif msg.body['msg'] == "BASKET_ORDER": order = msg.body['order'] log_print("{} received BASKET_ORDER: {}", self.name, order) if order.is_buy_order: self.create += 1 else: self.redeem += 1 order.fill_price = self.nav self.sendMessage( msg.body['sender'], Message({ "msg": "BASKET_EXECUTED", "order": order }))