def test_cancel_replace_not_allowed_on_fak_or_fok(): # FAK n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAK, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) cr = CancelReplaceCommand(121235, 1234235.324, 2342, "user_x", MARKET, BID_SIDE, Price("43.01"), 234) with pytest.raises(AssertionError): oec.apply_cancel_replace_command(cr) # FOK n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FOK, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) cr = CancelReplaceCommand(121235, 1234235.324, 2342, "user_x", MARKET, BID_SIDE, Price("43.01"), 234) with pytest.raises(AssertionError): oec.apply_cancel_replace_command(cr)
def test_basic_partial_fill_replenish_visible(): n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 100, 40) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # now ack it ack = AcknowledgementReport(121235, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 100, 40) oec.apply_acknowledgement_report(ack) aggressor = NewOrderCommand(1111, 1234237.123, 22222, "user_y", MARKET, ASK_SIDE, FAR, Price("34.52"), 40) # now resting partial fill pf = PartialFillReport(121236, 1234237.123, 2342, "user_x", MARKET, aggressor, 40, Price("34.52"), BID_SIDE, 99999, 100-40) oec.apply_partial_fill_report(pf) assert oec.open_exposure_requests() == [] assert oec.current_exposure().price() == Price("34.52") assert oec.current_exposure().qty() == 100-40 assert oec.visible_qty() == 40 # should have replenished assert oec.iceberg_peak_qty() == 40 # should not have changed assert oec.has_partial_fill() # now test the partial fill wipes out 40 more, so visible is min aggressor2 = NewOrderCommand(1114, 1234237.123, 33333, "user_y", MARKET, ASK_SIDE, FAR, Price("34.52"), 40) # now resting partial fill pf2 = PartialFillReport(121236, 1234237.123, 2342, "user_x", MARKET, aggressor2, 40, Price("34.52"), BID_SIDE, 99999, 100-40-40) # subtract out the size of 2 40 lot fills now oec.apply_partial_fill_report(pf2) assert oec.open_exposure_requests() == [] assert oec.current_exposure().price() == Price("34.52") assert oec.current_exposure().qty() == 100-40-40 assert oec.visible_qty() == 100-40-40 # should have replenished to min of 40 and 100-40-40 assert oec.iceberg_peak_qty() == 40 # should not have changed assert oec.has_partial_fill()
def test_full_fill_on_unacked_cr_with_acked_new_order(): n = NewOrderCommand(1, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # now ack it ack = AcknowledgementReport(2, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 1000, 1000) oec.apply_acknowledgement_report(ack) assert oec.visible_qty() == 1000 assert oec.current_exposure().qty() == 1000 assert oec.current_exposure().price() == Price("34.52") assert len(oec.open_exposure_requests()) == 0 assert oec.is_open() cr = CancelReplaceCommand(3, 1234236.842, 2342, "user_x", MARKET, BID_SIDE, Price("34.56"), 800) oec.apply_cancel_replace_command(cr) # now should have 2 open exposures assert oec.visible_qty() == 1000 assert oec.current_exposure().qty() == 1000 assert oec.current_exposure().price() == Price("34.52") assert oec.is_open() assert len(oec.open_exposure_requests()) == 1 assert oec.most_recent_requested_exposure() == Exposure(Price("34.56"), 800, 3) full_fill = FullFillReport(4, 1234237.123, 2342, "user_x", MARKET, cr, 800, Price("34.56"), BID_SIDE, 12345) oec.apply_full_fill_report(full_fill) assert oec.visible_qty() == 0 assert oec.current_exposure().price() is None assert oec.current_exposure().qty() == 0 assert oec.current_exposure().causing_event_id() == 4 assert len(oec.open_exposure_requests()) == 0 assert oec.is_open() is False
def test_partial_aggressive_fill(): # nothing should change as we only update orderbook for passive partial fills ob = build_base_order_book() # get baseline ask_prices = ob.ask_prices() bid_prices = ob.bid_prices() best_ask_price = ob.best_ask_price() best_bid_price = ob.best_bid_price() bast_ask_level = ob.best_ask_level() bast_bid_level = ob.best_bid_level() agg_new = NewOrderCommand(101, 1234002.123, 1008, "user_z", MARKET, BID_SIDE, FAR, Price("34.52"), 45) oec = OrderEventChain(agg_new, LOGGER, SUBCHAIN_ID_GENERATOR) pf = PartialFillReport(102, 1234002.123, 1008, "user_z", MARKET, agg_new, 35, Price('34.52'), BID_SIDE, 3333, 10) oec.apply_partial_fill_report(pf) ob.handle_partial_fill_report(pf, oec) # nothing at the order book should have changed assert ask_prices == ob.ask_prices() assert bid_prices == ob.bid_prices() assert best_ask_price == ob.best_ask_price() assert best_bid_price == ob.best_bid_price() assert bast_ask_level == ob.best_ask_level() assert bast_bid_level == ob.best_bid_level()
def test_close_exposure_cancel_closes_all(): id_gen = MonotonicIntID(seed=23043, increment=1) n = NewOrderCommand(id_gen.id(), 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # should have 1 open exposure assert len(oec.open_exposure_requests()) == 1 assert oec.most_recent_requested_exposure() == Exposure(Price("34.52"), 1000, id_gen.last_id()) cr = CancelReplaceCommand(id_gen.id(), 1234235.863, 2342, "user_x", MARKET, BID_SIDE, Price("34.51"), 800) oec.apply_cancel_replace_command(cr) # now should have 2 open exposures assert len(oec.open_exposure_requests()) == 2 assert oec.open_exposure_requests()[1] == Exposure(Price("34.51"), 800, id_gen.last_id()) assert oec.most_recent_requested_exposure() == Exposure(Price("34.51"), 800, id_gen.last_id()) cancel_command = CancelCommand(id_gen.id(), 1234274.663, 2342, "user_x", MARKET, CancelReasons.USER_CANCEL) oec.apply_cancel_command(cancel_command) # now should have 3 open exposures assert len(oec.open_exposure_requests()) == 3 assert oec.open_exposure_requests()[2] == Exposure(None, 0, id_gen.last_id()) assert oec.most_recent_requested_exposure() == Exposure(None, 0, id_gen.last_id()) cancel_confirm = CancelReport(id_gen.id(), 1234278.663, 2342, "user_x", MARKET, cancel_command, CancelReasons.USER_CANCEL) oec.apply_cancel_report(cancel_confirm) # all exposures should be closed now assert len(oec.open_exposure_requests()) == 0 assert oec.most_recent_requested_exposure() is None assert oec.has_partial_fill() is False
def build_base_order_book(): ob = OrderLevelBook(MARKET, LOGGER) b1 = NewOrderCommand(1, 1234000.123, 1001, "user_a", MARKET, BID_SIDE, FAR, Price("34.50"), 50) bid_oec1 = OrderEventChain(b1, LOGGER, SUBCHAIN_ID_GENERATOR) b1_ack = AcknowledgementReport(2, 1234000.123, 1001, "user_a", MARKET, b1, Price("34.50"), 50, 50) bid_oec1.apply_acknowledgement_report(b1_ack) ob.handle_acknowledgement_report(b1_ack, bid_oec1) a1 = NewOrderCommand(3, 1234000.888, 1002, "user_b", MARKET, ASK_SIDE, FAR, Price("34.52"), 35, 10) ask_oec1 = OrderEventChain(a1, LOGGER, SUBCHAIN_ID_GENERATOR) a1_ack = AcknowledgementReport(4, 1234000.888, 1002, "user_b", MARKET, a1, Price("34.52"), 35, 10) ask_oec1.apply_acknowledgement_report(a1_ack) ob.handle_acknowledgement_report(a1_ack, ask_oec1) a2 = NewOrderCommand(5, 1234001.888, 1003, "user_c", MARKET, ASK_SIDE, FAR, Price("34.52"), 20, 20) ask_oec2 = OrderEventChain(a2, LOGGER, SUBCHAIN_ID_GENERATOR) a2_ack = AcknowledgementReport(6, 1234001.888, 1003, "user_b", MARKET, a2, Price("34.52"), 20, 20) ask_oec2.apply_acknowledgement_report(a2_ack) ob.handle_acknowledgement_report(a2_ack, ask_oec2) b2 = NewOrderCommand(9, 1234000.123, 1004, "user_z", MARKET, BID_SIDE, FAR, Price("34.50"), 70) bid_oec2 = OrderEventChain(b2, LOGGER, SUBCHAIN_ID_GENERATOR) b2_ack = AcknowledgementReport(10, 1234000.123, 1004, "user_z", MARKET, b2, Price("34.50"), 70, 70) bid_oec2.apply_acknowledgement_report(b2_ack) ob.handle_acknowledgement_report(b2_ack, bid_oec2) # do some asserts to ensure we built book as expected bid_prices = ob.prices(BID_SIDE) print(ob.best_bid_price()) print(bid_prices) assert len(bid_prices) == 1 assert bid_prices[0] == Price("34.50") assert ob.best_bid_price() == Price("34.50") assert ob.best_bid_level() == PriceLevel(Price("34.50"), 120, 0, 2) assert ob.best_priority_chain(BID_SIDE) == bid_oec1 ask_prices = ob.ask_prices() assert len(ask_prices) == 1 assert ask_prices[0] == Price("34.52") assert ob.best_ask_price() == Price("34.52") assert ob.best_ask_level() == PriceLevel(Price("34.52"), 30, 25, 2) assert ob.best_priority_chain(ASK_SIDE) == ask_oec1 return ob
def test_subchain_to_json(): # pretty basic, just testing that it doesn't break n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # now ack it ack = AcknowledgementReport(121235, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 1000, None) oec.apply_acknowledgement_report(ack) assert oec.most_recent_event() == ack # now check I can get a to_json of the subchain no problem oec.most_recent_subchain().to_json()
def test_creation(): n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) assert oec.side().is_bid() assert oec.market() == MARKET assert oec.time_in_force() == FAR # no ack yet assert oec.current_exposure() is None assert len(oec.open_exposure_requests()) == 1 assert oec.most_recent_requested_exposure() == Exposure(Price("34.52"), 1000, 121234) # visible qty should be nothing still assert oec.visible_qty() == 0
def test_partial_fill_on_unacked_order(): # when an unacked order is filled the requested exposure gets impacted n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 100) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) assert oec.current_exposure() is None assert oec.most_recent_requested_exposure().qty() == 100 assert oec.most_recent_requested_exposure().price() == Price("34.52") # now resting partial fill pf = PartialFillReport(1212344, 1234237.123, 2342, "user_x", MARKET, n, 10, Price("34.52"), BID_SIDE, 99999, 90) oec.apply_partial_fill_report(pf) assert oec.current_exposure() is None assert oec.most_recent_requested_exposure().qty() == 90 assert oec.most_recent_requested_exposure().price() == Price("34.52")
def test_partial_fill_to_zero_closes_out_order(): # when a partialfill closses out to an order there should be a balking because it is a paritial fill so shouldn't happen, but should allow n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 100) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # now ack it ack = AcknowledgementReport(121235, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 100, 100) oec.apply_acknowledgement_report(ack) aggressor = NewOrderCommand(1111, 1234237.123, 22222, "user_y", MARKET, ASK_SIDE, FAR, Price("34.52"), 100) # now resting partial fill pf = PartialFillReport(1212344, 1234237.123, 2342, "user_x", MARKET, aggressor, 100, Price("34.52"), BID_SIDE, 99999, 0) oec.apply_partial_fill_report(pf) assert oec.open_exposure_requests() == [] assert oec.is_open() is False assert oec.visible_qty() == 0 assert oec.current_exposure() == Exposure(None, 0, 1212344)
def test_full_fill_with_too_much_size_on_unacked_new_order(): # should balk but shouldn't keep it from working n = NewOrderCommand(1, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) assert oec.visible_qty() == 0 assert oec.current_exposure() is None assert oec.most_recent_requested_exposure().price() == Price("34.52") assert oec.most_recent_requested_exposure().qty() == 1000 assert oec.is_open() full_fill = FullFillReport(3, 1234237.123, 2342, "user_x", MARKET, n, 17, Price('34.52'), BID_SIDE, 12345) oec.apply_full_fill_report(full_fill) assert oec.visible_qty() == 0 assert oec.current_exposure().price() is None assert oec.current_exposure().qty() == 0 assert oec.current_exposure().causing_event_id() == 3 assert oec.is_open() is False
def test_basic_full_fill_on_unacked_order(): n = NewOrderCommand(1, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) assert oec.visible_qty() == 0 assert oec.current_exposure() is None assert oec.most_recent_requested_exposure().price() == Price("34.52") assert oec.most_recent_requested_exposure().qty() == 1000 assert oec.is_open() full_fill = FullFillReport(3, 1234237.123, 2342, "user_x", MARKET, n, 1000, Price('34.52'), BID_SIDE, 12345) oec.apply_full_fill_report(full_fill) assert oec.visible_qty() == 0 assert oec.current_exposure().price() is None assert oec.current_exposure().qty() == 0 assert oec.current_exposure().causing_event_id() == 3 assert len(oec.open_exposure_requests()) == 0 assert oec.is_open() is False
def test_subchain_getters_partial_fill_before_ack(): # pretty basic, just testing that it doesn't break n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # now aggressive partial fill pf = PartialFillReport(121236, 1234237.123, 2342, "user_x", MARKET, n, 44, Price("34.52"), BID_SIDE, 99999, 1000 - 44) oec.apply_partial_fill_report(pf) # now ack it ack = AcknowledgementReport(121235, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 1000-44, None) oec.apply_acknowledgement_report(ack) subchain = oec.most_recent_subchain() assert subchain.open_event() == n assert subchain.first_execution_report() == pf assert subchain.fills() == [pf] assert subchain.last_event() == ack
def test_basic_full_fill_on_acked_order(): n = NewOrderCommand(1, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # now ack it ack = AcknowledgementReport(2, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 1000, 1000) oec.apply_acknowledgement_report(ack) assert oec.visible_qty() == 1000 assert oec.current_exposure().qty() == 1000 assert oec.current_exposure().price() == Price("34.52") assert oec.is_open() aggressor = NewOrderCommand(1111, 1234237.123, 22222, "user_y", MARKET, ASK_SIDE, FAR, Price("34.52"), 1000) full_fill = FullFillReport(3, 1234237.123, 2342, "user_x", MARKET, aggressor, 1000, Price('34.52'), BID_SIDE, 12345) oec.apply_full_fill_report(full_fill) assert oec.visible_qty() == 0 assert oec.current_exposure().price() is None assert oec.current_exposure().qty() == 0 assert oec.current_exposure().causing_event_id() == 3 assert oec.is_open() is False
def test_basic_partial_fill(): n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # now ack it ack = AcknowledgementReport(121235, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 1000, 1000) oec.apply_acknowledgement_report(ack) aggressor = NewOrderCommand(1111, 1234237.123, 22222, "user_x", MARKET, ASK_SIDE, FAR, Price("34.52"), 44) # now resting partial fill pf = PartialFillReport(121236, 1234237.123, 2342, "user_x", MARKET, aggressor, 44, Price("34.52"), BID_SIDE, 99999, 1000-44) oec.apply_partial_fill_report(pf) assert oec.open_exposure_requests() == [] assert oec.current_exposure().price() == Price("34.52") assert oec.current_exposure().qty() == 1000-44 assert oec.visible_qty() == 1000-44 assert oec.iceberg_peak_qty() == 1000 # should not have changed assert oec.has_partial_fill()
def test_acking_fak(): # should not allow -- so no changes to order book ob = build_base_order_book() a = NewOrderCommand(56, 1234002.123, 1008, "user_z", MARKET, ASK_SIDE, FAK, Price("34.51"), 35) ask_oec = OrderEventChain(a, LOGGER, SUBCHAIN_ID_GENERATOR) a_ack = AcknowledgementReport(57, 1234002.123, 1008, "user_z", MARKET, a, Price("34.51"), 35, 35) ask_oec.apply_acknowledgement_report(a_ack) ob.handle_acknowledgement_report(a_ack, ask_oec) # NOTHING SHOULD HAVE CHANGED bid_prices = ob.bid_prices() assert len(bid_prices) == 1 assert bid_prices[0] == Price("34.50") assert ob.best_bid_price() == Price("34.50") assert ob.best_bid_level() == PriceLevel(Price("34.50"), 120, 0, 2) ask_prices = ob.ask_prices() assert len(ask_prices) == 1 assert ask_prices[0] == Price("34.52") assert ob.best_ask_price() == Price("34.52") assert ob.best_ask_level() == PriceLevel(Price("34.52"), 30, 25, 2)
def test_establish_new_ask_tob(): ob = build_base_order_book() a = NewOrderCommand(56, 1234002.123, 1008, "user_z", MARKET, ASK_SIDE, FAR, Price("34.51"), 35) ask_oec = OrderEventChain(a, LOGGER, SUBCHAIN_ID_GENERATOR) a_ack = AcknowledgementReport(57, 1234002.123, 1008, "user_z", MARKET, a, Price("34.51"), 35, 35) ask_oec.apply_acknowledgement_report(a_ack) ob.handle_acknowledgement_report(a_ack, ask_oec) # ask should have changed ask_prices = ob.ask_prices() assert len(ask_prices) == 2 # now 2 prices assert ob.best_priority_chain(ASK_SIDE) == ask_oec # the bid order chain above should now be best assert ask_prices[0] == Price("34.51") # should be new best price assert ob.best_ask_price() == Price("34.51") # should be new best price assert ob.best_ask_level() == PriceLevel(Price("34.51"), 35, 0, 1) # should have new best price level with only 1 order # bid should not have changed bid_prices = ob.bid_prices() assert len(bid_prices) == 1 assert bid_prices[0] == Price("34.50") assert ob.best_bid_price() == Price("34.50") assert ob.best_bid_level() == PriceLevel(Price("34.50"), 120, 0, 2)
def test_cancel_confirm_entire_price_level(): ob = build_base_order_book() # first add an order at a second price (34.53) a = NewOrderCommand(56, 1234002.123, 1009, "user_x", MARKET, ASK_SIDE, FAR, Price("34.53"), 20) ask_oec = OrderEventChain(a, LOGGER, SUBCHAIN_ID_GENERATOR) a_ack = AcknowledgementReport(57, 1234002.123, 1009, "user_z", MARKET, a, Price("34.53"), 20, 20) ask_oec.apply_acknowledgement_report(a_ack) ob.handle_acknowledgement_report(a_ack, ask_oec) # cancel the orderchains at best price id_generator = MonotonicIntID() for i, chain in enumerate(ob.order_chains_at_price(ASK_SIDE, ob.best_price(ASK_SIDE))): cancel_command = CancelCommand(id_generator.id(), 1234002.123, chain.chain_id(), chain.user_id(), MARKET, USER_CANCEL) chain.apply_cancel_command(cancel_command) cancel_report = CancelReport(id_generator.id(), 1234002.123, chain.chain_id(), chain.user_id(), MARKET, cancel_command, USER_CANCEL) chain.apply_cancel_report(cancel_report) ob.handle_cancel_report(cancel_report, chain) assert ob.best_priority_chain(ASK_SIDE).chain_id() == ask_oec.chain_id() # best price should now be 34.53 assert ob.best_ask_price() == Price('34.53') # visible qty is 20 assert ob.visible_qty_at_price(ASK_SIDE, ob.best_ask_price()) == 20 # hidden qty is 0 assert ob.hidden_qty_at_price(ASK_SIDE, ob.best_ask_price()) == 0 # num orders is 1 assert ob.num_orders_at_price(ASK_SIDE, ob.best_ask_price()) == 1 # bids shouldn't change bid_prices = ob.bid_prices() assert len(bid_prices) == 1 assert bid_prices[0] == Price("34.50") assert ob.best_bid_price() == Price("34.50") assert ob.best_bid_level() == PriceLevel(Price("34.50"), 120, 0, 2)
def test_new_iceberg_order_ack(): n = NewOrderCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000, 50) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # no ack yet assert oec.most_recent_event() == n # check exposure assert len(oec.open_exposure_requests()) == 1 assert oec.most_recent_requested_exposure() == oec.open_exposure_requests()[-1] assert oec.current_exposure() is None assert oec.most_recent_requested_exposure() == Exposure(Price("34.52"), 1000, 121234) # visible qty should be nothing still assert oec.visible_qty() == 0 # now ack it ack = AcknowledgementReport(121235, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 1000, 50) oec.apply_acknowledgement_report(ack) assert oec.most_recent_event() == ack # check exposure assert len(oec.open_exposure_requests()) == 0 assert oec.most_recent_requested_exposure() is None assert oec.current_exposure() == Exposure(Price("34.52"), 1000, 121235) # check visible qty assert oec.visible_qty() == 50
def test_cancel_replace_order_up_in_qty(): ob = build_base_order_book() # add a third order to the base order book bid side b = NewOrderCommand(56, 1234002.123, 1008, "user_z", MARKET, BID_SIDE, FAR, Price("34.50"), 35) bid_oec = OrderEventChain(b, LOGGER, SUBCHAIN_ID_GENERATOR) b_ack = AcknowledgementReport(57, 1234002.123, 1008, "user_z", MARKET, b, Price("34.50"), 35, 35) bid_oec.apply_acknowledgement_report(b_ack) ob.handle_acknowledgement_report(b_ack, bid_oec) # should now be three order chains prev_order_chains = ob.order_chains_at_price(BID_SIDE, Price("34.50")) prev_visible_qty_at_tob = ob.visible_qty_at_price(BID_SIDE, Price("34.50")) prev_hidden_qty_at_tob = ob.hidden_qty_at_price(BID_SIDE, Price("34.50")) assert len(prev_order_chains) == 3 # and test correct order assert prev_order_chains[0].chain_id() == 1001 assert prev_order_chains[1].chain_id() == 1004 assert prev_order_chains[2].chain_id() == 1008 assert ob.best_priority_chain(BID_SIDE).chain_id() == 1001 # cancel replace the first orderchain up in qty cr = CancelReplaceCommand(77, 1234012.123, prev_order_chains[0].chain_id(), prev_order_chains[0].user_id(), MARKET, prev_order_chains[0].side(), prev_order_chains[0].current_price(), prev_order_chains[0].current_qty() + 40) prev_order_chains[0].apply_cancel_replace_command(cr) cr_ack = AcknowledgementReport(78, 1234012.123, prev_order_chains[0].chain_id(), "user_z", MARKET, cr, prev_order_chains[0].current_price(), prev_order_chains[0].current_qty() + 40, prev_order_chains[0].current_qty() + 40) prev_order_chains[0].apply_acknowledgement_report(cr_ack) ob.handle_acknowledgement_report(cr_ack, prev_order_chains[0]) # should still only be one price on bid side as no price changes` assert len(ob.prices(BID_SIDE)) == 1 # and that best price is still 34.50 assert ob.best_price(BID_SIDE) == Price("34.50") # visible qty on the bid tob should have gone up by 40 assert ob.visible_qty_at_price(BID_SIDE, Price("34.50")) == prev_visible_qty_at_tob + 40 # hidden qty should not have gone up at all assert ob.hidden_qty_at_price(BID_SIDE, Price("34.50")) == prev_hidden_qty_at_tob, str( ob.hidden_qty_at_price(BID_SIDE, Price("34.50"))) + " != " + str(prev_hidden_qty_at_tob) # there should still be three but the order should be different current_order_chains = ob.order_chains_at_price(BID_SIDE, Price("34.50")) assert len(current_order_chains) == 3 assert current_order_chains[0].chain_id() == 1004, "First chain at level should be 1004" assert current_order_chains[1].chain_id() == 1008, "Second chain at level should be 1008" assert current_order_chains[2].chain_id() == 1001, "Third chain at level should be 1001" # best priority order chain should now be 1004 assert ob.best_priority_chain(BID_SIDE).chain_id() == 1004, "Best priority chain in order book should be 1004" prev_visible_qty_at_tob = ob.visible_qty_at_price(BID_SIDE, Price("34.50")) prev_hidden_qty_at_tob = ob.hidden_qty_at_price(BID_SIDE, Price("34.50")) # now move the middle order up in qty, which should move it to back of list cr2 = CancelReplaceCommand(79, 1234052.123, current_order_chains[1].chain_id(), current_order_chains[1].user_id(), MARKET, current_order_chains[1].side(), current_order_chains[1].current_price(), current_order_chains[1].current_qty() + 100, current_order_chains[1].current_qty() + 50) current_order_chains[1].apply_cancel_replace_command(cr2) cr_ack2 = AcknowledgementReport(80, 1234062.123, current_order_chains[1].chain_id(), current_order_chains[1].user_id(), MARKET, cr2, current_order_chains[1].current_price(), current_order_chains[1].current_qty() + 100, current_order_chains[1].current_qty() + 50) current_order_chains[1].apply_acknowledgement_report(cr_ack2) ob.handle_acknowledgement_report(cr_ack2, current_order_chains[1]) # should still only be one price on bid side as no price changes` assert len(ob.prices(BID_SIDE)) == 1 # and that best price is still 34.50 assert ob.best_price(BID_SIDE) == Price("34.50") # visible qty on the bid tob should have gone up by 40 assert ob.visible_qty_at_price(BID_SIDE, Price("34.50")) == prev_visible_qty_at_tob + 50 # hidden qty should not have gone up at all assert ob.hidden_qty_at_price(BID_SIDE, Price("34.50")) == prev_hidden_qty_at_tob + 50, str( ob.hidden_qty_at_price(BID_SIDE, Price("34.50"))) + " != " + str(prev_hidden_qty_at_tob + 50) # there should still be three but the order should be different current_order_chains = ob.order_chains_at_price(BID_SIDE, Price("34.50")) assert len(current_order_chains) == 3 assert current_order_chains[0].chain_id() == 1004, "First chain at level should be 1004" assert current_order_chains[1].chain_id() == 1001, "Second chain at level should be 1001" assert current_order_chains[2].chain_id() == 1008, "Third chain at level should be 1008" # best priority order chain should now be 1004 assert ob.best_priority_chain(BID_SIDE).chain_id() == 1004, "Best priority chain in order book should be 1004"
def test_creation_without_neworder(): with pytest.raises(AssertionError): cr = CancelReplaceCommand(121234, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, Price("43.01"), 234) OrderEventChain(cr, LOGGER, MonotonicIntID())
def test_populating_tob(): ob = OrderLevelBook(MARKET, LOGGER) b1 = NewOrderCommand(1, 1234000.123, 1001, "user_a", MARKET, BID_SIDE, FAR, Price("34.50"), 50) bid_oec1 = OrderEventChain(b1, LOGGER, SUBCHAIN_ID_GENERATOR) b1_ack = AcknowledgementReport(2, 1234000.123, 1001, "user_a", MARKET, b1, Price("34.50"), 50, 50) bid_oec1.apply_acknowledgement_report(b1_ack) ob.handle_acknowledgement_report(b1_ack, bid_oec1) assert ob.best_bid_price() == Price("34.50") assert ob.best_priority_chain(BID_SIDE).chain_id() == bid_oec1.chain_id() assert ob.best_bid_level().price() == Price("34.50") assert ob.best_bid_level().visible_qty() == 50 assert ob.best_bid_level().hidden_qty() == 0 # now add an offer a1 = NewOrderCommand(3, 1234000.888, 1002, "user_b", MARKET, ASK_SIDE, FAR, Price("34.52"), 35, 10) ask_oec1 = OrderEventChain(a1, LOGGER, SUBCHAIN_ID_GENERATOR) a1_ack = AcknowledgementReport(4, 1234000.888, 1002, "user_b", MARKET, a1, Price("34.52"), 35, 10) ask_oec1.apply_acknowledgement_report(a1_ack) ob.handle_acknowledgement_report(a1_ack, ask_oec1) assert ob.best_ask_price() == Price("34.52") assert ob.best_priority_chain(ASK_SIDE).chain_id() == ask_oec1.chain_id() assert ob.best_ask_level().price() == Price("34.52") assert ob.best_ask_level().visible_qty() == 10 assert ob.best_ask_level().hidden_qty() == 25 assert ob.best_ask_level().number_of_orders() == 1 # bid should not have changed assert ob.best_bid_price() == Price("34.50") assert ob.best_priority_chain(BID_SIDE).chain_id() == bid_oec1.chain_id() assert ob.best_bid_level().price() == Price("34.50") assert ob.best_bid_level().visible_qty() == 50 assert ob.best_bid_level().hidden_qty() == 0 assert ob.best_ask_level().number_of_orders() == 1 assert ob.num_orders_at_price(BID_SIDE, Price("34.50")) == 1 # add a second ask order to top of book a2 = NewOrderCommand(5, 1234001.888, 1003, "user_c", MARKET, ASK_SIDE, FAR, Price("34.52"), 20, 20) ask_oec2 = OrderEventChain(a2, LOGGER, SUBCHAIN_ID_GENERATOR) a2_ack = AcknowledgementReport(6, 1234001.888, 1003, "user_b", MARKET, a2, Price("34.52"), 20, 20) ask_oec2.apply_acknowledgement_report(a2_ack) ob.handle_acknowledgement_report(a2_ack, ask_oec2) assert ob.best_ask_price() == Price("34.52") # best price has not changed assert ob.best_priority_chain(ASK_SIDE).chain_id() == ask_oec1.chain_id() # best priority chain has not changed assert ob.best_ask_level().price() == Price("34.52") # price of best level has not changed print(ob.best_ask_level().visible_qty()) assert ob.best_ask_level().visible_qty() == 10 + 20 # visible qty should have got up by 20 assert ob.best_ask_level().hidden_qty() == 25 # hidden qty should not have changed assert ob.best_ask_level().number_of_orders() == 2 # num orders should go up 2 assert ob.num_orders_at_price(ASK_SIDE, Price("34.52")) == 2 # bid should not have changed assert ob.best_bid_price() == Price("34.50") assert ob.best_priority_chain(BID_SIDE).chain_id() == bid_oec1.chain_id() assert ob.best_bid_level().price() == Price("34.50") assert ob.best_bid_level().visible_qty() == 50 assert ob.best_bid_level().hidden_qty() == 0 assert ob.best_bid_level().number_of_orders() == 1 assert ob.num_orders_at_price(BID_SIDE, Price("34.50")) == 1 # add a new bid at tob price b2 = NewOrderCommand(9, 1234000.123, 1004, "user_z", MARKET, BID_SIDE, FAR, Price("34.50"), 70) bid_oec2 = OrderEventChain(b2, LOGGER, SUBCHAIN_ID_GENERATOR) b2_ack = AcknowledgementReport(10, 1234000.123, 1004, "user_z", MARKET, b2, Price("34.50"), 70, 70) bid_oec2.apply_acknowledgement_report(b2_ack) ob.handle_acknowledgement_report(b2_ack, bid_oec2) assert ob.best_bid_price() == Price("34.50") assert ob.best_priority_chain(BID_SIDE).chain_id() == bid_oec1.chain_id() assert ob.best_bid_level().price() == Price("34.50") assert ob.best_bid_level().visible_qty() == 120 assert ob.best_bid_level().hidden_qty() == 0 assert ob.best_ask_level().number_of_orders() == 2 assert ob.num_orders_at_price(BID_SIDE, Price("34.50")) == 2 # asks should not have changed assert ob.best_ask_price() == Price("34.52") # best price has not changed assert ob.best_priority_chain(ASK_SIDE).chain_id() == ask_oec1.chain_id() # best priority chain has not changed assert ob.best_ask_level().price() == Price("34.52") # price of best level has not changed assert ob.best_ask_level().visible_qty() == 10 + 20 # visible qty should have got up by 20 assert ob.best_ask_level().hidden_qty() == 25 # hidden qty should not have changed assert ob.best_ask_level().number_of_orders() == 2 # num orders should go up 2 assert ob.num_orders_at_price(ASK_SIDE, Price("34.52")) == 2
def test_partial_fill_on_multiple_unacked_requests(): n = NewOrderCommand(1, 1234235.123, 2342, "user_x", MARKET, BID_SIDE, FAR, Price("34.52"), 1000) oec = OrderEventChain(n, LOGGER, MonotonicIntID()) # should have 1 open exposure assert len(oec.open_exposure_requests()) == 1 assert oec.most_recent_requested_exposure() == Exposure(Price("34.52"), 1000, 1) cr1 = CancelReplaceCommand(2, 1234235.863, 2342, "user_x", MARKET, BID_SIDE, Price("34.51"), 800) oec.apply_cancel_replace_command(cr1) # now should have 2 open exposures assert len(oec.open_exposure_requests()) == 2 assert oec.open_exposure_requests()[0] == Exposure(Price("34.52"), 1000, 1) assert oec.open_exposure_requests()[1] == Exposure(Price("34.51"), 800, 2) cr2 = CancelReplaceCommand(3, 1234236.842, 2342, "user_x", MARKET, BID_SIDE, Price("34.55"), 800) oec.apply_cancel_replace_command(cr2) # now should have 2 open exposures assert len(oec.open_exposure_requests()) == 3 assert oec.open_exposure_requests()[0] == Exposure(Price("34.52"), 1000, 1) assert oec.open_exposure_requests()[1] == Exposure(Price("34.51"), 800, 2) assert oec.open_exposure_requests()[2] == Exposure(Price("34.55"), 800, 3) cr3 = CancelReplaceCommand(4, 1234236.842, 2342, "user_x", MARKET, BID_SIDE, Price("34.56"), 800) oec.apply_cancel_replace_command(cr3) # now should have 2 open exposures assert len(oec.open_exposure_requests()) == 4 assert oec.open_exposure_requests()[0] == Exposure(Price("34.52"), 1000, 1) assert oec.open_exposure_requests()[1] == Exposure(Price("34.51"), 800, 2) assert oec.open_exposure_requests()[2] == Exposure(Price("34.55"), 800, 3) assert oec.open_exposure_requests()[3] == Exposure(Price("34.56"), 800, 4) # a partial fill should should only impact the one the partial fill is for # partially filling orderid 3 (cr2) pf1 = PartialFillReport(5, 1234237.123, 2342, "user_x", MARKET, cr2, 10, Price("34.55"), BID_SIDE, 999, 790) oec.apply_partial_fill_report(pf1) assert len(oec.open_exposure_requests()) == 4 assert oec.open_exposure_requests()[0] == Exposure(Price("34.52"), 1000, 1) assert oec.open_exposure_requests()[1] == Exposure(Price("34.51"), 800, 2) assert oec.open_exposure_requests()[2] == Exposure(Price("34.55"), 790, 3) assert oec.open_exposure_requests()[3] == Exposure(Price("34.56"), 800, 4) # and again pf2 = PartialFillReport(6, 1234237.123, 2342, "user_x", MARKET, cr2, 10, Price("34.55"), BID_SIDE, 1000, 780) oec.apply_partial_fill_report(pf2) assert len(oec.open_exposure_requests()) == 4 assert oec.open_exposure_requests()[0] == Exposure(Price("34.52"), 1000, 1) assert oec.open_exposure_requests()[1] == Exposure(Price("34.51"), 800, 2) assert oec.open_exposure_requests()[2] == Exposure(Price("34.55"), 780, 3) assert oec.open_exposure_requests()[3] == Exposure(Price("34.56"), 800, 4) # and now I can fill order id 4 (cr 3) pf3 = PartialFillReport(6, 1234237.123, 2342, "user_x", MARKET, cr3, 50, Price("34.56"), BID_SIDE, 1001, 750) oec.apply_partial_fill_report(pf3) assert len(oec.open_exposure_requests()) == 4 assert oec.open_exposure_requests()[0] == Exposure(Price("34.52"), 1000, 1) assert oec.open_exposure_requests()[1] == Exposure(Price("34.51"), 800, 2) assert oec.open_exposure_requests()[2] == Exposure(Price("34.55"), 780, 3) assert oec.open_exposure_requests()[3] == Exposure(Price("34.56"), 750, 4) # now start acking them ack1 = AcknowledgementReport(10, 1234235.123, 2342, "user_x", MARKET, n, Price("34.52"), 1000, None) oec.apply_acknowledgement_report(ack1) assert len(oec.open_exposure_requests()) == 3 assert oec.open_exposure_requests()[0] == Exposure(Price("34.51"), 800, 2) assert oec.open_exposure_requests()[1] == Exposure(Price("34.55"), 780, 3) assert oec.open_exposure_requests()[2] == Exposure(Price("34.56"), 750, 4) assert oec.current_exposure() == Exposure(Price("34.52"), 1000, 10) ack2 = AcknowledgementReport(11, 1234235.123, 2342, "user_x", MARKET, cr1, Price("34.51"), 800, None) oec.apply_acknowledgement_report(ack2) assert len(oec.open_exposure_requests()) == 2 assert oec.open_exposure_requests()[0] == Exposure(Price("34.55"), 780, 3) assert oec.open_exposure_requests()[1] == Exposure(Price("34.56"), 750, 4) assert oec.current_exposure() == Exposure(Price("34.51"), 800, 11) ack3 = AcknowledgementReport(12, 1234235.123, 2342, "user_x", MARKET, cr2, Price("34.55"), 780, None) oec.apply_acknowledgement_report(ack3) assert len(oec.open_exposure_requests()) == 1 print(oec.open_exposure_requests()[0]) assert oec.open_exposure_requests()[0] == Exposure(Price("34.56"), 750, 4) assert oec.current_exposure() == Exposure(Price("34.55"), 780, 12) ack4 = AcknowledgementReport(13, 1234235.123, 2342, "user_x", MARKET, cr3, Price("34.56"), 750, None) oec.apply_acknowledgement_report(ack4) assert len(oec.open_exposure_requests()) == 0 assert oec.current_exposure() == Exposure(Price("34.56"), 750, 13)
def _create_new_event_chain(self, new_order_command): order_chain = OrderEventChain( new_order_command, self._logger, subchain_id_generator=self._sub_chain_id_generator) return order_chain