async def listen_for_order_book_snapshots(self,
                                              ev_loop: asyncio.BaseEventLoop,
                                              output: asyncio.Queue):
        """
        Periodically polls for orderbook snapshots using the REST API.
        """
        if not len(self._trading_pair_id_map) > 0:
            await self.init_trading_pair_ids(self._domain, self._throttler,
                                             self._shared_client)
        while True:
            await self._sleep(self._ORDER_BOOK_SNAPSHOT_DELAY)
            try:
                for trading_pair in self._trading_pairs:
                    snapshot: Dict[str:Any] = await self.get_order_book_data(
                        trading_pair, domain=self._domain)
                    metadata = {
                        "trading_pair":
                        trading_pair,
                        "instrument_id":
                        self._trading_pair_id_map.get(trading_pair, None)
                    }
                    snapshot_message: NdaxOrderBookMessage = NdaxOrderBook.snapshot_message_from_exchange(
                        msg=snapshot,
                        timestamp=snapshot["timestamp"],
                        metadata=metadata)
                    output.put_nowait(snapshot_message)

            except asyncio.CancelledError:
                raise
            except Exception:
                self.logger().error(
                    "Unexpected error occured listening for orderbook snapshots. Retrying in 5 secs...",
                    exc_info=True)
                await self._sleep(5.0)
    def test_track_single_book_apply_snapshot(self):
        snapshot_data = [
            NdaxOrderBookEntry(*[
                93617617, 1, 1626788175000, 0, 37800.0, 1, 37750.0, 1, 0.015, 0
            ]),
            NdaxOrderBookEntry(*[
                93617617, 1, 1626788175000, 0, 37800.0, 1, 37751.0, 1, 0.015, 1
            ])
        ]
        snapshot_msg = NdaxOrderBook.snapshot_message_from_exchange(
            msg={"data": snapshot_data},
            timestamp=1626788175000,
            metadata={
                "trading_pair": self.trading_pair,
                "instrument_id": self.instrument_id
            })
        self.simulate_queue_order_book_messages(snapshot_msg)

        with self.assertRaises(asyncio.TimeoutError):
            # Allow 5 seconds for tracker to process some messages.
            self.tracking_task = self.ev_loop.create_task(
                asyncio.wait_for(
                    self.tracker._track_single_book(self.trading_pair), 2.0))
            self.ev_loop.run_until_complete(self.tracking_task)

        self.assertEqual(
            0, self.tracker.order_books[self.trading_pair].snapshot_uid)
    def setUp(self) -> None:
        super().setUp()
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        self.tracker: NdaxOrderBookTracker = NdaxOrderBookTracker(
            throttler=throttler, trading_pairs=[self.trading_pair])
        self.tracking_task = None

        # Simulate start()
        self.tracker._order_books[self.trading_pair] = NdaxOrderBook()
        self.tracker._tracking_message_queues[
            self.trading_pair] = asyncio.Queue()
        self.tracker._order_books_initialized.set()
    async def get_new_order_book(self, trading_pair: str) -> OrderBook:
        snapshot: Dict[str, Any] = await self.get_order_book_data(trading_pair, self._domain)

        snapshot_msg: NdaxOrderBookMessage = NdaxOrderBook.snapshot_message_from_exchange(
            msg=snapshot,
            timestamp=snapshot["timestamp"],
        )
        order_book = self.order_book_create_function()

        bids, asks = snapshot_msg.bids, snapshot_msg.asks
        order_book.apply_snapshot(bids, asks, snapshot_msg.update_id)

        return order_book
    async def listen_for_order_book_diffs(self, ev_loop: asyncio.BaseEventLoop,
                                          output: asyncio.Queue):
        """
        Listen for orderbook diffs using WebSocket API.
        """
        if not len(self._trading_pair_id_map) > 0:
            await self.init_trading_pair_ids(self._domain, self._throttler,
                                             self._shared_client)

        while True:
            try:
                ws_adaptor: NdaxWebSocketAdaptor = await self._create_websocket_connection(
                )
                for trading_pair in self._trading_pairs:
                    payload = {
                        "OMSId": 1,
                        "Symbol":
                        convert_to_exchange_trading_pair(trading_pair),
                        "Depth": 200
                    }
                    async with self._throttler.execute_task(
                            CONSTANTS.WS_ORDER_BOOK_CHANNEL):
                        await ws_adaptor.send_request(
                            endpoint_name=CONSTANTS.WS_ORDER_BOOK_CHANNEL,
                            payload=payload)
                async for raw_msg in ws_adaptor.iter_messages():
                    payload = NdaxWebSocketAdaptor.payload_from_raw_message(
                        raw_msg)
                    msg_event: str = NdaxWebSocketAdaptor.endpoint_from_raw_message(
                        raw_msg)
                    if msg_event in [
                            CONSTANTS.WS_ORDER_BOOK_CHANNEL,
                            CONSTANTS.WS_ORDER_BOOK_L2_UPDATE_EVENT
                    ]:
                        msg_data: List[NdaxOrderBookEntry] = [
                            NdaxOrderBookEntry(*entry) for entry in payload
                        ]
                        msg_timestamp: int = int(time.time() * 1e3)
                        msg_product_code: int = msg_data[0].productPairCode

                        content = {"data": msg_data}
                        msg_trading_pair: Optional[str] = None

                        for trading_pair, instrument_id in self._trading_pair_id_map.items(
                        ):
                            if msg_product_code == instrument_id:
                                msg_trading_pair = trading_pair
                                break

                        if msg_trading_pair:
                            metadata = {
                                "trading_pair": msg_trading_pair,
                                "instrument_id": msg_product_code,
                            }

                            order_book_message = None
                            if msg_event == CONSTANTS.WS_ORDER_BOOK_CHANNEL:
                                order_book_message: NdaxOrderBookMessage = NdaxOrderBook.snapshot_message_from_exchange(
                                    msg=content,
                                    timestamp=msg_timestamp,
                                    metadata=metadata)
                            elif msg_event == CONSTANTS.WS_ORDER_BOOK_L2_UPDATE_EVENT:
                                order_book_message: NdaxOrderBookMessage = NdaxOrderBook.diff_message_from_exchange(
                                    msg=content,
                                    timestamp=msg_timestamp,
                                    metadata=metadata)
                            self._last_traded_prices[
                                order_book_message.
                                trading_pair] = order_book_message.last_traded_price
                            await output.put(order_book_message)

            except asyncio.CancelledError:
                raise
            except Exception:
                self.logger().network(
                    "Unexpected error with WebSocket connection.",
                    exc_info=True,
                    app_warning_msg=
                    "Unexpected error with WebSocket connection. Retrying in 30 seconds. "
                    "Check network connection.")
                if ws_adaptor:
                    await ws_adaptor.close()
                await self._sleep(30.0)