from decimal import Decimal import logging from unittest.mock import ANY import pytest from connection.tcp_connection_manager import TcpConnectionManager from proto.common_pb2 import Instrument, Side from tests.common.mock_expectations import CallExpectationsManager from tests.test_client.admin_test_client import AdminClientConnectionHandlerFactory, AdminTestClient from tests.test_client.order_book_test_client import ( OrderBookClientConnectionHandlerFactory, OrderBookCreatedExpectation, OrderBookTestClient) logger = logging.getLogger(__name__) class TestOrderBookSystem: PROTOCOL = "order_book" @pytest.fixture(autouse=True) def setup(self, request: pytest.FixtureRequest) -> None: self.test_name = request.node.name logger.info(f"Setting up test: {self.test_name}") self.tcp_connection_manager = TcpConnectionManager() self.call_expectations_manager = CallExpectationsManager() self.call_expectations_manager.setup_network(self.tcp_connection_manager) self.order_book_client_factory = OrderBookClientConnectionHandlerFactory(self.call_expectations_manager) self._admin_factory = AdminClientConnectionHandlerFactory(self.call_expectations_manager) self._admin_client: AdminTestClient | None = None self._next_instrument_id = 1 def _connect_unauthenticated(self) -> OrderBookTestClient: return self.tcp_connection_manager.connect(self.server_address, self.order_book_client_factory) def _get_admin_client(self) -> AdminTestClient: if self._admin_client is None: admin_address = self.orchestrator.get_server_address("admin") self._admin_client = self.tcp_connection_manager.connect(admin_address, self._admin_factory) return self._admin_client def _create_order_book_via_admin(self, order_book_client: OrderBookTestClient, tick_size: Decimal) -> OrderBookCreatedExpectation: on_created = order_book_client.expect_on_order_book_created_event(tick_size=tick_size) instrument = Instrument( symbol=f"TEST.{self._next_instrument_id}", description="Test instrument", currency="USD", multiplier=1.0) self._next_instrument_id += 1 admin_client = self._get_admin_client() admin_client.test_create_instrument(instrument, tick_size) assert on_created.fulfilled, "OnOrderBookCreated not received" order_book_client._last_order_book_id = on_created.get_message().order_book_id return on_created def test_create_order_book(self) -> None: client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_insert_order(self) -> None: client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) client.test_insert_order(side=Side.BUY, price=Decimal("100.0"), quantity=10, username=self.test_name) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_cancel_order(self) -> None: client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) client.test_insert_order(side=Side.BUY, price=Decimal("100.0"), quantity=10, username=self.test_name) client.test_cancel_order() self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() @pytest.mark.parametrize( "buy_price, buy_quantity, sell_price, sell_quantity, aggressor_side, expected_trade_price, expected_trade_quantity", [ pytest.param(Decimal("100.0"), 10, Decimal("100.0"), 10, Side.BUY, Decimal("100.0"), 10, id="same_price_same_quantity_buyer_hit"), pytest.param(Decimal("100.0"), 10, Decimal("100.0"), 10, Side.SELL, Decimal("100.0"), 10, id="same_price_same_quantity_seller_hit"), pytest.param(Decimal("100.0"), 10, Decimal("100.0"), 5, Side.BUY, Decimal("100.0"), 5, id="same_price_different_quantity_buyer_hit"), pytest.param(Decimal("100.0"), 10, Decimal("100.0"), 5, Side.SELL, Decimal("100.0"), 5, id="same_price_different_quantity_seller_hit"), pytest.param(Decimal("100.0"), 10, Decimal("99.99"), 10, Side.BUY, Decimal("99.99"), 10, id="different_price_same_quantity_buyer_hit"), pytest.param(Decimal("100.0"), 10, Decimal("99.99"), 10, Side.SELL, Decimal("100.0"), 10, id="different_price_same_quantity_seller_hit"), pytest.param(Decimal("100.0"), 10, Decimal("99.99"), 5, Side.BUY, Decimal("99.99"), 5, id="different_price_different_quantity_buyer_hit"), pytest.param(Decimal("100.0"), 10, Decimal("99.99"), 5, Side.SELL, Decimal("100.0"), 5, id="different_price_different_quantity_seller_hit"), ] ) def test_match_single_order( self, buy_price: Decimal, buy_quantity: int, sell_price: Decimal, sell_quantity: int, aggressor_side: Side.ValueType, expected_trade_price: Decimal, expected_trade_quantity: int) -> None: if aggressor_side == Side.BUY: aggressive_price = buy_price aggressive_quantity = buy_quantity passive_price = sell_price passive_quantity = sell_quantity else: aggressive_price = sell_price aggressive_quantity = sell_quantity passive_price = buy_price passive_quantity = buy_quantity logger.info("Connecting first client and creating order book") self.client_passive = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(self.client_passive, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id logger.info("Inserting passive order with first client") passive_side = Side.BUY if aggressor_side == Side.SELL else Side.SELL passive_order_response_expectation = self.client_passive.test_insert_order( side=passive_side, price=passive_price, quantity=passive_quantity, order_book_id=order_book_id, username=f"{self.test_name}_1") passive_order_id = passive_order_response_expectation.get_response().order_id logger.info("Connecting second client and expecting existing orders to be published") self.client_aggressor = self._connect_unauthenticated() self.client_aggressor.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) self.client_aggressor.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=passive_order_id, side=passive_side, price=passive_price, quantity=passive_quantity) # Verify that the second client received the full state of the order book self.call_expectations_manager.verify_expectations() logger.info("Inserting aggressive order with second client and expecting trade to be published") aggressive_order_response_expectation = self.client_aggressor.test_insert_order( side=aggressor_side, price=aggressive_price, quantity=aggressive_quantity, order_book_id=order_book_id, username=f"{self.test_name}_2") aggressive_order_response = aggressive_order_response_expectation.get_response() aggressive_order_id = aggressive_order_response.order_id assert len(aggressive_order_response.trade_ids) == 1 trade_id = aggressive_order_response.trade_ids[0] assert aggressive_order_response.traded_quantity == expected_trade_quantity # expect order from client2 to be published to client1 self.client_passive.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=aggressive_order_id, side=aggressor_side, price=aggressive_price, quantity=aggressive_quantity) # expect trade to be published to both clients buy_order_id = passive_order_id if passive_side == Side.BUY else aggressive_order_id sell_order_id = aggressive_order_id if passive_side == Side.BUY else passive_order_id self.client_passive.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id, aggressive_side=aggressor_side, price=expected_trade_price, quantity=expected_trade_quantity) self.client_aggressor.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id, aggressive_side=aggressor_side, price=expected_trade_price, quantity=expected_trade_quantity) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_order_cancellation_after_partial_fill(self) -> None: logger.info("Start matching orders with partial fill") self.test_match_single_order( buy_price=Decimal("100.0"), buy_quantity=10, sell_price=Decimal("100.0"), sell_quantity=5, aggressor_side=Side.BUY, expected_trade_price=Decimal("100.0"), expected_trade_quantity=5) assert self.client_aggressor._last_order_id is not None order_id = self.client_aggressor._last_order_id order_book_id = self.client_passive._last_order_book_id self.client_aggressor.test_cancel_order(order_book_id=order_book_id) self.client_passive.expect_on_order_cancelled_event(order_id=order_id) self.call_expectations_manager.verify_expectations() def test_order_cancellation_after_full_fill(self) -> None: logger.info("Start matching orders fully") self.test_match_single_order( buy_price=Decimal("100.0"), buy_quantity=10, sell_price=Decimal("100.0"), sell_quantity=10, aggressor_side=Side.BUY, expected_trade_price=Decimal("100.0"), expected_trade_quantity=10) order_book_id = self.client_passive._last_order_book_id self.client_aggressor.test_cancel_order(order_book_id=order_book_id, expect_success=False) self.client_passive.test_cancel_order(order_book_id=order_book_id, expect_success=False) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() @pytest.mark.parametrize( "num_levels, orders_per_level", [ pytest.param(5, 1, id="single_order_per_level"), pytest.param(1, 5, id="single_level_multiple_orders"), pytest.param(5, 5, id="multiple_levels_multiple_orders"), pytest.param(50, 5, id="lots_of_levels_multiple_orders"), pytest.param(50, 20, id="lots_of_levels_lots_of_orders"), ] ) def test_match_single_aggressive_order_on_multiple_levels(self, num_levels: int, orders_per_level: int) -> None: tick_size = Decimal("0.01") best_bid = Decimal("100.0") bid_quantity = 10 logger.info("Connecting first client and creating order book") self.client_passive = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(self.client_passive, tick_size=tick_size) order_book_id = on_created.get_message().order_book_id logger.info("Inserting passive orders with first client") logger.info(f"Inserting {orders_per_level} passive orders per level, at {num_levels} levels") passive_order_response_expectations = [] bid_prices = [best_bid - i * tick_size for i in range(num_levels)] for price in bid_prices: for _ in range(orders_per_level): passive_order_response_expectations.append(self.client_passive.test_insert_order( side=Side.BUY, price=price, quantity=bid_quantity, order_book_id=order_book_id, username=f"{self.test_name}_1")) logger.info("Connecting second client and expecting existing orders to be published") self.client_aggressor = self._connect_unauthenticated() self.client_aggressor.expect_on_order_book_created_event( tick_size=tick_size, order_book_id=order_book_id) buy_order_ids = [] for passive_order_response_expectation in passive_order_response_expectations: passive_order_response = passive_order_response_expectation.get_response() buy_order_ids.append(passive_order_response.order_id) self.client_aggressor.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=passive_order_response.order_id, side=Side.BUY, price=ANY, quantity=bid_quantity) # Verify that the second client received the full state of the order book self.call_expectations_manager.verify_expectations() worst_bid = min(bid_prices) total_bid_quantity = num_levels * orders_per_level * bid_quantity logger.info("Inserting aggressive order with second client and expecting to trade all levels") aggressive_order_response_expectation = self.client_aggressor.test_insert_order( side=Side.SELL, price=worst_bid, quantity=total_bid_quantity, order_book_id=order_book_id, username=f"{self.test_name}_2") aggressive_order_response = aggressive_order_response_expectation.get_response() sell_order_id = aggressive_order_response.order_id assert len(aggressive_order_response.trade_ids) == num_levels * orders_per_level aggressive_order_response_trade_ids = set(aggressive_order_response.trade_ids) # expect order from client2 to be published (also) to client1 self.client_passive.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=ANY, side=Side.SELL, price=worst_bid, quantity=total_bid_quantity) # expect trades to be published to both clients on_trade_expectations = [] for price in bid_prices: for _ in range(orders_per_level): on_trade_expectations.append(self.client_passive.expect_on_trade_event( trade_id=ANY, order_book_id=order_book_id, buy_order_id=ANY, sell_order_id=sell_order_id, aggressive_side=Side.SELL, price=price, quantity=bid_quantity)) on_trade_expectations.append(self.client_aggressor.expect_on_trade_event( trade_id=ANY, order_book_id=order_book_id, buy_order_id=ANY, sell_order_id=sell_order_id, aggressive_side=Side.SELL, price=price, quantity=bid_quantity)) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # verify that all trade ids from the aggressive order response are present in the trade events trade_ids = set(trade_expectation.get_message().trade_id for trade_expectation in on_trade_expectations) assert trade_ids == aggressive_order_response_trade_ids # ========================================================================= # State publishing to new clients # ========================================================================= def test_new_client_receives_existing_order_book(self) -> None: """A client connecting after an order book is created should receive OnOrderBookCreated.""" client1 = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event(tick_size=Decimal("0.01"), order_book_id=order_book_id) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_new_client_receives_existing_orders(self) -> None: """A client connecting after orders are placed should receive OnOrderInserted for each resting order.""" client1 = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id buy_resp = client1.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_1") buy_order_id = buy_resp.get_response().order_id sell_resp = client1.test_insert_order( side=Side.SELL, price=Decimal("101.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_1") sell_order_id = sell_resp.get_response().order_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event(tick_size=Decimal("0.01"), order_book_id=order_book_id) client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=buy_order_id, side=Side.BUY, price=Decimal("100.0"), quantity=10) client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=sell_order_id, side=Side.SELL, price=Decimal("101.0"), quantity=5) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_new_client_receives_multiple_order_books(self) -> None: """A client connecting should receive OnOrderBookCreated for all existing order books.""" client1 = self._connect_unauthenticated() on_created_1 = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) ob_id_1 = on_created_1.get_message().order_book_id on_created_2 = self._create_order_book_via_admin(client1, tick_size=Decimal("0.05")) ob_id_2 = on_created_2.get_message().order_book_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event(tick_size=Decimal("0.01"), order_book_id=ob_id_1) client2.expect_on_order_book_created_event(tick_size=Decimal("0.05"), order_book_id=ob_id_2) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_new_client_does_not_receive_cancelled_orders(self) -> None: """Cancelled orders must not appear in the state published to a newly connecting client.""" client1 = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id buy_resp = client1.test_insert_order( side=Side.BUY, price=Decimal("99.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_1") buy_order_id = buy_resp.get_response().order_id client1.test_cancel_order(order_id=buy_order_id, order_book_id=order_book_id) sell_resp = client1.test_insert_order( side=Side.SELL, price=Decimal("101.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_1") sell_order_id = sell_resp.get_response().order_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event(tick_size=Decimal("0.01"), order_book_id=order_book_id) client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=sell_order_id, side=Side.SELL, price=Decimal("101.0"), quantity=5) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_new_client_receives_partially_filled_orders(self) -> None: """A partially filled order should still appear in state published to a newly connecting client.""" client1 = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id buy_resp = client1.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_buyer") buy_order_id = buy_resp.get_response().order_id sell_resp = client1.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_seller") sell_response = sell_resp.get_response() assert sell_response.traded_quantity == 5 trade_id = sell_response.trade_ids[0] client1.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_response.order_id, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=5) self.call_expectations_manager.verify_expectations() client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event(tick_size=Decimal("0.01"), order_book_id=order_book_id) client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=buy_order_id, side=Side.BUY, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Error handling # ========================================================================= def test_insert_order_on_nonexistent_order_book(self) -> None: """Inserting on an order_book_id that does not exist should return an error response.""" client = self._connect_unauthenticated() client._last_order_book_id = 99999 client.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=99999, username=self.test_name, expect_success=False, expect_public_feed=False) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_cancel_nonexistent_order(self) -> None: """Cancelling an order_id that does not exist should return an error response.""" client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) client.test_cancel_order( order_id=99999, expect_success=False, expect_public_feed=False) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_cancel_same_order_twice(self) -> None: """Cancelling the same order a second time should return an error response.""" client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) resp = client.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, username=self.test_name) order_id = resp.get_response().order_id client.test_cancel_order(order_id=order_id) client.test_cancel_order( order_id=order_id, expect_success=False, expect_public_feed=False) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() @pytest.mark.parametrize( "tick_size, valid_price, invalid_price", [ pytest.param(Decimal("0.01"), Decimal("100.01"), Decimal("100.005"), id="tick_0.01"), pytest.param(Decimal("0.05"), Decimal("100.05"), Decimal("100.01"), id="tick_0.05"), pytest.param(Decimal("0.50"), Decimal("100.50"), Decimal("100.25"), id="tick_0.50"), pytest.param(Decimal("1.00"), Decimal("100.0"), Decimal("100.50"), id="tick_1.00"), ] ) def test_insert_order_price_must_be_multiple_of_tick_size( self, tick_size: Decimal, valid_price: Decimal, invalid_price: Decimal) -> None: """Orders with a price that is not a multiple of the tick size should be rejected.""" client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=tick_size) client.test_insert_order( side=Side.BUY, price=invalid_price, quantity=10, username=self.test_name, expect_success=False, expect_public_feed=False) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() client.test_insert_order( side=Side.BUY, price=valid_price, quantity=10, username=self.test_name) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Floating-point price precision # ========================================================================= @pytest.mark.parametrize( "tick_size, sell_price, buy_price, expected_trade_price", [ pytest.param( Decimal("0.01"), Decimal("100.03"), Decimal("100.03"), Decimal("100.03"), id="0.03_is_not_exact_in_float"), pytest.param( Decimal("0.01"), Decimal("100.07"), Decimal("100.07"), Decimal("100.07"), id="0.07_is_not_exact_in_float"), pytest.param( Decimal("0.0001"), Decimal("100.0001"), Decimal("100.0001"), Decimal("100.0001"), id="4dp_exact_match"), pytest.param( Decimal("0.0001"), Decimal("99.9999"), Decimal("99.9999"), Decimal("99.9999"), id="4dp_just_below_round_number"), pytest.param( Decimal("0.0001"), Decimal("100.0003"), Decimal("100.0007"), Decimal("100.0003"), id="4dp_cross_trades_at_passive_ask"), pytest.param( Decimal("0.0001"), Decimal("99.99999"), Decimal("99.99999"), Decimal("99.99999"), id="5dp_just_below_round_number"), pytest.param( Decimal("0.0001"), Decimal("100.00003"), Decimal("100.00007"), Decimal("100.0000"), id="5dp_crossing_outside_precision"), pytest.param( Decimal("0.0001"), Decimal("0.0001"), Decimal("0.0001"), Decimal("0.0001"), id="smallest_possible_price"), pytest.param( Decimal("0.01"), Decimal("0.10"), Decimal("0.10"), Decimal("0.10"), id="0.1_is_not_exact_in_float"), pytest.param( Decimal("0.01"), Decimal("33.33"), Decimal("33.33"), Decimal("33.33"), id="repeating_decimal_in_float"), ] ) def test_price_precision_through_float_protocol( self, tick_size: Decimal, sell_price: Decimal, buy_price: Decimal, expected_trade_price: Decimal) -> None: """Prices use float (double) on the wire. Implementations must not lose precision up to 4 decimal places.""" client_passive = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client_passive, tick_size=tick_size) order_book_id = on_created.get_message().order_book_id passive_resp = client_passive.test_insert_order( side=Side.SELL, price=sell_price, quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_seller") passive_order_id = passive_resp.get_response().order_id client_aggressor = self._connect_unauthenticated() client_aggressor.expect_on_order_book_created_event( tick_size=tick_size, order_book_id=order_book_id) client_aggressor.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=passive_order_id, side=Side.SELL, price=sell_price, quantity=10) self.call_expectations_manager.verify_expectations() client_aggressor._last_order_book_id = order_book_id agg_resp = client_aggressor.test_insert_order( side=Side.BUY, price=buy_price, quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_buyer") agg_response = agg_resp.get_response() assert len(agg_response.trade_ids) == 1, \ f"Expected a trade at sell={sell_price} buy={buy_price}, but got none" assert agg_response.traded_quantity == 10 trade_id = agg_response.trade_ids[0] agg_order_id = agg_response.order_id client_passive.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=agg_order_id, side=Side.BUY, price=buy_price, quantity=10) client_passive.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=agg_order_id, sell_order_id=passive_order_id, aggressive_side=Side.BUY, price=expected_trade_price, quantity=10) client_aggressor.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=agg_order_id, sell_order_id=passive_order_id, aggressive_side=Side.BUY, price=expected_trade_price, quantity=10) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Response field validation # ========================================================================= def test_insert_order_response_no_trades_when_no_match(self) -> None: """InsertOrderResponse should have empty trade_ids and zero traded_quantity when no match occurs.""" client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) resp = client.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, username=self.test_name) response = resp.get_response() assert len(response.trade_ids) == 0, \ f"Expected no trades, got trade_ids={list(response.trade_ids)}" assert response.traded_quantity == 0, \ f"Expected traded_quantity=0, got {response.traded_quantity}" self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_cancel_order_response_remaining_quantity(self) -> None: """CancelOrderResponse should report the full remaining (unfilled) quantity.""" client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) resp = client.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, username=self.test_name) order_id = resp.get_response().order_id cancel_resp = client.test_cancel_order(order_id=order_id) cancel_response = cancel_resp.get_response() assert cancel_response.remaining_quantity == 10, \ f"Expected remaining_quantity=10, got {cancel_response.remaining_quantity}" self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_trade_ids_are_unique_across_matches(self) -> None: """Every trade executed must receive a globally unique trade_id.""" client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) order_book_id = client._last_order_book_id all_trade_ids: set[int] = set() for i in range(5): client.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_{i}_buy") sell_resp = client.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_{i}_sell") sell_response = sell_resp.get_response() assert len(sell_response.trade_ids) == 1 trade_id = sell_response.trade_ids[0] assert trade_id not in all_trade_ids, f"Duplicate trade_id {trade_id} on iteration {i}" all_trade_ids.add(trade_id) for trade_id in all_trade_ids: client.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=ANY, sell_order_id=ANY, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Matching – no cross # ========================================================================= def test_no_match_when_orders_do_not_cross(self) -> None: """A buy below the best ask should not trigger a trade.""" client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) order_book_id = client._last_order_book_id client.test_insert_order( side=Side.BUY, price=Decimal("99.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_buyer") sell_resp = client.test_insert_order( side=Side.SELL, price=Decimal("101.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_seller") sell_response = sell_resp.get_response() assert len(sell_response.trade_ids) == 0, \ "Orders should not match when buy price < sell price" assert sell_response.traded_quantity == 0 self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Matching – price priority # ========================================================================= @pytest.mark.parametrize( "aggressor_side", [ pytest.param(Side.BUY, id="aggressive_buy_fills_best_ask_first"), pytest.param(Side.SELL, id="aggressive_sell_fills_best_bid_first"), ] ) def test_match_price_priority(self, aggressor_side: Side.ValueType) -> None: """The best-priced resting order must fill before worse-priced ones (price priority).""" client_passive = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client_passive, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id if aggressor_side == Side.BUY: passive_side = Side.SELL worse_resp = client_passive.test_insert_order( side=passive_side, price=Decimal("101.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_worse") better_resp = client_passive.test_insert_order( side=passive_side, price=Decimal("100.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_better") aggressive_price = Decimal("101.0") expected_trade_price = Decimal("100.0") else: passive_side = Side.BUY worse_resp = client_passive.test_insert_order( side=passive_side, price=Decimal("99.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_worse") better_resp = client_passive.test_insert_order( side=passive_side, price=Decimal("100.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_better") aggressive_price = Decimal("99.0") expected_trade_price = Decimal("100.0") worse_order_id = worse_resp.get_response().order_id better_order_id = better_resp.get_response().order_id client_aggressor = self._connect_unauthenticated() client_aggressor.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) client_aggressor.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=worse_order_id, side=passive_side, price=ANY, quantity=5) client_aggressor.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=better_order_id, side=passive_side, price=ANY, quantity=5) self.call_expectations_manager.verify_expectations() client_aggressor._last_order_book_id = order_book_id agg_resp = client_aggressor.test_insert_order( side=aggressor_side, price=aggressive_price, quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_agg") agg_response = agg_resp.get_response() assert len(agg_response.trade_ids) == 1, \ "Should match exactly one order (the better-priced one)" assert agg_response.traded_quantity == 5 trade_id = agg_response.trade_ids[0] if aggressor_side == Side.BUY: expected_buy_id = agg_response.order_id expected_sell_id = better_order_id else: expected_buy_id = better_order_id expected_sell_id = agg_response.order_id client_passive.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=agg_response.order_id, side=aggressor_side, price=aggressive_price, quantity=5) client_passive.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=expected_buy_id, sell_order_id=expected_sell_id, aggressive_side=aggressor_side, price=expected_trade_price, quantity=5) client_aggressor.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=expected_buy_id, sell_order_id=expected_sell_id, aggressive_side=aggressor_side, price=expected_trade_price, quantity=5) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Matching – time priority (FIFO at the same price) # ========================================================================= @pytest.mark.parametrize( "aggressor_side", [ pytest.param(Side.BUY, id="aggressive_buy_matches_oldest_ask"), pytest.param(Side.SELL, id="aggressive_sell_matches_oldest_bid"), ] ) def test_match_time_priority_at_same_price(self, aggressor_side: Side.ValueType) -> None: """Among resting orders at the same price, the earliest-inserted must fill first (FIFO).""" client_passive = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client_passive, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id passive_side = Side.SELL if aggressor_side == Side.BUY else Side.BUY passive_price = Decimal("100.0") first_resp = client_passive.test_insert_order( side=passive_side, price=passive_price, quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_first") first_order_id = first_resp.get_response().order_id second_resp = client_passive.test_insert_order( side=passive_side, price=passive_price, quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_second") second_order_id = second_resp.get_response().order_id client_aggressor = self._connect_unauthenticated() client_aggressor.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) client_aggressor.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=first_order_id, side=passive_side, price=passive_price, quantity=5) client_aggressor.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=second_order_id, side=passive_side, price=passive_price, quantity=5) self.call_expectations_manager.verify_expectations() client_aggressor._last_order_book_id = order_book_id agg_resp = client_aggressor.test_insert_order( side=aggressor_side, price=passive_price, quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_agg") agg_response = agg_resp.get_response() assert len(agg_response.trade_ids) == 1 assert agg_response.traded_quantity == 5 trade_id = agg_response.trade_ids[0] if aggressor_side == Side.BUY: expected_buy_id = agg_response.order_id expected_sell_id = first_order_id else: expected_buy_id = first_order_id expected_sell_id = agg_response.order_id client_passive.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=agg_response.order_id, side=aggressor_side, price=passive_price, quantity=5) client_passive.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=expected_buy_id, sell_order_id=expected_sell_id, aggressive_side=aggressor_side, price=passive_price, quantity=5) client_aggressor.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=expected_buy_id, sell_order_id=expected_sell_id, aggressive_side=aggressor_side, price=passive_price, quantity=5) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Matching – multi-client scenarios # ========================================================================= def test_match_multiple_passive_orders_from_different_clients(self) -> None: """An aggressive order should sweep passive orders placed by different clients.""" client_a = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client_a, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id resp_a = client_a.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_a") order_id_a = resp_a.get_response().order_id client_b = self._connect_unauthenticated() client_b.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) client_b.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=order_id_a, side=Side.BUY, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() client_b._last_order_book_id = order_book_id resp_b = client_b.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_b") order_id_b = resp_b.get_response().order_id client_a.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=order_id_b, side=Side.BUY, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() client_c = self._connect_unauthenticated() client_c.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) client_c.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=order_id_a, side=Side.BUY, price=Decimal("100.0"), quantity=10) client_c.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=order_id_b, side=Side.BUY, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() client_c._last_order_book_id = order_book_id agg_resp = client_c.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=20, order_book_id=order_book_id, username=f"{self.test_name}_c") agg_response = agg_resp.get_response() assert len(agg_response.trade_ids) == 2, \ f"Expected 2 trades, got {len(agg_response.trade_ids)}" assert agg_response.traded_quantity == 20 sell_order_id = agg_response.order_id for client in [client_a, client_b]: client.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=sell_order_id, side=Side.SELL, price=Decimal("100.0"), quantity=20) for trade_id in agg_response.trade_ids: for client in [client_a, client_b, client_c]: client.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=ANY, sell_order_id=sell_order_id, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_multiple_sequential_aggressive_orders_match_same_passive(self) -> None: """Multiple small aggressors should each partially fill a single large passive order.""" client1 = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id buy_resp = client1.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=30, order_book_id=order_book_id, username=f"{self.test_name}_buyer") buy_order_id = buy_resp.get_response().order_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=buy_order_id, side=Side.BUY, price=Decimal("100.0"), quantity=30) self.call_expectations_manager.verify_expectations() client2._last_order_book_id = order_book_id all_trade_ids: list[int] = [] for i in range(3): sell_resp = client2.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_{i}") sell_response = sell_resp.get_response() assert len(sell_response.trade_ids) == 1 assert sell_response.traded_quantity == 10 all_trade_ids.append(sell_response.trade_ids[0]) sell_order_id = sell_response.order_id client1.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=sell_order_id, side=Side.SELL, price=Decimal("100.0"), quantity=10) client1.expect_on_trade_event( trade_id=sell_response.trade_ids[0], order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=10) client2.expect_on_trade_event( trade_id=sell_response.trade_ids[0], order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() assert len(set(all_trade_ids)) == 3, "Each trade should have a unique ID" self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Matching – aggressive partial fill and remainder behaviour # ========================================================================= def test_aggressive_partial_fill_then_match_remainder(self) -> None: """An aggressive order that partially fills should have its remainder matched by a later counterparty.""" client1 = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id sell_resp = client1.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_1") sell_order_id = sell_resp.get_response().order_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=sell_order_id, side=Side.SELL, price=Decimal("100.0"), quantity=5) self.call_expectations_manager.verify_expectations() client2._last_order_book_id = order_book_id buy_resp = client2.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_2") buy_response = buy_resp.get_response() assert len(buy_response.trade_ids) == 1 assert buy_response.traded_quantity == 5 buy_order_id = buy_response.order_id first_trade_id = buy_response.trade_ids[0] client1.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=buy_order_id, side=Side.BUY, price=Decimal("100.0"), quantity=10) client1.expect_on_trade_event( trade_id=first_trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id, aggressive_side=Side.BUY, price=Decimal("100.0"), quantity=5) client2.expect_on_trade_event( trade_id=first_trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id, aggressive_side=Side.BUY, price=Decimal("100.0"), quantity=5) self.call_expectations_manager.verify_expectations() sell_resp_2 = client1.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_1") sell_response_2 = sell_resp_2.get_response() assert len(sell_response_2.trade_ids) == 1 assert sell_response_2.traded_quantity == 5 sell_order_id_2 = sell_response_2.order_id second_trade_id = sell_response_2.trade_ids[0] client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=sell_order_id_2, side=Side.SELL, price=Decimal("100.0"), quantity=5) client1.expect_on_trade_event( trade_id=second_trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id_2, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=5) client2.expect_on_trade_event( trade_id=second_trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id_2, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=5) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_aggressive_partial_fill_then_cancel_remainder(self) -> None: """After a partial fill, cancelling the remainder should succeed with the correct remaining_quantity.""" client1 = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id client1.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=5, order_book_id=order_book_id, username=f"{self.test_name}_1") sell_order_id = client1._last_order_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=sell_order_id, side=Side.SELL, price=Decimal("100.0"), quantity=5) self.call_expectations_manager.verify_expectations() client2._last_order_book_id = order_book_id buy_resp = client2.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_2") buy_response = buy_resp.get_response() buy_order_id = buy_response.order_id assert buy_response.traded_quantity == 5 trade_id = buy_response.trade_ids[0] client1.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=buy_order_id, side=Side.BUY, price=Decimal("100.0"), quantity=10) client1.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id, aggressive_side=Side.BUY, price=Decimal("100.0"), quantity=5) client2.expect_on_trade_event( trade_id=trade_id, order_book_id=order_book_id, buy_order_id=buy_order_id, sell_order_id=sell_order_id, aggressive_side=Side.BUY, price=Decimal("100.0"), quantity=5) self.call_expectations_manager.verify_expectations() cancel_resp = client2.test_cancel_order( order_id=buy_order_id, order_book_id=order_book_id) cancel_response = cancel_resp.get_response() assert cancel_response.remaining_quantity == 5, \ f"Expected remaining_quantity=5, got {cancel_response.remaining_quantity}" client1.expect_on_order_cancelled_event(order_id=buy_order_id) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Cancel prevents match # ========================================================================= def test_cancel_passive_prevents_future_match(self) -> None: """Cancelling a passive order must prevent it from being matched by a later crossing order.""" client = self._connect_unauthenticated() self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) order_book_id = client._last_order_book_id buy_resp = client.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_buyer") client.test_cancel_order( order_id=buy_resp.get_response().order_id, order_book_id=order_book_id) sell_resp = client.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=f"{self.test_name}_seller") sell_response = sell_resp.get_response() assert len(sell_response.trade_ids) == 0, \ "No trade should occur after the passive was cancelled" assert sell_response.traded_quantity == 0 self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Multiple order books – isolation # ========================================================================= def test_orders_on_different_order_books_do_not_interact(self) -> None: """Crossing orders in separate order books must not trigger a trade.""" client = self._connect_unauthenticated() on_created_1 = self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) ob_id_1 = on_created_1.get_message().order_book_id on_created_2 = self._create_order_book_via_admin(client, tick_size=Decimal("0.01")) ob_id_2 = on_created_2.get_message().order_book_id buy_resp = client.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=ob_id_1, username=f"{self.test_name}_1") assert buy_resp.get_response().traded_quantity == 0 sell_resp = client.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=10, order_book_id=ob_id_2, username=f"{self.test_name}_2") sell_response = sell_resp.get_response() assert len(sell_response.trade_ids) == 0, \ "Orders in different order books should not match" assert sell_response.traded_quantity == 0 self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() def test_matching_independent_across_order_books(self) -> None: """A trade in one order book must not affect orders in another order book.""" client1 = self._connect_unauthenticated() on_created_1 = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) ob_id_1 = on_created_1.get_message().order_book_id on_created_2 = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) ob_id_2 = on_created_2.get_message().order_book_id buy_resp_1 = client1.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=ob_id_1, username=f"{self.test_name}_1") buy_order_1 = buy_resp_1.get_response().order_id buy_resp_2 = client1.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=ob_id_2, username=f"{self.test_name}_1") buy_order_2 = buy_resp_2.get_response().order_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=ob_id_1) client2.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=ob_id_2) client2.expect_on_order_inserted_event( order_book_id=ob_id_1, order_id=buy_order_1, side=Side.BUY, price=Decimal("100.0"), quantity=10) client2.expect_on_order_inserted_event( order_book_id=ob_id_2, order_id=buy_order_2, side=Side.BUY, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() client2._last_order_book_id = ob_id_1 sell_resp = client2.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=10, order_book_id=ob_id_1, username=f"{self.test_name}_2") sell_response = sell_resp.get_response() assert sell_response.traded_quantity == 10 trade_id = sell_response.trade_ids[0] sell_order_id = sell_response.order_id client1.expect_on_order_inserted_event( order_book_id=ob_id_1, order_id=sell_order_id, side=Side.SELL, price=Decimal("100.0"), quantity=10) client1.expect_on_trade_event( trade_id=trade_id, order_book_id=ob_id_1, buy_order_id=buy_order_1, sell_order_id=sell_order_id, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=10) client2.expect_on_trade_event( trade_id=trade_id, order_book_id=ob_id_1, buy_order_id=buy_order_1, sell_order_id=sell_order_id, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() sell_resp_2 = client2.test_insert_order( side=Side.SELL, price=Decimal("100.0"), quantity=10, order_book_id=ob_id_2, username=f"{self.test_name}_2") sell_response_2 = sell_resp_2.get_response() assert sell_response_2.traded_quantity == 10, \ "Order on book 2 should still be available for matching" sell_order_id_2 = sell_response_2.order_id trade_id_2 = sell_response_2.trade_ids[0] client1.expect_on_order_inserted_event( order_book_id=ob_id_2, order_id=sell_order_id_2, side=Side.SELL, price=Decimal("100.0"), quantity=10) client1.expect_on_trade_event( trade_id=trade_id_2, order_book_id=ob_id_2, buy_order_id=buy_order_2, sell_order_id=sell_order_id_2, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=10) client2.expect_on_trade_event( trade_id=trade_id_2, order_book_id=ob_id_2, buy_order_id=buy_order_2, sell_order_id=sell_order_id_2, aggressive_side=Side.SELL, price=Decimal("100.0"), quantity=10) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls() # ========================================================================= # Field propagation # ========================================================================= def test_order_inserted_event_contains_username(self) -> None: """OnOrderInserted events must propagate the correct username.""" client1 = self._connect_unauthenticated() on_created = self._create_order_book_via_admin(client1, tick_size=Decimal("0.01")) order_book_id = on_created.get_message().order_book_id test_username = f"{self.test_name}_user" resp = client1.test_insert_order( side=Side.BUY, price=Decimal("100.0"), quantity=10, order_book_id=order_book_id, username=test_username) order_id = resp.get_response().order_id client2 = self._connect_unauthenticated() client2.expect_on_order_book_created_event( tick_size=Decimal("0.01"), order_book_id=order_book_id) client2.expect_on_order_inserted_event( order_book_id=order_book_id, order_id=order_id, side=Side.BUY, price=Decimal("100.0"), quantity=10, username=test_username) self.call_expectations_manager.verify_expectations() self.call_expectations_manager.verify_no_unexpected_calls()