def test_to_dict_returns_expected_dict(self): # Arrange order = Order(price=10, size=5, side=OrderSide.BUY, id="1") delta = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order, ts_event=0, ts_init=0, ) # Act result = OrderBookDelta.to_dict(delta) # Assert assert result == { "type": "OrderBookDelta", "instrument_id": "AUD/USD.SIM", "book_type": "L2_MBP", "action": "ADD", "order_id": "1", "order_price": 10.0, "order_side": "BUY", "order_size": 5.0, "update_id": 0, "ts_event": 0, "ts_init": 0, }
def test_from_dict_returns_expected_tick(self): # Arrange order1 = Order(price=10, size=5, side=OrderSide.BUY, id="1") delta1 = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order1, ts_event=0, ts_init=0, ) order2 = Order(price=10, size=15, side=OrderSide.BUY, id="2") delta2 = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order2, ts_event=0, ts_init=0, ) deltas = OrderBookDeltas( instrument_id=AUDUSD, book_type=BookType.L2_MBP, deltas=[delta1, delta2], ts_event=0, ts_init=0, ) # Act result = OrderBookDeltas.from_dict(OrderBookDeltas.to_dict(deltas)) # Assert assert result == deltas
def test_apply(empty_l2_book, clock): snapshot = OrderBookSnapshot( instrument_id=empty_l2_book.instrument_id, book_type=BookType.L2_MBP, bids=[[150.0, 0.51]], asks=[[160.0, 1.51]], ts_event=0, ts_init=0, ) empty_l2_book.apply_snapshot(snapshot) assert empty_l2_book.best_ask_price() == 160 delta = OrderBookDelta( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, action=BookAction.ADD, order=Order( 155.0, 672.45, OrderSide.SELL, "4a25c3f6-76e7-7584-c5a3-4ec84808e240", ), ts_event=clock.timestamp(), ts_init=clock.timestamp(), ) empty_l2_book.apply(delta) assert empty_l2_book.best_ask_price() == 155
def parse_book_delta_ws( instrument_id: InstrumentId, side: OrderSide, delta: Tuple[str, str], ts_event: int, ts_init: int, update_id: int, ) -> OrderBookDelta: price = float(delta[0]) size = float(delta[1]) order = Order( price=price, size=size, side=side, ) return OrderBookDelta( instrument_id=instrument_id, book_type=BookType.L2_MBP, action=BookAction.UPDATE if size > 0.0 else BookAction.DELETE, order=order, ts_event=ts_event, ts_init=ts_init, update_id=update_id, )
def test_from_dict_returns_expected_clear(self): # Arrange delta = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.CLEAR, order=None, ts_event=0, ts_init=0, ) # Act result = OrderBookDelta.from_dict(OrderBookDelta.to_dict(delta)) # Assert assert result == delta
def test_from_dict_returns_expected_delta(self): # Arrange order = Order(price=10, size=5, side=OrderSide.BUY) delta = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order, ts_event=0, ts_init=0, ) # Act result = OrderBookDelta.from_dict(OrderBookDelta.to_dict(delta)) # Assert assert result == delta
def _build_order_book_deltas(values): return OrderBookDeltas( instrument_id=InstrumentId.from_str(values[0]["instrument_id"]), book_type=BookTypeParser.from_str_py(values[0]["book_type"]), deltas=[OrderBookDelta.from_dict(v) for v in values], ts_event=values[0]["ts_event"], ts_init=values[0]["ts_init"], )
def order_book_delta(order=None): return OrderBookDelta( instrument_id=TestIdStubs.audusd_id(), book_type=BookType.L2_MBP, action=BookAction.ADD, order=order or TestDataStubs.order(), ts_event=0, ts_init=0, )
def test_to_dict_returns_expected_dict(self): # Arrange order1 = Order(price=10, size=5, side=OrderSide.BUY, id="1") delta1 = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order1, ts_event=0, ts_init=0, ) order2 = Order(price=10, size=15, side=OrderSide.BUY, id="2") delta2 = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order2, ts_event=0, ts_init=0, ) deltas = OrderBookDeltas( instrument_id=AUDUSD, book_type=BookType.L2_MBP, deltas=[delta1, delta2], ts_event=0, ts_init=0, ) # Act result = OrderBookDeltas.to_dict(deltas) # Assert assert result == { "type": "OrderBookDeltas", "instrument_id": "AUD/USD.SIM", "book_type": "L2_MBP", "deltas": b'[{"type":"OrderBookDelta","instrument_id":"AUD/USD.SIM","book_type":"L2_MBP","action":"ADD","order_price":10.0,"order_size":5.0,"order_side":"BUY","order_id":"1","update_id":0,"ts_event":0,"ts_init":0},{"type":"OrderBookDelta","instrument_id":"AUD/USD.SIM","book_type":"L2_MBP","action":"ADD","order_price":10.0,"order_size":15.0,"order_side":"BUY","order_id":"2","update_id":0,"ts_event":0,"ts_init":0}]', # noqa "update_id": 0, "ts_event": 0, "ts_init": 0, }
def serialize(data: OrderBookData): if isinstance(data, OrderBookDelta): result = [_parse_delta(delta=data, cls=OrderBookDelta)] elif isinstance(data, OrderBookDeltas): result = [_parse_delta(delta=delta, cls=OrderBookDeltas) for delta in data.deltas] elif isinstance(data, OrderBookSnapshot): # For a snapshot, we store the individual deltas required to rebuild, namely a CLEAR, followed by ADDs result = [ _parse_delta( OrderBookDelta( instrument_id=data.instrument_id, book_type=data.book_type, order=None, action=BookAction.CLEAR, ts_event=data.ts_event, ts_init=data.ts_init, ), cls=OrderBookSnapshot, ) ] orders = list(zip(repeat(OrderSide.BUY), data.bids)) + list( zip(repeat(OrderSide.SELL), data.asks) ) result.extend( [ _parse_delta( OrderBookDelta( instrument_id=data.instrument_id, book_type=data.book_type, ts_event=data.ts_event, ts_init=data.ts_init, order=Order(price=price, size=volume, side=side), action=BookAction.ADD, ), cls=OrderBookSnapshot, ) for side, (price, volume) in orders ] ) else: # pragma: no cover (design-time error) raise TypeError(f"invalid OrderBookData type, was {type(data)}") # Add a "last" message to let downstream consumers know the end of this group of messages result[-1]["_last"] = True return result
def test_hash_str_and_repr(self): # Arrange order1 = Order(price=10, size=5, side=OrderSide.BUY, id="1") delta1 = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order1, ts_event=0, ts_init=0, ) order2 = Order(price=10, size=15, side=OrderSide.BUY, id="2") delta2 = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order2, ts_event=0, ts_init=0, ) deltas = OrderBookDeltas( instrument_id=AUDUSD, book_type=BookType.L2_MBP, deltas=[delta1, delta2], ts_event=0, ts_init=0, ) # Act, Assert assert isinstance(hash(deltas), int) assert ( str(deltas) == "OrderBookDeltas('AUD/USD.SIM', book_type=L2_MBP, [OrderBookDelta('AUD/USD.SIM', book_type=L2_MBP, action=ADD, order=Order(10.0, 5.0, BUY, 1), update_id=0, ts_event=0, ts_init=0), OrderBookDelta('AUD/USD.SIM', book_type=L2_MBP, action=ADD, order=Order(10.0, 15.0, BUY, 2), update_id=0, ts_event=0, ts_init=0)], update_id=0, ts_event=0, ts_init=0)" # noqa ) assert ( repr(deltas) == "OrderBookDeltas('AUD/USD.SIM', book_type=L2_MBP, [OrderBookDelta('AUD/USD.SIM', book_type=L2_MBP, action=ADD, order=Order(10.0, 5.0, BUY, 1), update_id=0, ts_event=0, ts_init=0), OrderBookDelta('AUD/USD.SIM', book_type=L2_MBP, action=ADD, order=Order(10.0, 15.0, BUY, 2), update_id=0, ts_event=0, ts_init=0)], update_id=0, ts_event=0, ts_init=0)" # noqa )
def test_serialize_and_deserialize_order_book_deltas(self): kw = { "instrument_id": "AUD/USD.SIM", "ts_event": 0, "ts_init": 0, "book_type": "L2_MBP", } deltas = OrderBookDeltas( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, deltas=[ OrderBookDelta.from_dict({ "action": "ADD", "order_side": "BUY", "order_price": 8.0, "order_size": 30.0, "order_id": "e0364f94-8fcb-0262-cbb3-075c51ee4917", **kw, }), OrderBookDelta.from_dict({ "action": "ADD", "order_side": "SELL", "order_price": 15.0, "order_size": 10.0, "order_id": "cabec174-acc6-9204-9ebf-809da3896daf", **kw, }), ], ts_event=0, ts_init=0, ) serialized = ParquetSerializer.serialize(deltas) deserialized = ParquetSerializer.deserialize(cls=OrderBookDeltas, chunk=serialized) # Assert assert deserialized == [deltas] write_objects(catalog=self.catalog, chunk=[deltas])
def test_timestamp_ns(empty_l2_book, clock): delta = OrderBookDelta( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, action=BookAction.ADD, order=Order( 0.5900, 672.45, OrderSide.SELL, "4a25c3f6-76e7-7584-c5a3-4ec84808e240", ), ts_event=clock.timestamp(), ts_init=clock.timestamp(), ) empty_l2_book.apply_delta(delta) assert empty_l2_book.ts_last == delta.ts_init
def test_orderbook_operation_update(empty_l2_book, clock): delta = OrderBookDelta( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, action=BookAction.UPDATE, order=Order( 0.5814, 672.45, OrderSide.SELL, "4a25c3f6-76e7-7584-c5a3-4ec84808e240", ), ts_event=clock.timestamp(), ts_init=clock.timestamp(), ) empty_l2_book.apply_delta(delta) assert empty_l2_book.best_ask_price() == 0.5814
def test_orderbook_operations(empty_l2_book): delta = OrderBookDelta( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, action=BookAction.UPDATE, order=Order( 0.5814, 672.45, OrderSide.SELL, "4a25c3f6-76e7-7584-c5a3-4ec84808e240", ), ts_event=pd.Timestamp.utcnow().timestamp() * 1e9, ts_init=pd.Timestamp.utcnow().timestamp() * 1e9, ) deltas = OrderBookDeltas( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, deltas=[delta], ts_event=pd.Timestamp.utcnow().timestamp() * 1e9, ts_init=pd.Timestamp.utcnow().timestamp() * 1e9, ) empty_l2_book.apply_deltas(deltas) assert empty_l2_book.best_ask_price() == 0.5814
def test_hash_str_and_repr(self): # Arrange order = Order(price=10, size=5, side=OrderSide.BUY) delta = OrderBookDelta( instrument_id=AUDUSD, book_type=BookType.L2_MBP, action=BookAction.ADD, order=order, update_id=123456789, ts_event=0, ts_init=1_000_000_000, ) # Act, Assert assert isinstance(hash(delta), int) assert ( str(delta) == f"OrderBookDelta('AUD/USD.SIM', book_type=L2_MBP, action=ADD, order=Order(10.0, 5.0, BUY, {order.id}), update_id=123456789, ts_event=0, ts_init=1000000000)" # noqa ) assert ( repr(delta) == f"OrderBookDelta('AUD/USD.SIM', book_type=L2_MBP, action=ADD, order=Order(10.0, 5.0, BUY, {order.id}), update_id=123456789, ts_event=0, ts_init=1000000000)" # noqa )
def _handle_book_updates(runner, instrument, ts_event, ts_init): deltas = [] for side in B_SIDE_KINDS: for upd in runner.get(side, []): # TODO(bm): Clean this up if len(upd) == 3: _, price, volume = upd else: price, volume = upd if price == 0.0: continue deltas.append( OrderBookDelta( instrument_id=instrument.id, book_type=BookType.L2_MBP, action=BookAction.DELETE if volume == 0 else BookAction.UPDATE, order=Order( price=price_to_probability(str(price)), size=Quantity(volume, precision=8), side=B2N_MARKET_STREAM_SIDE[side], ), ts_event=ts_event, ts_init=ts_init, )) if deltas: ob_update = OrderBookDeltas( book_type=BookType.L2_MBP, instrument_id=instrument.id, deltas=deltas, ts_event=ts_event, ts_init=ts_init, ) return [ob_update] else: return []
def test_serialize_and_deserialize_order_book_delta(self): delta = OrderBookDelta( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, action=BookAction.CLEAR, order=None, ts_event=0, ts_init=0, ) serialized = ParquetSerializer.serialize(delta) [deserialized] = ParquetSerializer.deserialize(cls=OrderBookDelta, chunk=serialized) # Assert expected = OrderBookDeltas( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, deltas=[delta], ts_event=0, ts_init=0, ) assert deserialized == expected write_objects(catalog=self.catalog, chunk=[delta])
def test_add_order_book_deltas_adds_to_engine(self, capsys): # Arrange engine = BacktestEngine() engine.add_instrument(AUDUSD_SIM) engine.add_instrument(ETHUSDT_BINANCE) deltas = [ OrderBookDelta( instrument_id=AUDUSD_SIM.id, book_type=BookType.L2_MBP, action=BookAction.ADD, order=Order( price=Price.from_str("13.0"), size=Quantity.from_str("40"), side=OrderSide.SELL, ), ts_event=0, ts_init=0, ), OrderBookDelta( instrument_id=AUDUSD_SIM.id, book_type=BookType.L2_MBP, action=BookAction.ADD, order=Order( price=Price.from_str("12.0"), size=Quantity.from_str("30"), side=OrderSide.SELL, ), ts_event=0, ts_init=0, ), OrderBookDelta( instrument_id=AUDUSD_SIM.id, book_type=BookType.L2_MBP, action=BookAction.ADD, order=Order( price=Price.from_str("11.0"), size=Quantity.from_str("20"), side=OrderSide.SELL, ), ts_event=0, ts_init=0, ), OrderBookDelta( instrument_id=AUDUSD_SIM.id, book_type=BookType.L2_MBP, action=BookAction.ADD, order=Order( price=Price.from_str("10.0"), size=Quantity.from_str("20"), side=OrderSide.BUY, ), ts_event=0, ts_init=0, ), OrderBookDelta( instrument_id=AUDUSD_SIM.id, book_type=BookType.L2_MBP, action=BookAction.ADD, order=Order( price=Price.from_str("9.0"), size=Quantity.from_str("30"), side=OrderSide.BUY, ), ts_event=0, ts_init=0, ), OrderBookDelta( instrument_id=AUDUSD_SIM.id, book_type=BookType.L2_MBP, action=BookAction.ADD, order=Order( price=Price.from_str("0.0"), size=Quantity.from_str("40"), side=OrderSide.BUY, ), ts_event=0, ts_init=0, ), ] operations1 = OrderBookDeltas( instrument_id=ETHUSDT_BINANCE.id, book_type=BookType.L2_MBP, deltas=deltas, ts_event=0, ts_init=0, ) operations2 = OrderBookDeltas( instrument_id=ETHUSDT_BINANCE.id, book_type=BookType.L2_MBP, deltas=deltas, ts_event=1000, ts_init=1000, ) # Act engine.add_order_book_data([operations2, operations1]) # <-- not sorted # Assert log = "".join(capsys.readouterr()) assert "Added 2 ETHUSDT.BINANCE OrderBookData elements." in log
def test_serialize_and_deserialize_order_book_deltas_grouped(self): kw = { "instrument_id": "AUD/USD.SIM", "ts_event": 0, "ts_init": 0, "book_type": "L2_MBP", } deltas = [ { "action": "ADD", "order_side": "SELL", "order_price": 0.9901, "order_size": 327.25, "order_id": "1", }, { "action": "CLEAR", "order_side": None, "order_price": None, "order_size": None, "order_id": None, }, { "action": "ADD", "order_side": "SELL", "order_price": 0.98039, "order_size": 27.91, "order_id": "2", }, { "action": "ADD", "order_side": "SELL", "order_price": 0.97087, "order_size": 14.43, "order_id": "3", }, ] deltas = OrderBookDeltas( instrument_id=TestStubs.audusd_id(), book_type=BookType.L2_MBP, deltas=[OrderBookDelta.from_dict({ **kw, **d }) for d in deltas], ts_event=0, ts_init=0, ) serialized = ParquetSerializer.serialize(deltas) [deserialized] = ParquetSerializer.deserialize(cls=OrderBookDeltas, chunk=serialized) # Assert assert deserialized == deltas write_objects(catalog=self.catalog, chunk=[deserialized]) assert [d.action for d in deserialized.deltas] == [ BookAction.ADD, BookAction.CLEAR, BookAction.ADD, BookAction.ADD, ]
def _parse_delta(delta: OrderBookDelta, cls): return dict(**OrderBookDelta.to_dict(delta), _type=cls.__name__)
def to_dict(obj) -> Dict: values = OrderBookDelta.to_dict(obj) values["type"] = obj.__class__.__name__ return values
def test_fully_qualified_name(self): # Arrange, Act, Assert assert (OrderBookDelta.fully_qualified_name() == "nautilus_trader.model.orderbook.data.OrderBookDelta")