def find_crit_order(self, market_id, side, holdings=None, units=1): """ Returns a buy/sell order with score-increasing critical price, for the chosen market. """ holdings = holdings if holdings else self._holdings # initialise test order and relevant variables test_o = Order(0, units, OrderType.LIMIT, side, market_id, ref=f"crit_{market_id}_{side.name}") buy = side==OrderSide.BUY sell = not buy x = 1 if sell else -1 # if order exists, iterate on current price until critical price is reached current_crit_o = self._crit_orders.get(market_id, {}).get(side) if current_crit_o: test_o.price = copy.copy(current_crit_o.price) while self.get_performance_change([test_o]) <= 0: test_o.price += x while self.get_performance_change([test_o]) > 0: test_o.price -= x test_o.price += x return test_o # else, use binary search to find solution else: price_range = [self.markets[market_id]['minimum'], self.markets[market_id]['maximum']] # solve inequality: price @ min(pchange), s.t. pchange>0 while price_range[1] - price_range[0] > 1: test_o.price = int(round(np.mean(price_range))) pchange = self.get_performance_change([test_o]) if ((pchange>0) & buy) | ((pchange<0) & sell): price_range = [test_o.price, price_range[1]] elif ((pchange>0) & sell) | ((pchange<0) & buy): price_range = [price_range[0], test_o.price] # if performance change equals zero, score-increasing order can be immediately inferred else: test_o.price = test_o.price + x return test_o test_o.price = price_range[int(sell)] return test_o
def received_order_book(self, order_book, market_id): try: # PUBLIC MARKET ACTIONS if self._public_market_id == market_id and len(order_book) > 0: self.order_housekeeping(order_book) # record and compute confint if market info is available ci = (0, self._MAX_MKT_PRICE) if len([o for o in order_book if not o.mine]) > 0: self.record_ob_data(order_book) ci = self.compute_confint() # MARKET MAKER STRATEGY if self.bot_type() == BotType.MARKET_MAKER: # if no order exists in the market, create new order if len(self._own_orders) < self._MAX_ACTIVE_ORDERS and len( self._pending_orders) == 0: # initialise order new_order = Order(0, 1, OrderType.LIMIT, OrderSide.BUY, self._public_market_id, ref='pub_order') # sell if too many assets, buy if too few assets, choose based on market price if asset number is correct is_sell = self._ob_data[-1][1] / self._MAX_MKT_PRICE if self._holdings['markets'][market_id][ 'units'] > self._ASSETS_REQ: is_sell = 1 elif self._holdings['markets'][market_id][ 'units'] < self._ASSETS_REQ: is_sell = 0 # create order based on CI self.inform( f"[PUBLIC] w{int(round(self._CONFIDENCE*100))}CI: {tuple([round(v, 3) for v in ci])}" ) if is_sell < 0.5: new_order.price = int(round(max(0, ci[0]))) else: new_order.side = OrderSide.SELL new_order.price = int( round(min(ci[1], self._MAX_MKT_PRICE))) # check that order is valid before sending if self.check_order_validity(new_order, (ci[1] - ci[0]) / 2): self.inform( f"[{self.bot_type().name}] sending valid {new_order.side.name} order @ {new_order.price}." ) self._pending_orders[new_order.ref] = new_order self.send_order(new_order) # infer bot role from private market order ## THIS ASSUMES THERE IS ONLY 1 ORDER IN THE PRIVATE MARKET AND NO NEW ORDERS ARRIVE THROUGH THE SESSION if self.role( ) == None and market_id == self._private_market_id and len( order_book) > 0: if order_book[0].side == OrderSide.BUY: self._role = Role.BUYER else: self._role = Role.SELLER except: traceback.print_exc()
def received_order_book(self, order_book, market_id): try: # order housekeeping try: # increment order age updated_ao = [o for o in order_book if o.mine][0] if self._active_orders.get(market_id) == updated_ao: self._active_order_age[ market_id] = self._active_order_age.get(market_id, 0) + 1 # unless the order is new, then make its age 1 else: self._active_orders[market_id] = updated_ao self._active_order_age[market_id] = 1 # purge order if it has stayed in the market for too long refresh_interval = (self._MM_REFRESH_INTERVAL if self.bot_type() == BotType.MARKET_MAKER else self._REACTIVE_REFRESH_INTERVAL) if self._active_order_age.get(market_id, 0) > refresh_interval: self.cancel_order(self._active_orders[market_id]) self.inform("[ORDER REFRESH] purging stagnant order.") # clear active order info if no active order is found except IndexError: self._active_orders[market_id] = None self._active_order_age[market_id] = 0 # public market actions if market_id == self._public_market_id: # stop processing if orderbook doesn't contain orders or role is not defined if len([o for o in order_book if not o.mine ]) == 0 or self.role() == None: return # if orderbook contains orders, initialise relevant variables (profit/critical prices) self.update_aggression() # the price at which profit will be made according to the minimum profit margin buy_profit = self._private_price - PROFIT_MARGIN sell_profit = self._private_price + PROFIT_MARGIN # the critical price at which desired profit (scaled using aggression) will be made buy_crit = self._private_price - self._target_profit sell_crit = self._private_price + self._target_profit # inform bot's desired market price if self.role() != None: self.inform( f"[{self.role().name}] target price: {buy_crit if self.role() == Role.BUYER else sell_crit}." ) # intialise order new_order = Order(self._private_price, 1, OrderType.LIMIT, (OrderSide.BUY if self.role() == Role.BUYER else OrderSide.SELL), self._public_market_id, ref='pub_order') # implementation for market maker and reactive bot types # set appropriate buyer price, capture arbitrage if reactive if self.role() == Role.BUYER: # find ask and assign order price so that profit will be made ask = min([ o.price for o in order_book if o.side == OrderSide.SELL and not o.mine ] + [self._MAX_MKT_PRICE]) new_order.price = buy_crit # identify all profitable trade opportunities if ask <= buy_profit: self._print_trade_opportunity(f'BUY @ {ask}.') # reactive bot: capture desirable arbitrage opportunity at market price if self.bot_type( ) == BotType.REACTIVE and ask <= buy_crit: new_order.price = ask self.send_if_valid_order(new_order) if ask >= buy_crit: self.inform( f"[{self.bot_type().name}] order is profitable but not profitable enough." ) # set appropriate seller price, capture arbitrage if reactive elif self.role() == Role.SELLER: # find bid and assign order price so that profit will be made bid = max([ o.price for o in order_book if o.side == OrderSide.BUY and not o.mine ] + [0]) new_order.price = sell_crit # identify all profitable trade opportunities if bid >= sell_profit: self._print_trade_opportunity(f'SELL @ {bid}.') # reactive bot: capture desirable arbitrage opportunity at market price if self.bot_type( ) == BotType.REACTIVE and bid >= sell_crit: new_order.price = bid self.send_if_valid_order(new_order) if bid <= sell_crit: self.inform( f"[{self.bot_type().name}] order is profitable but not profitable enough." ) # market maker bot: send order if the order is valid, regardless if arbitrage opportunity is present if self.bot_type() == BotType.MARKET_MAKER: self.send_if_valid_order(new_order) # private market actions elif market_id == self._private_market_id: try: # infer bot role from private market order po = [o for o in order_book if not o.mine][0] if po.side == OrderSide.BUY: self._role = Role.BUYER elif po.side == OrderSide.SELL: self._role = Role.SELLER # store private market price and units info self._private_price = int(po.price) self._private_units = po.units except IndexError: # on role detection failure, clear role, private price and private units self._role = None self._private_price = self._MAX_MKT_PRICE / 2 self._private_units = 0 # display tracebacks for main recurring loops to aid debugging except: traceback.print_exc()