def order_accepted(self, order: Order): self.inform( f"{order.order_side} for {order.units}@{order.price} was accepted") self._waiting_for_order = False all_orders = list(Order.all().values()) my_current_orders = [o for o in all_orders if o.is_pending and o.mine] #allow market maker orders to remain pending for 2 seconds if self._bot_type == BotType.MARKET_MAKER and not order.is_private: time.sleep(2) #if there is still a pending order, cancel it. if order in my_current_orders: cancel_order = self.make_cancel_order(order) self.send_order(cancel_order) self._waiting_for_order = True else: #otherwise, act in the private market to arbitrage if not order.is_private and order.order_type != OrderType.CANCEL: if self._role == Role.BUYER: oside = OrderSide.SELL else: oside = OrderSide.BUY match_order = self.make_order(self._private_market_id, oside, \ self._standing_priv_order.price) self.send_order(match_order) self._waiting_for_order = True
def received_orders(self, orders: List[Order]): # Track best bids/asks for reactive strategy, this is tracked # throughout the session in case is_portfolio optimal is called current_orders = [o for o in Order.all().values() if o.is_pending] for market_id, market_info in sorted(self.markets.items()): orders_in_market = [] security = market_info.item for order in current_orders: if order.market.fm_id == market_id: orders_in_market.append(order) self._best_bid_dict[security] = self._get_best_bid(orders_in_market) self._best_ask_dict[security] = self._get_best_ask(orders_in_market) my_orders = [o for o in Order.current().values() if o.mine and o.is_pending] # Clear stale orders (based on order depth) when running reative strategy if self._reactive_condition and len(my_orders) != 0: for o in my_orders: if self._find_order_depth(o, current_orders) > 2 and \ not self._waiting_for_order: cancel_order = self._make_cancel_order(o) self.send_order(cancel_order) self._waiting_for_order = True # Clear order book if there are unmet orders only when the MM strategy is running if self._mm_condition() and len(my_orders) != 0: self._clear_orders()
def _categorize_orders(self, item): """ Categorizes orders in 4 groups: {Mine, Others} and {Buy, Sell}. :param orders: List of orders :return: a dictionary with top level key for the owner, and second level key for the order side. The values are sorted list of orders. """ # Uses a dictionary mapping from a string to a list within a dicitonary # mapping a string to a string to retrieve orders in one of the four # categories. orders_dict = { "mine": { "buy": [], "sell": [] }, "others": { "buy": [], "sell": [] } } for order in Order.all().values(): # Make sure to exclude cancel orders order_item = order.market.item if order.order_type == OrderType.LIMIT and order.is_pending and \ order_item == item: if order.mine and order.order_side == OrderSide.BUY: orders_dict["mine"]["buy"].append(order) elif order.mine and order.order_side == OrderSide.SELL: orders_dict["mine"]["sell"].append(order) elif not order.mine and order.order_side == OrderSide.SELL: orders_dict["others"]["sell"].append(order) elif not order.mine and order.order_side == OrderSide.BUY: orders_dict["others"]["buy"].append(order) # IMPORTANT! Sort the orders to make it easier to reason on what to do. for owner_key in orders_dict.keys(): for type_key in orders_dict[owner_key]: # multiplier ensures the sorting is correct for the buy ands sell side if type_key == "buy": multiplier = -1 else: multiplier = 1 orders_dict[owner_key][type_key].sort( key=lambda o: multiplier * o.price) return orders_dict
def _clear_orders(self): """ Clears all pending orders in the order book that are mine """ all_orders = Order.all().values() my_orders = [o for o in all_orders if o.mine and o.is_pending] self._order_count = len(my_orders) if self._order_count != 0: for order in my_orders: if not self._waiting_for_order: cancel_order = self._make_cancel_order(order) self.send_order(cancel_order) self._waiting_for_order = True
def received_orders(self, orders: List[Order]): all_orders = list(Order.all().values()) current_orders = [order for order in all_orders if order.is_pending] #update best bid/ask if a cancel order is detected for order in orders: if order.order_type == OrderType.CANCEL: #self.inform('Cancel order') best_bid = self._get_best_bid(all_orders) best_ask = self._get_best_ask(all_orders) return #update standing private order and role priv_orders = [order for order in all_orders if order.is_private and \ order.is_pending] if priv_orders: self._standing_priv_order = priv_orders[0] #self.inform("private order updated") self.role(self._standing_priv_order) else: self._standing_priv_order = None #self.inform("no private order, do not trade") return #Obtaining best bid/ask best_bid = self._get_best_bid(current_orders) best_ask = self._get_best_ask(current_orders) if best_bid == None and best_ask == None: self.inform("No best ask and best bid, do nothing") return elif self._role == Role.SELLER and best_bid == None: self.inform("Is seller, no best bid, do nothing") return elif self._role == Role.BUYER and best_ask == None: self.inform("Is buyer, no best ask, do nothing") return best_bid_price = best_bid.price if best_bid else None best_ask_price = best_ask.price if best_ask else None priv_price = self._standing_priv_order.price #Printing possible trade opportunities, not necessarily acting on them if (self._role == Role.SELLER and best_bid_price > priv_price + \ PROFIT_MARGIN) or (self._role == Role.BUYER and best_ask_price < \ priv_price - PROFIT_MARGIN): self._print_trade_opportunity(best_ask) #Pending order check if not self._check_existing_order(current_orders): my_order = [order for order in current_orders if order.mine] if len(my_order) > 0: self.inform(f"Cannot trade, {len(my_order)} pending order(s)") return else: return #Market Maker strategy if self._bot_type == BotType.MARKET_MAKER: #if seller and no pending orders if self._role == Role.SELLER and not self._waiting_for_order and \ self._check_existing_order(current_orders): #send sell order at standing private price + profit margin new_order = self.make_order(self._public_market_id, \ OrderSide.SELL, priv_price + PROFIT_MARGIN) if self._check_holdings(new_order, self.holdings): self.send_order(new_order) self._waiting_for_order = True #if buyer and no pending order elif self._role == Role.BUYER and not self._waiting_for_order and \ self._check_existing_order(current_orders): new_order = self.make_order(self._public_market_id, \ OrderSide.BUY, priv_price - PROFIT_MARGIN) #send by order at standing private price - profit margin if self._check_holdings(new_order, self.holdings): self.send_order(new_order) self._waiting_for_order = True if best_ask_price == None or best_bid_price == None: return #Reactive strategy if self._bot_type == BotType.REACTIVE: if self._role == Role.SELLER and best_bid_price > priv_price + \ PROFIT_MARGIN: #send sell order at best bid in public market #if best bid is a profitable opportunity if not self._waiting_for_order and \ self._check_existing_order(current_orders): new_order = self.make_order(self._public_market_id, \ OrderSide.SELL, best_bid_price) if self._check_holdings(new_order, self.holdings): self.send_order(new_order) self._waiting_for_order = True elif self._role == Role.BUYER and best_ask_price < priv_price - \ PROFIT_MARGIN: #send buy order at best ask in public market #if best ask is a profitable opportunity if not self._waiting_for_order and self._check_existing_order( current_orders): new_order = self.make_order(self._public_market_id, \ OrderSide.BUY, best_ask_price) if self._check_holdings(new_order, self.holdings): self.send_order(new_order) self._waiting_for_order = True else: self.inform("No profitable trading opportunities")
def _make_market(self): """ Market Making Strategy Parts: 1. Iterate over all orders, track number of active public orders by me 2. Track active private orders 3. Cancel stale orders 4. If private order by manager exists, and no public orders by me, create one """ num_pending_orders = 0 num_manager_order = 0 manager_order = None for _, order in Order.all().items(): # 1. if i have a current order in the public market if order.mine and not order.is_private and not order.is_consumed: num_pending_orders += 1 # 3. cancel stale orders in public market if not self._priv_order_exists and not self._waiting_for_server: cancel_order = copy.copy(order) cancel_order.ref = f"Cancel {order.ref}" cancel_order.order_type = OrderType.CANCEL self.send_order(cancel_order) self._waiting_for_server = True self._sent_order_count -= 1 # 2. track active private orders # SIMULATION # if order.is_private and not order.mine: if order.is_private and not order.is_consumed and not order.mine: # set role of bot if order.order_side == OrderSide.BUY: self._role = Role.BUYER else: self._role = Role.SELLER num_manager_order += 1 manager_order = order # 4. if 0 mine orders in public, and not waiting for server, and there is a # private order to be serviced if num_pending_orders == 0 and not self._waiting_for_server \ and num_manager_order > 0: is_private = False # PUBLIC ORDER SIDE - Determine attributes of order to be created========== market = self._public_market_id if self._role == Role.BUYER: price = manager_order.price - PROFIT_MARGIN order_side = OrderSide.BUY else: price = manager_order.price + PROFIT_MARGIN order_side = OrderSide.SELL units = 1 order_type = OrderType.LIMIT ref = f"Order: {self._sent_order_count} - SM" self._create_new_order(market, price, units, order_side, order_type, ref, is_private) # PRIVATE ORDER SIDE - Determine attributes of order to be created======== # flip attributes is_private = True market = self._private_market_id price = manager_order.price if self._role == Role.BUYER: order_side = OrderSide.SELL else: order_side = OrderSide.BUY units = 1 order_type = OrderType.LIMIT ref = f"Order: {self._sent_order_count} - SM" self._create_new_order(market, price, units, order_side, order_type, ref, is_private)