def _get_best_bid_ask(): """ Determine and return best active bid and asks in the order book :return: best_bid: best buy price best_ask: best sell price best_ask_order: best bid order object best_bid_order: best ask order object """ # initial bid and asks unrealistic / out of bound best_bid = 0 best_ask = 999999 best_ask_order = None best_bid_order = None # run through the order book, determine best bid and asks for fm_id, order in Order.current().items(): if not order.is_consumed and not order.is_private: if order.order_side == OrderSide.BUY and order.price > best_bid: best_bid = order.price best_bid_order = order elif order.order_side == OrderSide.SELL and order.price < best_ask: best_ask = order.price best_ask_order = order return best_bid, best_ask, best_bid_order, best_ask_order
def _make_market(self, priv_order: Order): """ Implements market-maker functionality Public order price determination - private order price - Profit margin for buying private order price + Profit margin for selling """ self._pending_order = False # ensure we don't have an already active/pending order for _, order in Order.current().items(): if order.mine and not order.is_consumed and not order.is_private: self._pending_order = True # PUBLIC MARKET SIDE ====================================================== market = self._public_market_id if self._role == Role.BUYER: price = priv_order.price - PROFIT_MARGIN order_side = OrderSide.BUY else: price = priv_order.price + PROFIT_MARGIN order_side = OrderSide.SELL # fixed attributes units = 1 order_type = OrderType.LIMIT ref = f"Order: {self._sent_order_count} - SM" # submit order if not self._pending_order and not self._waiting_for_server: self._create_new_order(market, price, units, order_side, order_type, ref)
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 received_orders(self, orders: List[Order]): """ Subscribed to Order book updates :param orders: list of order objects """ for order in orders: self.inform(order) # pending order in private market # even if order is partially filled out, it returns 3 entries # the buy for x units going through (consumed, not pending) # the sell (how many units were sold) (consumed not pending) # an order update, with outstanding quantity and price (pending) if order.is_private and order.is_pending: # if order.is_private and not order.mine: FOR ACTUAL SIMULATION TIMES # assign correct role to bot if order.order_side == OrderSide.BUY: self._role = Role.BUYER elif order.order_side == OrderSide.SELL: self._role = Role.SELLER # call strategy based on bot type if self._bot_type == BotType.MARKET_MAKER: self._make_market(order) elif self._bot_type == BotType.REACTIVE: self._react_to_market(order) # during actual simulation add a not order.mine to this # if the private order gets cancelled, delete our order from public market if order.is_private and order.order_type == OrderType.CANCEL: # for some reason, all the order attributes need to be present, so it's not # possible to keep track of your old order by reference # old order reference only contain the specific attributes we define, everything # else is None type orders = Order.current() for _, o in orders.items(): # future proofing, if multiple types of orders in priv and one type expires # only delete the corresponding orders in public if o.mine and not o.is_private and order.order_side == o.order_side\ and not self._waiting_for_server: cancel_order = copy.copy(o) cancel_order.ref = f"Cancel {o.ref}" cancel_order.order_type = OrderType.CANCEL self.send_order(cancel_order) self._waiting_for_server = True break # if our order gets consumed if order.mine and not order.is_private and order.is_consumed: self._pending_order = False
def _cancel_my_orders(self): """ Cancels all of my orders in the market """ for _, order in Order.current().items(): # if I have an active order, cancel it UNLESS if order.mine and not self._waiting: self._waiting = True cancel_order = copy.copy(order) cancel_order.order_type = OrderType.CANCEL cancel_order.ref = f"Cancel - {order.ref} - SM" self.send_order(cancel_order) self._waiting = False
def _make_market(self): # if there is a private order, and no public order, CREATE NEW ORDER(S) self._active_public_orders_count = len(self._active_public_orders) self.inform( f"Private: {self._active_private_orders_count}, Public: {self._active_public_orders_count}" ) self.inform( f"{self._active_private_orders}, {self._active_public_orders}") if self._active_private_orders_count > 0 and self._active_public_orders_count < 1\ and not self._waiting_for_server: self.inform("Order required ====================================") # for each active order in private market (for task 1, just 1) for fm_id in self._active_private_orders: # PUBLIC ORDER CREATION ================================================== is_private = False priv_order = self._active_private_orders[fm_id] # if manager order is a bid if priv_order.order_side == OrderSide.BUY: self._role = Role.BUYER price = priv_order.price - PROFIT_MARGIN order_side = OrderSide.BUY # if manager order is an ask else: self._role = Role.SELLER price = priv_order.price + PROFIT_MARGIN order_side = OrderSide.SELL units = 1 order_type = OrderType.LIMIT ref = f"Order: {self._order_tracking_number} - SM" # PRIVATE ORDER CREATION self._create_new_order(price, units, order_side, order_type, ref, is_private) # if there is a public order, but not private order (STALE PUBLIC ORDER) # walk through current orders, delete my orders if self._active_public_orders_count > 0 and self._active_private_orders_count < 1\ and not self._waiting_for_server: current_orders = Order.current() for _, o in current_orders.items(): if o.mine: cancel_order = copy.copy(o) cancel_order.order_type = OrderType.CANCEL cancel_order.ref = f"Cancel {o.ref}" self.send_order(cancel_order) self._waiting_for_server = True
def _reactive_condition(self): """ Returns True when the reactive strategy is to be executed """ self._order_count = len([o for o in Order.current().values() if o.mine]) time = self._find_time_elapsed() # Do not run on the first instance as variables are yet to be initiated if time < 2/30: return False # Run reactive strategy in the latter 1 - MM_PROPORTION of the session time. if time > self._MM_PROPORTION * self._session_time and self._order_count == 0: return True elif self._order_count != 0: return False else: return False
def _get_best_bid_ask(): """ Walks the order book and determines what the best bid and asks are for each market :return : dictionaries of best bid and ask orders for each market """ VERY_HIGH_ASK = 999999 VERY_LOW_BID = -1 # track best bid_ask prices and orders # key - market, value - [best price, best price order] best_bids = { 'a': [VERY_LOW_BID, None], 'b': [VERY_LOW_BID, None], 'c': [VERY_LOW_BID, None], 'note': [VERY_LOW_BID, None] } best_asks = { 'a': [VERY_HIGH_ASK, None], 'b': [VERY_HIGH_ASK, None], 'c': [VERY_HIGH_ASK, None], 'note': [VERY_HIGH_ASK, None] } # track current best bids and asks for order_id, order in Order.current().items(): dict_key = order.market.item.lower() if order.order_side == OrderSide.BUY: if order.price > best_bids[dict_key][0]: best_bids[dict_key][0] = order.price best_bids[dict_key][1] = order else: if order.price < best_asks[dict_key][0]: best_asks[dict_key][0] = order.price best_asks[dict_key][1] = order return best_bids, best_asks
def _get_order_book_state(self): """ Ascertain how many active orders are mine, or from the manager to me, and if I have a stale order in the markets :return: num_private_orders : number of active private orders num_my_public_orders : number of my non-trade public orders my_stale_priv_order : which order of mine is stale manager_order : order object created by the manager """ num_private_orders = 0 manager_order = None num_my_public_orders = 0 my_stale_priv_order = None for fm_id, order in Order.current().items(): # if there is a private order not created by me if order.is_private and not order.mine: num_private_orders += 1 manager_order = order if manager_order.fm_id not in self._priv_orders and \ manager_order.original_id not in self._priv_orders: self._units_to_trade = manager_order.units self._priv_orders[order.fm_id] = order.units # figure out our role based on private order if manager_order.order_side == OrderSide.BUY: self._role = Role.BUYER else: self._role = Role.SELLER # if i created a public order that DID NOT EXECUTE IMMEDIATELY elif not order.is_private and order.mine: num_my_public_orders += 1 my_stale_priv_order = order return num_private_orders, num_my_public_orders, my_stale_priv_order, manager_order
def _make_market(self): # get order book state summary num_private_orders, num_my_public_orders, my_stale_priv_order, \ manager_order = self._get_order_book_state() # reset order tracking when no private left - IDLE state if num_private_orders == 0: self._units_to_trade = 0 self._priv_orders = {} # CANCELLING STALE ORDERS ========================================================= if num_my_public_orders > 0 and self._units_to_trade < 1 and \ not self._waiting_for_server: self._waiting_for_server = True self._cancel_order(my_stale_priv_order) self._last_accepted_public_order_id = 0 self._priv_orders = {} # reset private order tracking return # PRIVATE ORDER CREATION ========================================================== # need to confirm that the last sent order traded, and manager order > 0 if (self._last_accepted_public_order_id not in Order.current()) \ and (self._units_to_trade > 0) and (not self._last_accepted_public_order_id == 0) \ and (num_my_public_orders == 0): # determine order attributes is_private = True price = manager_order.price units = 1 order_side = None if self.role() == Role.BUYER: order_side = OrderSide.SELL elif self.role() == Role.SELLER: order_side = OrderSide.BUY order_type = OrderType.LIMIT ref = f"Private order - {self._tradeID}" if not self._waiting_for_server: self._last_accepted_public_order_id = 0 self._units_to_trade -= 1 self._create_new_order(price, units, order_side, order_type, ref, is_private) return # END PRIVATE ORDER CREATION ====================================================== # PUBLIC ORDER CREATION =========================================================== # no order of mine in the public market, but there is a private request if self._units_to_trade > 0 and num_my_public_orders == 0 and \ not self._waiting_for_server: # self._waiting_for_server = True # self.inform("Creating public order.") # determine order attributes is_private = False if self.role() == Role.BUYER: order_side = OrderSide.BUY price = manager_order.price - PROFIT_MARGIN else: order_side = OrderSide.SELL price = manager_order.price + PROFIT_MARGIN units = 1 order_type = OrderType.LIMIT ref = f"Public order - {self._tradeID}" if self.role() == Role.BUYER: if self._cash_available >= price: self._waiting_for_server = True self._create_new_order(price, units, order_side, order_type, ref, is_private) else: self.inform(f"Not enough cash to trade.") else: if self.role() == Role.SELLER: if self._public_widgets_available > 0: self._waiting_for_server = True self._create_new_order(price, units, order_side, order_type, ref, is_private) else: self.inform(f"Not enough widgets to trade.") return
def _react_to_market(self): """ Ascertains state of the market, best bid and asks Creates orders in public and private market Cancel public order if none left in private """ # track best bid and asks best_bid, best_ask, best_bid_order, best_ask_order = self._get_best_bid_ask() # track state of order book num_private_orders, num_my_public_orders, my_stale_priv_order, \ manager_order = self._get_order_book_state() # reset order tracking when no private left - IDLE state if num_private_orders == 0: self._units_to_trade = 0 self._priv_orders = {} self._cant_respond_orders = {} # CANCELLING STALE ORDERS ========================================================= if num_my_public_orders > 0 and self._units_to_trade < 1 and \ not self._waiting_for_server: self._waiting_for_server = True self._cancel_order(my_stale_priv_order) self._last_accepted_public_order_id = 0 # PRIVATE ORDER CREATION ============================================================== # only create private order if public order traded successfully, and there exists # a corresponding manager order if (self._last_accepted_public_order_id not in Order.current()) \ and (self._units_to_trade > 0) and (not self._last_accepted_public_order_id == 0) \ and (num_my_public_orders == 0): self._last_accepted_public_order_id = 0 self.inform("Last public order traded, creating private order.") # determine order attributes is_private = True price = manager_order.price units = 1 if self.role() == Role.BUYER: order_side = OrderSide.SELL else: order_side = OrderSide.BUY order_type = OrderType.LIMIT ref = f"Private order - {self._tradeID}" # if we are a buyer, we sell in private market. Ensure have enough priv widgets # if we are a seller, we buy in private market. Ensure have enough cash if (self.role() == Role.BUYER and self._private_widgets_available > 0) or \ (self.role() == Role.SELLER and self._cash_available >= price): # self.inform(f"Units left to trade = {self._units_to_trade}") if not self._waiting_for_server: self._units_to_trade -= 1 self._create_new_order(price, units, order_side, order_type, ref, is_private) else: self.inform("Not enough assets to trade. Please check widget and cash balance.") # END PRIVATE ORDER CREATION ========================================================== # self.inform(f"{num_my_public_orders}, {num_private_orders}, remaining trades => {self._units_to_trade}") # PUBLIC ORDER CREATION =============================================================== # stale orders should be cleared at this stage; there exists a private order # create an order based on best bid / ask if self._units_to_trade > 0 and num_my_public_orders == 0 and \ not self._waiting_for_server: is_private = False self._create_profitable_order(best_ask, best_bid, manager_order, is_private, best_bid_order, best_ask_order)
def _make_market(self): num_pending_orders = 0 num_manager_orders = 0 manager_order = None for _, order in Order.current().items(): # if previous private order sent is consumed if order.fm_id == self._last_order_sent and order.is_consumed: self._last_order_sent = None if order.mine and not order.is_consumed and not order.is_private: num_pending_orders += 1 if order.mine and order.is_consumed and not order.is_private: num_pending_orders -= 1 elif order.is_private and not order.mine: if not order.is_consumed: num_manager_orders += 1 manager_order = order else: num_manager_orders -= 1 self.inform(f"{num_pending_orders}, {num_manager_orders}") # if there is a manager order present, decide role if num_manager_orders > 0: if manager_order.order_side == OrderSide.BUY: self._role = Role.BUYER elif manager_order.order_side == OrderSide.SELL: self._role = Role.SELLER # if there is a private order from manager, but no public order if num_pending_orders == 0 and not self._waiting_for_server \ and num_manager_orders > 0: # create new public order ============================================== if self._role == Role.BUYER: price = manager_order.price - PROFIT_MARGIN order_side = OrderSide.BUY elif self._role == Role.SELLER: price = manager_order.price + PROFIT_MARGIN order_side = OrderSide.SELL is_private = False units = 1 order_type = OrderType.LIMIT ref = f"Order: {self._total_orders_sent} - SM" self._create_new_order(price, units, order_side, order_type, ref, is_private) # if the previous public order has traded, then create private order if num_pending_orders == 0 and self._last_order_sent is not None \ and num_manager_orders > 0: # create corresponding private order =================================== is_private = True price = manager_order.price if manager_order.order_side == OrderSide.BUY: order_side = OrderSide.SELL elif manager_order.order_side == OrderSide.SELL: order_side = OrderSide.BUY ref = f"Order: {self._total_orders_sent} - SM" self._create_new_order(price, units, order_side, order_type, ref, is_private)