def test_matching_engine_can_add_and_run_multiple_instruments(): instrument_id_1 = "AAPL" instrument_id_2 = "MSFT" quantity = 100 price = 10 limit_orders_1 = [ LimitOrder(instrument_id=instrument_id_1, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity, price=price + (i if i % 2 else -i)) for i in range(10) ] limit_orders_2 = [ LimitOrder(instrument_id=instrument_id_2, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity, price=price + (i if i % 2 else -i)) for i in range(10) ] matching_engine = MatchingEngine() for order in limit_orders_1 + limit_orders_2: matching_engine.add_order(order) matching_engine.run() matching_engine.live = False order_book = matching_engine.order_books[instrument_id_1] assert len(matching_engine.order_books ) == 2, "Test Failed: There should be 2 order books" assert not matching_engine.orders, "Test Failed: There should be no orders" assert len(matching_engine.processed_orders ) == 20, "Test Failed: There should be 20 processed_orders" assert not order_book.bids, "Test Failed: There should be no bids after complete matching" assert not order_book.asks, "Test Failed: There should be no asks after complete matching" assert order_book.best_bid is None, "Test Failed: best_bid should be empty" assert order_book.best_ask is None, "Test Failed: best_ask should be empty" assert len( order_book.trades) == 5, "Test Failed: trades should have 5 orders" assert len(order_book.complete_orders ) == 10, "Test Failed: complete_orders should have all orders" assert not order_book.attempt_match, "Test Failed: attempt_match should be False" order_book = matching_engine.order_books[instrument_id_2] assert not order_book.bids, "Test Failed: There should be no bids after complete matching" assert not order_book.asks, "Test Failed: There should be no asks after complete matching" assert order_book.best_bid is None, "Test Failed: best_bid should be empty" assert order_book.best_ask is None, "Test Failed: best_ask should be empty" assert len( order_book.trades) == 5, "Test Failed: trades should have 5 orders" assert len(order_book.complete_orders ) == 10, "Test Failed: complete_orders should have all orders" assert not order_book.attempt_match, "Test Failed: attempt_match should be False" pass
def test_matching_engine_can_add_and_process(): instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity, price=price + (i if i % 2 else -i)) for i in range(10) ] matching_engine = MatchingEngine() for order in limit_orders: matching_engine.add_order(order) matching_engine.match() order_book = matching_engine.order_books[instrument_id] assert len(matching_engine.order_books ) == 1, "Test Failed: There should be 1 order book" assert not matching_engine.orders, "Test Failed: There should be no orders" assert len(matching_engine.processed_orders ) == 10, "Test Failed: There should be 10 processed_orders" assert not order_book.bids, "Test Failed: There should be no bids after complete matching" assert not order_book.asks, "Test Failed: There should be no asks after complete matching" assert order_book.best_bid is None, "Test Failed: best_bid should be empty" assert order_book.best_ask is None, "Test Failed: best_ask should be empty" assert len( order_book.trades) == 5, "Test Failed: trades should have 5 orders" assert len(order_book.complete_orders ) == 10, "Test Failed: complete_orders should have all orders" assert not order_book.attempt_match, "Test Failed: attempt_match should be False" pass
def test_order_book_can_match_orders(): instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity, price=price + (i if i % 2 else -i)) for i in range(10) ] order_book = OrderBook() for order in limit_orders: order_book.add_order(order) order_book.match() assert not order_book.bids, "Test Failed: There should be no bids after complete matching" assert not order_book.asks, "Test Failed: There should be no asks after complete matching" assert order_book.best_bid is None, "Test Failed: best_bid should be empty" assert order_book.best_ask is None, "Test Failed: best_ask should be empty" assert len( order_book.trades) == 5, "Test Failed: trades should have 5 orders" assert len(order_book.complete_orders ) == 10, "Test Failed: complete_orders should have all orders" assert not order_book.attempt_match, "Test Failed: attempt_match should be False" pass
def test_order_book_cannot_match_non_crossing_orders(): instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity, price=price + (-i if i % 2 else i)) for i in range(10) ] order_book = OrderBook() for order in limit_orders: order_book.add_order(order) order_book.match() assert len(order_book.bids) == 4, "Test Failed: There should be 5 bids" assert len(order_book.asks) == 4, "Test Failed: There should be 5 asks" assert order_book.best_bid is not None, "Test Failed: best_bid should not be empty" assert order_book.best_ask is not None, "Test Failed: best_ask should not be empty" assert order_book.best_bid.price <= order_book.best_ask.price, "Test Failed: best prices are not aligned" assert all(order_book.bids[i].price <= order_book.asks[i].price for i in range(4)), \ "Test Failed: prices are not aligned" assert not order_book.trades, "Test Failed: trades should be empty" assert not order_book.complete_orders, "Test Failed: complete_orders should be empty" assert not order_book.attempt_match, "Test Failed: attempt_match should be False" pass
def test_order_book_can_handle_limit_and_market_orders_together(): """ Here there are more asks than bids, so the bids will fill""" instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy, quantity=quantity - 10 * i, price=price + i) for i in range(5) ] market_order = MarketOrder(instrument_id=instrument_id, order_direction=OrderDirection.sell, quantity=400) order_book = OrderBook() for order in limit_orders: order_book.add_order(order) order_book.add_order(market_order) order_book.match() assert not order_book.asks, "Test Failed: There should be no asks after this matching" assert order_book.best_ask is None, "Test Failed: best_ask should be empty" assert not order_book.bids, "Test Failed: There should be no bids after this matching" assert order_book.best_bid is None, "Test Failed: best_bid should be empty" assert len(order_book.trades) == 5, "Test Failed: there should be 5 trades" assert len(order_book.complete_orders ) == 6, "Test Failed: complete_orders should have 6 orders" assert not order_book.attempt_match, "Test Failed: attempt_match should be False" pass
def test_order_book_can_cancel_sell_other_than_best_ask(): instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity - 10 * i, price=price + (i if i % 2 else -i)) for i in range(10) ] for i, l in enumerate(limit_orders): l.order_id = i limit_orders[i] = l order_book = OrderBook() for order in limit_orders: order_book.add_order(order) cancel_order = CancelOrder(instrument_id=instrument_id, order_id=2, order_direction=OrderDirection.sell) order_book.add_order(cancel_order) assert len(order_book.bids) > len( order_book.asks), "Test Failed: should be more buys than sells" assert cancel_order.cancel_success, "Test Failed: cancel should succeed" pass
def test_order_book_can_match_incomplete_more_bids(): """ Here there are more asks than bids, so the bids will fill""" instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if not i % 2 else OrderDirection.sell, quantity=quantity - 10 * i, price=price + (i if not i % 2 else -i)) for i in range(10) ] order_book = OrderBook() for order in limit_orders: order_book.add_order(order) order_book.match() assert not order_book.asks, "Test Failed: There should be no asks after this matching" assert order_book.best_ask is None, "Test Failed: best_ask should be empty" assert not order_book.bids, "Test Failed: There should be no bids after this matching" assert order_book.best_bid is not None, "Test Failed: best_bid should not be empty" assert len( order_book.trades) > 5, "Test Failed: trades should more than 5 trades" assert len( order_book.complete_orders ) < 10, "Test Failed: complete_orders should have fewer than 10 orders" assert not order_book.attempt_match, "Test Failed: attempt_match should be False" pass
def test_limit_order_sell_can_handle_trade(): instrument_id = "AAPL" order_direction = OrderDirection.sell quantity = 100 price = 10 limit_order = LimitOrder(instrument_id=instrument_id, order_direction=order_direction, quantity=quantity, price=price) trade = Trade(datetime=np.datetime64("2020-01-01"), price=10, quantity=10) limit_order.update_on_trade(trade) assert limit_order.quantity == quantity, "Test failed, incorrect quantity" assert limit_order.order_direction == order_direction, "Test failed, incorrect quantity" assert limit_order.price == price, "Test failed, incorrect price for limit order" assert limit_order.instrument_id == instrument_id, "Test failed, incorrect instrument_id" assert limit_order.order_type == OrderType.limit, "Test failed, incorrect order type" assert limit_order.fill_info, "Test failed, no fill info" assert limit_order.unfilled_quantity == quantity - \ 10, "Test failed, incorrect unfilled quantity" pass
def test_order_book_raise_exception_on_invalid_order(): instrument_id = "AAPL" quantity = 100 price = 10 limit_order = LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.test, quantity=quantity, price=price) order_book = OrderBook() with pytest.raises(InvalidOrderDirectionException) as exn: order_book.add_order(limit_order) pass
def get_data(n): instrument_ids = ["AAPL", "MSFT", "TSLA", "FB", "NFLX"] quantity = 100 price = 40 m = n // len(instrument_ids) output = [] for instr in instrument_ids: buy_limits = [ LimitOrder(instrument_id=instr, order_direction=OrderDirection.buy, quantity=quantity + random.uniform(-50, 50), price=price + random.uniform(-2.5, 2.5)) for i in range(m // 4) ] sell_limits = [ LimitOrder(instrument_id=instr, order_direction=OrderDirection.sell, quantity=quantity + random.uniform(-50, 50), price=price + random.uniform(-2.5, 2.5)) for i in range(m // 4) ] buy_markets = [ MarketOrder(instrument_id=instr, order_direction=OrderDirection.buy, quantity=quantity + random.uniform(-50, 50)) for i in range(m // 4) ] sell_markets = [ MarketOrder(instrument_id=instr, order_direction=OrderDirection.sell, quantity=quantity + random.uniform(-50, 50)) for i in range(m // 4) ] output += buy_limits + sell_limits + buy_markets + sell_markets random.shuffle(output) return output
def test_cannot_cancel_filled_order(): instrument_id = "AAPL" order_direction = OrderDirection.buy quantity = 100 price = 10 limit_order = LimitOrder(instrument_id=instrument_id, order_direction=order_direction, quantity=quantity, price=price) limit_order.order_id = 1 limit_order.status = OrderStatus.filled instrument_id = "AAPL" order_id = 1 cancel_order = CancelOrder(instrument_id=instrument_id, order_id=order_id, order_direction=order_direction) cancel_order.cancel_order(order=limit_order) assert limit_order.status == OrderStatus.filled, "Test failed: order cancelled" assert not cancel_order.cancel_success, "Test Failed: order cancelled" pass
def test_limit_order_sell(): instrument_id = "AAPL" order_direction = OrderDirection.sell quantity = 100 price = 10 limit_order = LimitOrder(instrument_id=instrument_id, order_direction=order_direction, quantity=quantity, price=price) assert limit_order.quantity == quantity, "Test failed, incorrect quantity" assert limit_order.order_direction == order_direction, "Test failed, incorrect quantity" assert limit_order.price == price, "Test failed, incorrect price for limit order" assert limit_order.instrument_id == instrument_id, "Test failed, incorrect instrument_id" assert limit_order.order_type == OrderType.limit, "Test failed, incorrect order type" assert limit_order.unfilled_quantity == quantity, "Test failed, incorrect unfilled quantity" pass
def test_order_book_can_generate_order_book_plot(): instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity, price=price + (-i if i % 2 else i)) for i in range(10) ] order_book = OrderBook() for order in limit_orders: order_book.add_order(order) order_book.plot_order_book() pass
def test_order_book_can_generate_execution_plot(): instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if not i % 2 else OrderDirection.sell, quantity=quantity - 10 * i, price=price + (i if not i % 2 else -2 * i)) for i in range(10) ] order_book = OrderBook() for order in limit_orders: order_book.add_order(order) order_book.match() order_book.plot_executions() pass
def test_matching_engine_can_add_orders(): instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity, price=price + (i if i % 2 else -i)) for i in range(10) ] matching_engine = MatchingEngine() for order in limit_orders: matching_engine.add_order(order) assert not matching_engine.order_books, "Test Failed: There should beno order books" assert matching_engine.orders, "Test Failed: There should be orders" assert len( matching_engine.orders) == 10, "Test Failed: There should be 10 orders" assert not matching_engine.processed_orders, "Test Failed: There should be no processed_orders" pass
def test_order_book_can_cancel_sell(): """ Here there are more asks than bids, so the bids will fill""" instrument_id = "AAPL" quantity = 100 price = 10 limit_orders = [ LimitOrder(instrument_id=instrument_id, order_direction=OrderDirection.buy if i % 2 else OrderDirection.sell, quantity=quantity - 10 * i, price=price + (i if i % 2 else -i)) for i in range(10) ] for i, l in enumerate(limit_orders): l.order_id = i limit_orders[i] = l order_book = OrderBook() for order in limit_orders: order_book.add_order(order) cancel_order = CancelOrder(instrument_id=instrument_id, order_id=0, order_direction=OrderDirection.sell) order_book.match() order_book.add_order(cancel_order) assert not order_book.asks, "Test Failed: There should be no asks after this matching" assert order_book.best_ask is None, "Test Failed: best_ask should be empty" assert not order_book.bids, "Test Failed: There should be no bids after this matching" assert order_book.best_bid is None, "Test Failed: best_bid should be empty" assert len( order_book.trades) > 5, "Test Failed: trades should more than 5 trades" assert len(order_book.complete_orders ) == 10, "Test Failed: complete_orders should have 10 orders" assert cancel_order.cancel_success, "Test Failed: cancel should succeed" pass