def update_matched_orders(self, attacking_order: ServerOrder, matching_orders) -> OrderBookChanges: """ Iterate and update attacked orders, create deals upon total and partial executions """ changes = OrderBookChanges() for attacked_order in matching_orders: if self.is_attacked_order_full_executed(attacking_order, attacked_order): executed_quantity = attacked_order.get_remaining_quantity() attacking_order.executed_quantity += executed_quantity attacked_order.executed_quantity += executed_quantity self.update_statistics(last_executed_order=attacked_order) logger.debug(f"Attacker [{attacking_order.counterparty}]") logger.debug(f"Attacked [{attacked_order.counterparty}]") # Create deal deal = ServerDeal(attacking_order, attacked_order, executed_quantity) changes.deals_to_add.append(deal) # Remove executed order changes.order_to_remove.append(attacked_order) else: executed_quantity = attacking_order.get_remaining_quantity() attacking_order.executed_quantity += executed_quantity attacked_order.executed_quantity += executed_quantity self.update_statistics(last_executed_order=attacking_order) # Create a deal deal = ServerDeal(attacking_order, attacked_order, executed_quantity) changes.deals_to_add.append(attacking_order) if attacking_order.get_remaining_quantity() == 0.0: break return changes
def on_new_order(self, order: ServerOrder, apply_changes=False) -> OrderBookChanges: """ Entry point to process a new order in order book apply_changes indicates either order book changes are applied directly at the end (testing purpose) """ if order.instrument_identifier != self.instrument_identifier: raise Exception( "[LOGIC FAILURE] Order instrument must match order book instrument" ) quantity_before_execution = order.get_remaining_quantity() changes = self.match_order(order) # Case 1: unmatched if quantity_before_execution == order.get_remaining_quantity(): logger.debug( f"Attacking order is unmatched, adding [{order}] to trading book" ) changes.order_to_add.append(order) # Case 2: partially executed (existing order(s) have been executed) elif order.get_remaining_quantity() > 0.0: logger.debug( f"Attacking order cannot be fully executed, adding [{order}] to trading book" ) changes.order_to_add.append(order) # Case 3: order has been fully executed else: logger.debug( f"Attacking order [{order}] has been totally executed") if apply_changes: self.apply_order_book_changes(changes) return changes
def test_buy_price_greater_than_sell(self): attacking_order = ServerOrder(Buy(), self.instrument.identifier, 10, 40.0, 'Trader1') attacked_order = ServerOrder(Sell(), self.instrument.identifier, 10, 38.0, 'Trader2') self.validate_one_matching(attacking_order, attacked_order) self.assertEqual(attacking_order.get_remaining_quantity(), 10) self.assertEqual(attacked_order.get_remaining_quantity(), 10)
def test_buy_price_equal_to_sell(self): attacking_order = ServerOrder(Buy(), self.instrument.identifier, 10, 40.0, 'Trader1') attacked_order = ServerOrder(Sell(), self.instrument.identifier, 10, 40.0, 'Trader2') self.book.on_new_order(attacked_order, apply_changes=True) self.book.on_new_order(attacking_order, apply_changes=True) self.assertEqual(attacking_order.get_remaining_quantity(), 0) self.assertEqual(attacked_order.get_remaining_quantity(), 0)
def test_one_full_execution(self): quantity = 10 price = 42.0 attacking_order = ServerOrder(Sell(), self.instrument.identifier, quantity, price, 'Trader1') attacked_order = ServerOrder(Buy(), self.instrument.identifier, quantity, price, 'Trader2') self.book.on_new_order(attacked_order) self.book.on_new_order(attacking_order) self.assertEqual(self.book.count_bids(), 0) self.assertEqual(self.book.count_asks(), 0) self.assertEqual(attacking_order.executed_quantity, quantity) self.assertEqual(attacked_order.executed_quantity, quantity) self.assertEqual(attacking_order.get_remaining_quantity(), 0) self.assertEqual(attacked_order.get_remaining_quantity(), 0)
def test_one_partial_execution(self): attacking_quantity = 20 attacked_quantity = 10 price = 42.0 attacking_order = ServerOrder(Buy(), self.instrument.identifier, attacking_quantity, price, 'Trader1') attacked_order = ServerOrder(Sell(), self.instrument.identifier, attacked_quantity, price, 'Trader2') self.book.on_new_order(attacked_order, apply_changes=True) self.book.on_new_order(attacking_order, apply_changes=True) self.assertEqual(self.book.count_bids(), 1) self.assertEqual(self.book.count_asks(), 0) self.assertEqual(attacking_order.executed_quantity, attacking_quantity - attacked_quantity) self.assertEqual(attacked_order.executed_quantity, attacked_quantity) self.assertEqual(attacking_order.get_remaining_quantity(), attacking_quantity - attacked_quantity) self.assertEqual(attacked_order.get_remaining_quantity(), 0)
def test_multiple_partial_executions(self): attacking_quantity = 50 attacked_quantity = 10 price = 42.0 attacking_order = ServerOrder(Buy(), self.instrument.identifier, attacking_quantity, price, 'Trader1') attacked_orders = [] for _ in list(range(5)): attacked_order = ServerOrder(Sell(), self.instrument.identifier, attacked_quantity, price, 'Trader2') attacked_orders.append(attacked_order) self.book.on_new_order(attacked_order) self.book.on_new_order(attacking_order) self.assertEqual(self.book.count_bids(), 0) self.assertEqual(self.book.count_asks(), 0) self.assertEqual(attacking_order.executed_quantity, attacking_quantity) self.assertEqual(attacking_order.get_remaining_quantity(), 0) for attacked_order in attacked_orders: self.assertEqual(attacked_order.executed_quantity, attacked_quantity) self.assertEqual(attacked_order.get_remaining_quantity(), 0)