Example #1
0
    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
Example #2
0
 def test_fifo_matching_orders(self):
     """ Ensure FIFO matching is enforced """
     orders = [
         ServerOrder(Buy(),
                     self.instrument.identifier,
                     50,
                     40.0,
                     'Trader1',
                     timestamp=1),
         ServerOrder(Buy(),
                     self.instrument.identifier,
                     50,
                     40.0,
                     'Trader2',
                     timestamp=2),
         ServerOrder(Sell(),
                     self.instrument.identifier,
                     50,
                     40.0,
                     'Trader3',
                     timestamp=3)
     ]
     for order in orders:
         self.book.on_new_order(order)
     self.assertEqual(self.book.count_bids(), 1)
     self.assertEqual(self.book.count_asks(), 0)
     self.assertEqual(self.book.get_bids()[0].timestamp, 2)
     self.assertEqual(self.book.get_bids()[0].counterparty, 'Trader2')
Example #3
0
    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
Example #4
0
 def test_two_orders_no_match(self):
     buy_order = ServerOrder(Buy(), self.instrument.identifier, 50, 40.0,
                             'Trader1')
     sell_order = ServerOrder(Sell(), self.instrument.identifier, 50, 42.0,
                              'Trader2')
     self.book.on_new_order(buy_order)
     self.book.on_new_order(sell_order)
     self.assertEqual(self.book.count_bids(), 1)
     self.assertEqual(self.book.count_asks(), 1)
Example #5
0
 def test_two_orders_no_match(self):
     """
     Two orders not matching (different price)
     """
     buy_order = ServerOrder(Buy(), self.instrument.identifier, 50, 40.0,
                             'Trader1')
     sell_order = ServerOrder(Sell(), self.instrument.identifier, 50, 42.0,
                              'Trader2')
     self.book.on_new_order(buy_order, apply_changes=True)
     self.book.on_new_order(sell_order, apply_changes=True)
     self.assertEqual(self.book.count_bids(), 1)
     self.assertEqual(self.book.count_asks(), 1)
Example #6
0
 def test_self_execution(self):
     """ Ensure you cannot trade with yourself """
     orders = [
         ServerOrder(Buy(), self.instrument.identifier, 50, 40.0,
                     'Trader1'),
         ServerOrder(Sell(), self.instrument.identifier, 50, 40.0,
                     'Trader1')
     ]
     for order in orders:
         self.book.on_new_order(order)
     self.assertEqual(self.book.count_bids(), 1)
     self.assertEqual(self.book.count_asks(), 1)
Example #7
0
 def test_four_stacked_orders_no_match(self):
     orders = [
         ServerOrder(Buy(), self.instrument.identifier, 50, 40.0,
                     'Trader1'),
         ServerOrder(Buy(), self.instrument.identifier, 50, 40.0,
                     'Trader1'),
         ServerOrder(Sell(), self.instrument.identifier, 50, 42.0,
                     'Trader2'),
         ServerOrder(Sell(), self.instrument.identifier, 50, 42.0,
                     'Trader2')
     ]
     for order in orders:
         self.book.on_new_order(order)
     self.assertEqual(self.book.count_bids(), 2)
     self.assertEqual(self.book.count_asks(), 2)
Example #8
0
    def handle_create_order(self, create_order, sock):
        client_session = self.client_sessions[sock]
        if client_session.status != SessionStatus.Authenticated:
            raise OrderRejected("Client is not authenticated")

        # TODO: does client session is allowed to create orders ?

        try:
            logger.info('Create order for {}'.format(client_session))
            order_book = self.order_books[create_order.instrument_identifier]
            new_order = ServerOrder(
                way=create_order.way,
                instrument_identifier=create_order.instrument_identifier,
                quantity=create_order.quantity,
                price=create_order.price,
                counterparty=client_session.login)
            order_book_changes = order_book.on_new_order(new_order)
            order_book.apply_order_book_changes(order_book_changes)
            self.apply_order_book_changes_in_storage(order_book_changes)
            logger.debug(f"Changes to be applied:\n{str(order_book_changes)}")
        except KeyError:
            instrument_identifier = create_order.instrument_identifier
            logger.warning(
                f"Order book related to instrument identifier [{instrument_identifier}] does not exist"
            )
Example #9
0
 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)
Example #10
0
 def test_two_opposite_orders_in_order_book(self):
     order_book = OrderBook(self.instrument_identifier)
     orders = [
         ServerOrder(Buy(),
                     self.instrument_identifier,
                     quantity=100.0,
                     price=9.0,
                     counterparty='Trader1'),
         ServerOrder(Sell(),
                     self.instrument_identifier,
                     quantity=100.0,
                     price=10.0,
                     counterparty='Trader2')
     ]
     for order in orders:
         order_book.add_order(order)
     encoded_order_book = self.marshaller.encode_order_book(order_book)
     message_type, body, _ = self.marshaller.decode_header(
         encoded_order_book)
     decoded_order_book = self.marshaller.decode_order_book(body)
     self.assertEqual(message_type, MessageTypes.OrderBook.value)
     self.assertEqual(encoded_order_book,
                      self.marshaller.encode_order_book(decoded_order_book))
 def test_one_buy_order_book(instrument_identifier, marshaller):
     simple_order_book = OrderBook(instrument_identifier)
     buy_order = ServerOrder(Buy(),
                             instrument_identifier,
                             quantity=100.0,
                             price=10.0,
                             counterparty='Trader1')
     simple_order_book.add_order(buy_order)
     encoded_order_book = marshaller.encode_order_book(simple_order_book)
     message_type, body, _ = marshaller.decode_header(encoded_order_book)
     decoded_order_book = marshaller.decode_order_book(body)
     assert message_type == MessageTypes.OrderBook.value
     assert encoded_order_book == marshaller.encode_order_book(
         decoded_order_book)
Example #12
0
 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)
Example #13
0
 def test_one_buy_order_book(self):
     simple_order_book = OrderBook(self.instrument_identifier)
     buy_order = ServerOrder(Buy(),
                             self.instrument_identifier,
                             quantity=100.0,
                             price=10.0,
                             counterparty='Trader1')
     simple_order_book.add_order(buy_order)
     encoded_order_book = self.marshaller.encode_order_book(
         simple_order_book)
     message_type, body, _ = self.marshaller.decode_header(
         encoded_order_book)
     decoded_order_book = self.marshaller.decode_order_book(body)
     self.assertEqual(message_type, MessageTypes.OrderBook.value)
     self.assertEqual(encoded_order_book,
                      self.marshaller.encode_order_book(decoded_order_book))
Example #14
0
 def decode_order_book(self, encoded_order_book):
     order_book_message = orderbook_pb2.OrderBook()
     order_book_message.ParseFromString(encoded_order_book)
     order_book = OrderBook(order_book_message.instrument_identifier)
     order_book.last_price = order_book_message.statistics.last_price
     order_book.high_price = order_book_message.statistics.high_price
     order_book.low_price = order_book_message.statistics.low_price
     for decoded_order in order_book_message.orders:
         order = ServerOrder(identifier=decoded_order.identifier,
                             way=OrderWay(decoded_order.way),
                             instrument_identifier=order_book_message.instrument_identifier,
                             quantity=decoded_order.quantity,
                             canceled_quantity=decoded_order.canceled_quantity,
                             executed_quantity=decoded_order.executed_quantity,
                             price=decoded_order.price,
                             counterparty=decoded_order.counterparty,
                             timestamp=decoded_order.timestamp)
         order_book.add_order(order)
     self.logger.debug(order_book)
     return order_book
Example #15
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)
Example #16
0
    def handle_create_order(self, create_order, sock):
        client_session = self.client_sessions[sock]
        if client_session.status != SessionStatus.Authenticated:
            raise OrderRejected('Client is not authenticated')

        # TODO: does client session is allowed to create orders ?

        try:
            self.logger.info('Create order for {}'.format(client_session))
            order_book = self.order_books[create_order.instrument_identifier]
        except KeyError:
            self.logger.warning(
                'Order book related to instrument identifier [{}] does not exist'
                .format(create_order.instrument_identifier))
        else:
            new_order = ServerOrder(
                way=create_order.way,
                instrument_identifier=create_order.instrument_identifier,
                quantity=create_order.quantity,
                price=create_order.price,
                counterparty=client_session.login)
            order_book.on_new_order(new_order)
    def decode_order_book(self, encoded_order_book):
        tokens = list(filter(None, encoded_order_book.split(self.separator)))

        instrument_identifier = int(tokens[0])
        order_book = OrderBook(instrument_identifier)
        order_book.last_price = float(tokens[1])
        order_book.high_price = float(tokens[2])
        order_book.low_price = float(tokens[3])

        order_tokens = tokens[4:]
        for x in range(0, len(order_tokens), 8):
            order_book.add_order(
                ServerOrder(instrument_identifier=instrument_identifier,
                            identifier=order_tokens[x],
                            way=OrderWay(int(order_tokens[x + 1])),
                            quantity=float(order_tokens[x + 2]),
                            canceled_quantity=float(order_tokens[x + 3]),
                            executed_quantity=float(order_tokens[x + 4]),
                            price=float(order_tokens[x + 5]),
                            counterparty=order_tokens[x + 6],
                            timestamp=order_tokens[x + 7]))

        return order_book
Example #18
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)
Example #19
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)
Example #20
0
 def test_sell_price_equal_to_buy(self):
     attacking_order = ServerOrder(Sell(), self.instrument.identifier, 10,
                                   40.0, 'Trader1')
     attacked_order = ServerOrder(Buy(), self.instrument.identifier, 10,
                                  40.0, 'Trader2')
     self.validate_one_matching(attacking_order, attacked_order)
Example #21
0
 def test_count_bids(self):
     buy_order = ServerOrder(Buy(), self.instrument.identifier, 50, 42.0,
                             'Trader1')
     self.assertEqual(self.book.count_bids(), 0)
     self.book.on_new_order(buy_order)
     self.assertEqual(self.book.count_bids(), 1)
Example #22
0
 def test_count_asks(self):
     sell_order = ServerOrder(Sell(), self.instrument.identifier, 50, 42.0,
                              'Trader1')
     self.assertEqual(self.book.count_asks(), 0)
     self.book.on_new_order(sell_order)
     self.assertEqual(self.book.count_asks(), 1)