def test_to_dict_returns_expected_dict(self):
        # Arrange
        snapshot = OrderBookSnapshot(
            instrument_id=AUDUSD,
            book_type=BookType.L2_MBP,
            bids=[[1010, 2], [1009, 1]],
            asks=[[1020, 2], [1021, 1]],
            update_id=123456789,
            ts_event=0,
            ts_init=1_000_000_000,
        )

        # Act
        result = OrderBookSnapshot.to_dict(snapshot)

        # Assert
        assert result == {
            "type": "OrderBookSnapshot",
            "instrument_id": "AUD/USD.SIM",
            "book_type": "L2_MBP",
            "bids": b"[[1010,2],[1009,1]]",
            "asks": b"[[1020,2],[1021,1]]",
            "update_id": 123456789,
            "ts_event": 0,
            "ts_init": 1_000_000_000,
        }
Пример #2
0
    def test_add_order_book_snapshots_adds_to_engine(self, capsys):
        # Arrange
        engine = BacktestEngine()
        engine.add_instrument(ETHUSDT_BINANCE)

        snapshot1 = OrderBookSnapshot(
            instrument_id=ETHUSDT_BINANCE.id,
            book_type=BookType.L2_MBP,
            bids=[[1550.15, 0.51], [1580.00, 1.20]],
            asks=[[1552.15, 1.51], [1582.00, 2.20]],
            ts_event=0,
            ts_init=0,
        )

        snapshot2 = OrderBookSnapshot(
            instrument_id=ETHUSDT_BINANCE.id,
            book_type=BookType.L2_MBP,
            bids=[[1551.15, 0.51], [1581.00, 1.20]],
            asks=[[1553.15, 1.51], [1583.00, 2.20]],
            ts_event=1_000_000_000,
            ts_init=1_000_000_000,
        )

        # Act
        engine.add_order_book_data([snapshot2, snapshot1])  # <-- reverse order

        # Assert
        log = "".join(capsys.readouterr())
        assert "Added 2 ETHUSDT.BINANCE OrderBookData elements." in log
Пример #3
0
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
Пример #4
0
    def test_order_book_when_order_book_exists_returns_expected(self):
        # Arrange
        snapshot = OrderBookSnapshot(
            instrument_id=ETHUSDT_BINANCE.id,
            book_type=BookType.L2_MBP,
            bids=[[1550.15, 0.51], [1580.00, 1.20]],
            asks=[[1552.15, 1.51], [1582.00, 2.20]],
            ts_event=0,
            ts_init=0,
        )

        order_book = L2OrderBook(
            instrument_id=ETHUSDT_BINANCE.id,
            price_precision=2,
            size_precision=8,
        )
        order_book.apply_snapshot(snapshot)

        self.cache.add_order_book(order_book)

        # Act
        result = self.cache.order_book(ETHUSDT_BINANCE.id)

        # Assert
        assert result == order_book
    def test_from_dict_returns_expected_tick(self):
        # Arrange
        snapshot = OrderBookSnapshot(
            instrument_id=AUDUSD,
            book_type=BookType.L2_MBP,
            bids=[[1010, 2], [1009, 1]],
            asks=[[1020, 2], [1021, 1]],
            update_id=123456789,
            ts_event=0,
            ts_init=1_000_000_000,
        )

        # Act
        result = OrderBookSnapshot.from_dict(
            OrderBookSnapshot.to_dict(snapshot))

        # Assert
        assert result == snapshot
Пример #6
0
def test_orderbook_snapshot(empty_l2_book):
    snapshot = OrderBookSnapshot(
        instrument_id=empty_l2_book.instrument_id,
        book_type=BookType.L2_MBP,
        bids=[[1550.15, 0.51], [1580.00, 1.20]],
        asks=[[1552.15, 1.51], [1582.00, 2.20]],
        ts_event=0,
        ts_init=0,
    )
    empty_l2_book.apply_snapshot(snapshot)
    assert empty_l2_book.best_bid_price() == 1580.0
    assert empty_l2_book.best_ask_price() == 1552.15
Пример #7
0
def _handle_market_snapshot(selection, instrument, ts_event, ts_init):
    updates = []
    # Check we only have one of [best bets / depth bets / all bets]
    bid_keys = [k for k in B_BID_KINDS if k in selection] or ["atb"]
    ask_keys = [k for k in B_ASK_KINDS if k in selection] or ["atl"]
    if set(bid_keys) == {"batb", "atb"}:
        bid_keys = ["atb"]
    if set(ask_keys) == {"batl", "atl"}:
        ask_keys = ["atl"]

    assert len(bid_keys) <= 1
    assert len(ask_keys) <= 1

    # OrderBook Snapshot
    # TODO(bm): Clean this up
    if bid_keys[0] == "atb":
        bids = selection.get("atb", [])
    else:
        bids = [(p, v) for _, p, v in selection.get(bid_keys[0], [])]
    if ask_keys[0] == "atl":
        asks = selection.get("atl", [])
    else:
        asks = [(p, v) for _, p, v in selection.get(ask_keys[0], [])]
    if bids or asks:
        snapshot = OrderBookSnapshot(
            book_type=BookType.L2_MBP,
            instrument_id=instrument.id,
            bids=[(price_to_probability(str(p)), v) for p, v in asks if p],
            asks=[(price_to_probability(str(p)), v) for p, v in bids if p],
            ts_event=ts_event,
            ts_init=ts_init,
        )
        updates.append(snapshot)
    if "trd" in selection:
        updates.extend(
            _handle_market_trades(
                runner=selection,
                instrument=instrument,
                ts_event=ts_event,
                ts_init=ts_init,
            )
        )
    if "spb" in selection or "spl" in selection:
        updates.extend(
            _handle_bsp_updates(
                runner=selection,
                instrument=instrument,
                ts_event=ts_event,
                ts_init=ts_init,
            )
        )

    return updates
Пример #8
0
def parse_book_snapshot_ws(instrument_id: InstrumentId, msg: Dict,
                           update_id: int, ts_init: int) -> OrderBookSnapshot:
    ts_event: int = ts_init

    return OrderBookSnapshot(
        instrument_id=instrument_id,
        book_type=BookType.L2_MBP,
        bids=[[float(o[0]), float(o[1])] for o in msg.get("bids")],
        asks=[[float(o[0]), float(o[1])] for o in msg.get("asks")],
        ts_event=ts_event,
        ts_init=ts_init,
        update_id=update_id,
    )
Пример #9
0
def parse_book_snapshot(
    instrument_id: InstrumentId,
    data: BinanceOrderBookData,
    ts_init: int,
) -> OrderBookSnapshot:
    return OrderBookSnapshot(
        instrument_id=instrument_id,
        book_type=BookType.L2_MBP,
        bids=[[float(o[0]), float(o[1])] for o in data.bids],
        asks=[[float(o[0]), float(o[1])] for o in data.asks],
        ts_event=millis_to_nanos(data.T),
        ts_init=ts_init,
        update_id=data.u,
    )
Пример #10
0
 def _on_top_level_snapshot(self, ticker: Ticker):
     instrument_id = self._instrument_provider.contract_id_to_instrument_id[
         ticker.contract.conId]
     ts_event = dt_to_unix_nanos(ticker.time)
     ts_init = self._clock.timestamp_ns()
     snapshot = OrderBookSnapshot(
         book_type=BookType.L1_TBBO,
         instrument_id=instrument_id,
         bids=[(ticker.bid, ticker.bidSize)],
         asks=[(ticker.ask, ticker.askSize)],
         ts_event=ts_event,
         ts_init=ts_init,
     )
     self._handle_data(snapshot)
Пример #11
0
def parse_book_partial_ws(
    instrument_id: InstrumentId,
    data: Dict[str, Any],
    ts_init: int,
) -> OrderBookSnapshot:
    return OrderBookSnapshot(
        instrument_id=instrument_id,
        book_type=BookType.L2_MBP,
        bids=[[o[0], o[1]] for o in data.get("bids")],
        asks=[[o[0], o[1]] for o in data.get("asks")],
        ts_event=secs_to_nanos(data["time"]),
        ts_init=ts_init,
        update_id=data["checksum"],
    )
Пример #12
0
def _build_order_book_snapshot(values):
    # First value is a CLEAR message, which we ignore
    assert len(set([v["instrument_id"] for v in values])) == 1
    assert len(values) >= 2, f"Not enough values passed! {values}"
    return OrderBookSnapshot(
        instrument_id=InstrumentId.from_str(values[1]["instrument_id"]),
        book_type=BookTypeParser.from_str_py(values[1]["book_type"]),
        bids=[(order["order_price"], order["order_size"])
              for order in values[1:] if order["order_side"] == "BUY"],
        asks=[(order["order_price"], order["order_size"])
              for order in values[1:] if order["order_side"] == "SELL"],
        ts_event=values[1]["ts_event"],
        ts_init=values[1]["ts_init"],
    )
Пример #13
0
def parse_spot_book_snapshot(
    instrument_id: InstrumentId,
    data: BinanceSpotOrderBookDepthData,
    ts_init: int,
) -> OrderBookSnapshot:
    return OrderBookSnapshot(
        instrument_id=instrument_id,
        book_type=BookType.L2_MBP,
        bids=[[float(o[0]), float(o[1])] for o in data.bids],
        asks=[[float(o[0]), float(o[1])] for o in data.asks],
        ts_event=ts_init,
        ts_init=ts_init,
        update_id=data.lastUpdateId,
    )
Пример #14
0
    def test_handle_order_book_snapshot_sends_to_data_engine(self):
        # Arrange
        snapshot = OrderBookSnapshot(
            instrument_id=ETHUSDT_BINANCE.id,
            book_type=BookType.L2_MBP,
            bids=[[1000, 1]],
            asks=[[1001, 1]],
            ts_event=0,
            ts_init=0,
        )

        # Act
        self.client._handle_data_py(snapshot)

        # Assert
        assert self.data_engine.data_count == 1
Пример #15
0
 def _on_order_book_snapshot(self,
                             ticker: Ticker,
                             book_type: BookType = BookType.L2_MBP):
     instrument_id = self._instrument_provider.contract_id_to_instrument_id[
         ticker.contract.conId]
     ts_event = dt_to_unix_nanos(ticker.time)
     ts_init = self._clock.timestamp_ns()
     if not (ticker.domBids or ticker.domAsks):
         return
     snapshot = OrderBookSnapshot(
         book_type=book_type,
         instrument_id=instrument_id,
         bids=[(level.price, level.size) for level in ticker.domBids],
         asks=[(level.price, level.size) for level in ticker.domAsks],
         ts_event=ts_event,
         ts_init=ts_init,
     )
     self._handle_data(snapshot)
Пример #16
0
    def test_hash_str_and_repr(self):
        # Arrange
        snapshot = OrderBookSnapshot(
            instrument_id=AUDUSD,
            book_type=BookType.L2_MBP,
            bids=[[1010, 2], [1009, 1]],
            asks=[[1020, 2], [1021, 1]],
            ts_event=0,
            ts_init=0,
        )

        # Act, Assert
        assert isinstance(hash(snapshot), int)
        assert (
            str(snapshot) ==
            "OrderBookSnapshot('AUD/USD.SIM', book_type=L2_MBP, bids=[[1010, 2], [1009, 1]], asks=[[1020, 2], [1021, 1]], update_id=0, ts_event=0, ts_init=0)"  # noqa
        )
        assert (
            repr(snapshot) ==
            "OrderBookSnapshot('AUD/USD.SIM', book_type=L2_MBP, bids=[[1010, 2], [1009, 1]], asks=[[1020, 2], [1021, 1]], update_id=0, ts_event=0, ts_init=0)"  # noqa
        )
Пример #17
0
    def order_book_snapshot(
        instrument_id=None,
        bid_price=10,
        ask_price=15,
        bid_levels=3,
        ask_levels=3,
        bid_volume=10,
        ask_volume=10,
        book_type=BookType.L2_MBP,
    ) -> OrderBookSnapshot:
        err = "Too many levels generated; orders will be in cross. Increase bid/ask spread or reduce number of levels"
        assert bid_price < ask_price, err

        return OrderBookSnapshot(
            instrument_id=instrument_id or TestStubs.audusd_id(),
            book_type=book_type,
            bids=[(float(bid_price - i), float(bid_volume * (1 + i))) for i in range(bid_levels)],
            asks=[(float(ask_price + i), float(ask_volume * (1 + i))) for i in range(ask_levels)],
            ts_event=0,
            ts_init=0,
        )
Пример #18
0
    async def _subscribe_order_book(
        self,
        instrument_id: InstrumentId,
        book_type: BookType,
        depth: Optional[int] = None,
    ) -> None:
        if book_type == BookType.L3_MBO:
            self._log.error(
                "Cannot subscribe to order book deltas: "
                "L3_MBO data is not published by Binance. "
                "Valid book types are L1_TBBO, L2_MBP.",
            )
            return

        if depth is None or depth == 0:
            depth = 20

        # Add delta stream buffer
        self._book_buffer[instrument_id] = []

        if depth <= 20:
            if depth not in (5, 10, 20):
                self._log.error(
                    "Cannot subscribe to order book snapshots: "
                    f"invalid depth, was {depth}. "
                    "Valid depths are 5, 10 or 20.",
                )
                return
            self._ws_client.subscribe_partial_book_depth(
                symbol=instrument_id.symbol.value,
                depth=depth,
                speed=100,
            )
        else:
            self._ws_client.subscribe_diff_book_depth(
                symbol=instrument_id.symbol.value,
                speed=100,
            )

        while not self._ws_client.is_connected:
            await self.sleep0()

        data: Dict[str, Any] = await self._http_market.depth(
            symbol=instrument_id.symbol.value,
            limit=depth,
        )

        ts_event: int = self._clock.timestamp_ns()
        last_update_id: int = data.get("lastUpdateId")

        snapshot = OrderBookSnapshot(
            instrument_id=instrument_id,
            book_type=BookType.L2_MBP,
            bids=[[float(o[0]), float(o[1])] for o in data.get("bids")],
            asks=[[float(o[0]), float(o[1])] for o in data.get("asks")],
            ts_event=ts_event,
            ts_init=ts_event,
            update_id=last_update_id,
        )

        self._handle_data(snapshot)

        book_buffer = self._book_buffer.pop(instrument_id)
        for deltas in book_buffer:
            if deltas.update_id <= last_update_id:
                continue
            self._handle_data(deltas)
 def test_fully_qualified_name(self):
     # Arrange, Act, Assert
     assert (OrderBookSnapshot.fully_qualified_name() ==
             "nautilus_trader.model.orderbook.data.OrderBookSnapshot")