def receiveMessage(self, currentTime, msg): super().receiveMessage(currentTime, msg) if self.state == 'AWAITING_SPREAD': if msg.body['msg'] == 'QUERY_SPREAD': if self.mkt_closed: return self.placeOrder() self.state = 'AWAITING_WAKEUP' if msg.body['msg'] == "SLAVE_DELAY_RESPONSE": self.slave_delays[msg.body['sender']] = msg.body['delay'] elif msg.body['msg'] == "ORDER_EXECUTED": order = msg.body['order'] if self.send_signal == 'MASTER_ORDER_EXECUTED': for s_id in self.slave_ids: self.sendMessage(recipientID=s_id, msg=Message({"msg": "MASTER_ORDER", "sender": self.id, "symbol": self.symbol, "quantity": order.quantity, "is_buy_order": order.is_buy_order, 'limit_price': order.limit_price}), delay=self.slave_delays[s_id]) elif msg.body['msg'] == "ORDER_CANCELLED": order = msg.body['order'] for s_id in self.slave_ids: self.sendMessage(recipientID=s_id, msg=Message({"msg": "MASTER_ORDER_CANCELLED", "sender": self.id, "order": order}), delay=self.slave_delays[s_id])
def wakeup(self, currentTime): super().wakeup(currentTime) if self.first_wake: # Log initial holdings. self.logEvent('HOLDINGS_UPDATED', self.holdings) self.first_wake = False if self.mkt_open is None: # Ask our exchange when it opens and closes. self.sendMessage( self.exchangeID, Message({ "msg": "WHEN_MKT_OPEN", "sender": self.id })) self.sendMessage( self.exchangeID, Message({ "msg": "WHEN_MKT_CLOSE", "sender": self.id })) # For the sake of subclasses, TradingAgent now returns a boolean # indicating whether the agent is "ready to trade" -- has it received # the market open and closed times, and is the market not already closed. return (self.mkt_open and self.mkt_close) and not self.mkt_closed
def dispatch_message(delay, sender_id, receiver_id, msg, extra): receiver = EntityManager.get_entity_from_id(receiver_id) message = Message(sender_id, receiver_id, msg, extra) if delay <= 0.0: discharge(receiver, message) else: message.setTime(time.time() + delay) priorityQ.add(message)
def record(self, msg: Message, force_record_meta=False, notify_agents=True): record_meta = False if msg.action == ACTION_RECEIVED: key = msg.key meta = self.meta_table() if key in meta.db: del meta.db[key] elif msg.action == ACTION_WRITE: self.write(msg.path, msg.key, msg.value) if msg.dest == DEST_LOCAL: record_meta = False elif msg.dest == DEST_NODE: record_meta = not (msg.dest_node == self.node_id) elif msg.dest == DEST_PARENT: record_meta = False elif msg.dest == DEST_CHILD: record_meta = False elif msg.dest == DEST_NEIGHBORS: record_meta = False else: record_meta = True elif msg.action == ACTION_REQUEST: record_meta = True try: # If we have the requested data, create a response message with that data key, val = self.latest(msg.path) msg.key = key msg.value = val msg.action = ACTION_RESPONSE except ValueError: # Otherwise just store the message to forward it to other nodes pass elif msg.action == ACTION_RESPONSE: if (msg.dest == DEST_NODE and msg.dest_node == self.node_id) or \ (msg.dest == DEST_UPLINK and self.is_uplink): self.write(msg.path, msg.key, msg.value) if record_meta or force_record_meta: self._record_meta(msg) if notify_agents: self.notify_agents(msg)
def placeLimitOrder (self, symbol, quantity, is_buy_order, limit_price): order = LimitOrder(self.id, self.currentTime, symbol, quantity, is_buy_order, limit_price) if quantity > 0: # Test if this order can be permitted given our at-risk limits. new_holdings = self.holdings.copy() q = order.quantity if order.is_buy_order else -order.quantity if order.symbol in new_holdings: new_holdings[order.symbol] += q else: new_holdings[order.symbol] = q # Compute before and after at-risk capital. at_risk = self.markToMarket(self.holdings) - self.holdings['CASH'] new_at_risk = self.markToMarket(new_holdings) - new_holdings['CASH'] # If at_risk is lower, always allow. Otherwise, new_at_risk must be below starting cash. if (new_at_risk > at_risk) and (new_at_risk > self.startingCash): print ("TradingAgent ignored limit order due to at-risk constraints: {}\n{}".format(order, self.fmtHoldings(self.holdings))) return self.orders[order.order_id] = deepcopy(order) self.sendMessage(self.exchangeID, Message({ "msg" : "LIMIT_ORDER", "sender": self.id, "order" : order })) # Log this activity. self.logEvent('ORDER_SUBMITTED', order) else: print ("TradingAgent ignored limit order of quantity zero: {}".format(order))
def modifyOrder(self, order, new_order): book = self.bids if order.is_buy_order else self.asks if not book: return for i, o in enumerate(book): if self.isEqualPrice(order, o[0]): for mi, mo in enumerate(book[i]): if order.order_id == mo.order_id: book[i][0] = new_order for idx, orders in enumerate(self.history): if new_order.order_id not in orders: continue self.history[idx][ new_order.order_id]['transactions'].append( (self.owner.currentTime, new_order.quantity)) print("MODIFIED: order {}".format(order)) print( "SENT: notifications of order modification to agent {} for order {}" .format(new_order.agent_id, new_order.order_id)) self.owner.sendMessage( order.agent_id, Message({ "msg": "ORDER_MODIFIED", "new_order": new_order })) if order.is_buy_order: self.bids = book else: self.asks = book self.updateOrderbookLevelDicts()
def cancelDataSubscription(self, symbol): self.sendMessage(recipientID=self.exchangeID, msg=Message({ "msg": "MARKET_DATA_SUBSCRIPTION_CANCELLATION", "sender": self.id, "symbol": symbol }))
def run(self): with self._socket: try: while True: req = None try: req = Message.recv_via(self._socket) logger.info('Received message: ' + str(req)) req.process() if isinstance(req, Message): req.respond_via(self._socket) else: raise RuntimeError('Unknown message type: ' + str(req.type)) except (OSError, BrokenPipeError, ssl.SSLError) as e: # pass up raise except Exception as e: # catch everything else tb = ''.join(traceback.format_tb(sys.exc_info()[2])) logger.warning('Message ' + str(req) + ' execution produced exception: ' + str(e) + tb) resp = ExceptionMessage({'exception': e, 'traceback': tb}) resp.send_via(self._socket) except (ssl.SSLError) as e: logger.info('Unable to secure connection to ' + str(self._address) + ', closing...' + str(e)) self._socket.shutdown(socket.SHUT_RDWR) except (OSError, BrokenPipeError) as e: logger.info('Socket connection broken with ' + str(self._address) + ' due to ' + str(e)) else: logger.info('Connection to ' + str(self._address) + ' closing...') self._socket.shutdown(socket.SHUT_RDWR) logger.info('Connection to ' + str(self._address) + ' closed')
def test_ping(s): # test Ping/Pong Messages req = PingMessage() req.send_via(s) resp = Message.recv_via(s) assert (isinstance(resp, PongMessage)) assert (req.payload == resp.payload)
def test_facts(s): req = FactsRequestMessage() req.send_via(s) resp = Message.recv_via(s) assert(isinstance(resp, FactsResponseMessage)) assert(isinstance(resp.payload, dict)) assert('os.name' in resp.payload)
def wakeup(self, currentTime): super().wakeup(currentTime) self.state = 'INACTIVE' if not self.mkt_open or not self.mkt_close: for s_id in self.slave_ids: self.sendMessage(recipientID=s_id, msg=Message({"msg": "SLAVE_DELAY_REQUEST", "sender": self.id})) return else: if not self.trading: self.trading = True self.oracle.compute_fundamental_value_series(self.symbol, currentTime, sigma_n=0, random_state=self.random_state) log_print("{} is ready to start trading now.", self.name) if self.mkt_closed and (self.symbol in self.daily_close_price): return if self.mkt_closed and (self.symbol not in self.daily_close_price): self.getCurrentSpread(self.symbol) self.state = 'AWAITING_SPREAD' return self.cancelOrders() if type(self) == HerdMasterAgent: self.getCurrentSpread(self.symbol) self.state = 'AWAITING_SPREAD' else: self.state = 'ACTIVE'
def wakeup (self, currentTime): super().wakeup(currentTime) if self.first_wake: # Log initial holdings. #self.logEvent('HOLDINGS_UPDATED', self.fmtHoldings(self.holdings)) self.logEvent('HOLDINGS_UPDATED', self.holdings) self.first_wake = False if self.mkt_open is None: # Ask our exchange when it opens and closes. self.sendMessage(self.exchangeID, Message({ "msg" : "WHEN_MKT_OPEN", "sender": self.id })) self.sendMessage(self.exchangeID, Message({ "msg" : "WHEN_MKT_CLOSE", "sender": self.id })) # New for MomentumAgent. return (self.mkt_open and self.mkt_close) and not self.mkt_closed
def test_ping(s): # test Ping/Pong Messages req = PingMessage() req.send_via(s) resp = Message.recv_via(s) assert(isinstance(resp, PongMessage)) assert(req.payload == resp.payload)
def modifyOrder(self, order, new_order): # Modifies the quantity of an existing limit order in the order book if not self.isSameOrder(order, new_order): return book = self.bids if order.is_buy_order else self.asks if not book: return for i, o in enumerate(book): if self.isEqualPrice(order, o[0]): for mi, mo in enumerate(book[i]): if order.order_id == mo.order_id: book[i][0] = new_order for idx, orders in enumerate(self.history): if new_order.order_id not in orders: continue self.history[idx][ new_order.order_id]['modifications'].append( (self.owner.currentTime, new_order.quantity)) log_print("MODIFIED: order {}", order) log_print( "SENT: notifications of order modification to agent {} for order {}", new_order.agent_id, new_order.order_id) self.owner.sendMessage( order.agent_id, Message({ "msg": "ORDER_MODIFIED", "new_order": new_order })) if order.is_buy_order: self.bids = book else: self.asks = book self.last_update_ts = self.owner.currentTime
def placeLimitOrder (self, symbol, quantity, is_buy_order, limit_price, order_id=None, ignore_risk = True): order = LimitOrder(self.id, self.currentTime, symbol, quantity, is_buy_order, limit_price, order_id) if quantity > 0: # Test if this order can be permitted given our at-risk limits. new_holdings = self.holdings.copy() q = order.quantity if order.is_buy_order else -order.quantity if order.symbol in new_holdings: new_holdings[order.symbol] += q else: new_holdings[order.symbol] = q # Compute before and after at-risk capital. at_risk = self.markToMarket(self.holdings) - self.holdings['CASH'] new_at_risk = self.markToMarket(new_holdings) - new_holdings['CASH'] # If at_risk is lower, always allow. Otherwise, new_at_risk must be below starting cash. if not ignore_risk: if (new_at_risk > at_risk) and (new_at_risk > self.starting_cash): log_print ("TradingAgent ignored limit order due to at-risk constraints: {}\n{}", order, self.fmtHoldings(self.holdings)) return # Copy the intended order for logging, so any changes made to it elsewhere # don't retroactively alter our "as placed" log of the order. Eventually # it might be nice to make the whole history of the order into transaction # objects inside the order (we're halfway there) so there CAN be just a single # object per order, that never alters its original state, and eliminate all these copies. self.orders[order.order_id] = deepcopy(order) self.sendMessage(self.exchangeID, Message({ "msg" : "LIMIT_ORDER", "sender": self.id, "order" : order })) # Log this activity. if self.log_orders: self.logEvent('ORDER_SUBMITTED', js.dump(order)) else: log_print ("TradingAgent ignored limit order of quantity zero: {}", order)
def publishOrderBookData(self): ''' The exchange agents sends an order book update to the agents using the subscription API if one of the following conditions are met: 1) agent requests ALL order book updates (freq == 0) 2) order book update timestamp > last time agent was updated AND the orderbook update time stamp is greater than the last agent update time stamp by a period more than that specified in the freq parameter. ''' for agent_id, params in self.subscription_dict.items(): for symbol, values in params.items(): levels, freq, last_agent_update = values[0], values[1], values[ 2] orderbook_last_update = self.order_books[symbol].last_update_ts if (freq == 0) or \ ((orderbook_last_update > last_agent_update) and ((orderbook_last_update - last_agent_update).delta >= freq)): self.sendMessage( agent_id, Message({ "msg": "MARKET_DATA", "symbol": symbol, "bids": self.order_books[symbol].getInsideBids(levels), "asks": self.order_books[symbol].getInsideAsks(levels), "last_transaction": self.order_books[symbol].last_trade })) self.subscription_dict[agent_id][symbol][ 2] = orderbook_last_update
def getLastTrade(self, symbol): self.sendMessage( self.exchangeID, Message({ "msg": "QUERY_LAST_TRADE", "sender": self.id, "symbol": symbol }))
def requestDataSubscription(self, symbol, levels, freq): self.sendMessage(recipientID=self.exchangeID, msg=Message({ "msg": "MARKET_DATA_SUBSCRIPTION_REQUEST", "sender": self.id, "symbol": symbol, "levels": levels, "freq": freq }))
def test_returncode(s): req = RunProcessMessage({ 'args': 'exit 1', 'shell': True, }) req.send_via(s) resp = Message.recv_via(s) assert(resp.payload['returncode'] == 1)
def cancelOrder(self, order): # Attempts to cancel (the remaining, unexecuted portion of) a trade in the order book. # By definition, this pretty much has to be a limit order. If the order cannot be found # in the order book (probably because it was already fully executed), presently there is # no message back to the agent. This should possibly change to some kind of failed # cancellation message. (?) Otherwise, the agent receives ORDER_CANCELLED with the # order as the message body, with the cancelled quantity correctly represented as the # number of shares that had not already been executed. if order.is_buy_order: book = self.bids else: book = self.asks # If there are no orders on this side of the book, there is nothing to do. if not book: return # There are orders on this side. Find the price level of the order to cancel, # then find the exact order and cancel it. # Note that o is a LIST of all orders (oldest at index 0) at this same price. for i, o in enumerate(book): if self.isEqualPrice(order, o[0]): # This is the correct price level. for ci, co in enumerate(book[i]): if order.order_id == co.order_id: # Cancel this order. cancelled_order = book[i].pop(ci) # Record cancellation of the order if it is still present in the recent history structure. for idx, orders in enumerate(self.history): if cancelled_order.order_id not in orders: continue # Found the cancelled order in history. Update it with the cancelation. self.history[idx][cancelled_order.order_id][ 'cancellations'].append( (self.owner.currentTime, cancelled_order.quantity)) # If the cancelled price now has no orders, remove it completely. if not book[i]: del book[i] print("CANCELLED: order {}".format(order)) print( "SENT: notifications of order cancellation to agent {} for order {}" .format(cancelled_order.agent_id, cancelled_order.order_id)) self.owner.sendMessage( order.agent_id, Message({ "msg": "ORDER_CANCELLED", "order": cancelled_order })) # We found the order and cancelled it, so stop looking. return
def getCurrentSpread(self, symbol, depth=1): self.sendMessage( self.exchangeID, Message({ "msg": "QUERY_SPREAD", "sender": self.id, "symbol": symbol, "depth": depth }))
def getOrderStream(self, symbol, length=1): self.sendMessage( self.exchangeID, Message({ "msg": "QUERY_ORDER_STREAM", "sender": self.id, "symbol": symbol, "length": length }))
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 test_reg_query(s): req = RunProcessMessage({ 'args': 'REG QUERY HKLM\Software\Microsoft\Windows\CurrentVersion /v ProgramFilesDir', }) req.send_via(s) resp = Message.recv_via(s) assert(resp.payload['returncode'] == 0) assert(b'Program Files' in resp.payload['stdout']) assert(resp.payload['stderr'] == b'')
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 test_timeout(s): req = RunProcessMessage({ 'args': 'pause', 'shell': True, 'timeout': 1, }) req.send_via(s) resp = Message.recv_via(s) assert(isinstance(resp, ExceptionMessage))
def get_transacted_volume(self, symbol, lookback_period='10min'): """ Used by any trading agent subclass to query the total transacted volume in a given lookback period """ self.sendMessage( self.exchangeID, Message({ "msg": "QUERY_TRANSACTED_VOLUME", "sender": self.id, "symbol": symbol, "lookback_period": lookback_period }))
def cancelOrder(self, order): self.sendMessage( self.exchangeID, Message({ "msg": "CANCEL_ORDER", "sender": self.id, "order": order })) # Log this activity. if self.log_orders: self.logEvent('CANCEL_SUBMITTED', js.dump(order))
def modifyOrder(self, order, newOrder): self.sendMessage( self.exchangeID, Message({ "msg": "MODIFY_ORDER", "sender": self.id, "order": order, "new_order": newOrder })) # Log this activity. if self.log_orders: self.logEvent('MODIFY_ORDER', js.dump(order))
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 placeMarketOrder(self, symbol, quantity, is_buy_order, order_id=None, ignore_risk=True, tag=None): """ Used by any Trading Agent subclass to place a market order. The market order is created as multiple limit orders crossing the spread walking the book until all the quantities are matched. :param symbol (str): name of the stock traded :param quantity (int): order quantity :param is_buy_order (bool): True if Buy else False :param order_id: Order ID for market replay :param ignore_risk (bool): Determines whether cash or risk limits should be enforced or ignored for the order :return: """ order = MarketOrder(self.id, self.currentTime, symbol, quantity, is_buy_order, order_id) if quantity > 0: # compute new holdings new_holdings = self.holdings.copy() q = order.quantity if order.is_buy_order else -order.quantity if order.symbol in new_holdings: new_holdings[order.symbol] += q else: new_holdings[order.symbol] = q if not ignore_risk: # Compute before and after at-risk capital. at_risk = self.markToMarket( self.holdings) - self.holdings['CASH'] new_at_risk = self.markToMarket( new_holdings) - new_holdings['CASH'] if (new_at_risk > at_risk) and (new_at_risk > self.starting_cash): log_print( "TradingAgent ignored market order due to at-risk constraints: {}\n{}", order, self.fmtHoldings(self.holdings)) return self.orders[order.order_id] = deepcopy(order) self.sendMessage( self.exchangeID, Message({ "msg": "MARKET_ORDER", "sender": self.id, "order": order })) if self.log_orders: self.logEvent('ORDER_SUBMITTED', order.to_dict()) else: log_print("TradingAgent ignored market order of quantity zero: {}", order)
def placeOrder(self): delta = pd.Timedelta(self.future_window, unit='ns') if self.currentTime+delta < self.mkt_close: self.setWakeup(self.currentTime + delta) r_f = self.oracle.observeFuturePrice(self.symbol, self.currentTime + delta, sigma_n=self.sigma_n, random_state=self.random_state) bid, bid_vol, ask, ask_vol = self.getKnownBidAsk(self.symbol) if bid and ask: spread = abs(ask - bid) if np.random.rand() < self.percent_aggr: adjust_int = 0 else: adjust_int = np.random.randint(0, self.depth_spread*spread) if ask < r_f: buy = True p = ask - adjust_int size = self.getHoldings(self.symbol)*(-1) if self.getHoldings(self.symbol) < 0 else self.size if p >= r_f: return elif bid > r_f: buy = False p = bid + adjust_int size = self.getHoldings(self.symbol) if self.getHoldings(self.symbol) > 0 else self.size if p <= r_f: return else: return else: return if self.currentTime+delta < self.mkt_close: order_type = np.random.choice(['limit', 'market']) if self.strategy == 'mixed' else self.strategy if order_type == 'limit': self.placeLimitOrder(self.symbol, size, buy, p) else: p = 0 self.placeMarketOrder(self.symbol, size, buy) if self.send_signal == 'MASTER_ORDER_PLACED': for s_id in self.slave_ids: self.sendMessage(recipientID=s_id, msg=Message({"msg": "MASTER_ORDER", "sender": self.id, "symbol": self.symbol, "quantity": size, "is_buy_order": buy, 'limit_price': p}), delay=self.slave_delays[s_id])
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 test_response(s): req = RunProcessMessage({ 'args': 'exit', 'shell': True, }) req.send_via(s) resp = Message.recv_via(s) assert(isinstance(resp, CompletedProcessMessage)) assert(isinstance(resp.payload, dict)) assert('args' in resp.payload) assert('returncode' in resp.payload) assert('stdout' in resp.payload) assert('stderr' in resp.payload)
def test_valid(): try: with ctx.wrap_socket( socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_hostname=socket.gethostbyaddr(HOST)[0] ) as s: s.connect((HOST, PORT)) print(str(s.getpeercert())) req = PingMessage() req.send_via(s) resp = Message.recv_via(s) assert(isinstance(resp, PongMessage)) assert(req.payload == resp.payload) s.shutdown(socket.SHUT_RDWR) except ssl.CertificateError: assert(False)