async def listen_for_order_book_snapshots(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue): while True: try: async with aiohttp.ClientSession() as client: for trading_pair in self._trading_pairs: try: snapshot: Dict[str, Any] = await self.get_snapshot(client, trading_pair, domain=self._domain) snapshot_timestamp: float = time.time() snapshot_msg: OrderBookMessage = BinanceOrderBook.snapshot_message_from_exchange( snapshot, snapshot_timestamp, metadata={"trading_pair": trading_pair} ) output.put_nowait(snapshot_msg) self.logger().debug(f"Saved order book snapshot for {trading_pair}") # Be careful not to go above Binance's API rate limits. await asyncio.sleep(5.0) except asyncio.CancelledError: raise except Exception: self.logger().error("Unexpected error.", exc_info=True) await asyncio.sleep(5.0) this_hour: pd.Timestamp = pd.Timestamp.utcnow().replace(minute=0, second=0, microsecond=0) next_hour: pd.Timestamp = this_hour + pd.Timedelta(hours=1) delta: float = next_hour.timestamp() - time.time() await asyncio.sleep(delta) except asyncio.CancelledError: raise except Exception: self.logger().error("Unexpected error.", exc_info=True) await asyncio.sleep(5.0)
async def _order_book_snapshot(self, trading_pair: str) -> OrderBookMessage: snapshot: Dict[str, Any] = await self._request_order_book_snapshot( trading_pair) snapshot_timestamp: float = time.time() snapshot_msg: OrderBookMessage = BinanceOrderBook.snapshot_message_from_exchange( snapshot, snapshot_timestamp, metadata={"trading_pair": trading_pair}) return snapshot_msg
async def get_new_order_book(self, trading_pair: str) -> OrderBook: async with aiohttp.ClientSession() as client: snapshot: Dict[str, Any] = await self.get_snapshot(client, trading_pair, 1000, self._domain) snapshot_timestamp: float = time.time() snapshot_msg: OrderBookMessage = BinanceOrderBook.snapshot_message_from_exchange( snapshot, snapshot_timestamp, metadata={"trading_pair": trading_pair} ) order_book = self.order_book_create_function() order_book.apply_snapshot(snapshot_msg.bids, snapshot_msg.asks, snapshot_msg.update_id) return order_book
def test_track_single_book_snapshot_message_with_past_diffs(self, mock_utils): # Mocks binance_utils for BinanceOrderBook.diff_message_from_exchange() mock_utils.return_value = self.trading_pair snapshot_msg: OrderBookMessage = BinanceOrderBook.snapshot_message_from_exchange( msg={ "trading_pair": self.trading_pair, "lastUpdateId": 1, "bids": [ ["4.00000000", "431.00000000"] ], "asks": [ ["4.00000200", "12.00000000"] ] }, timestamp=time.time() ) past_diff_msg: OrderBookMessage = BinanceOrderBook.diff_message_from_exchange( msg={ "e": "depthUpdate", "E": 123456789, "s": "BNBBTC", "U": 1, "u": 2, "b": [ [ "0.0024", "10" ] ], "a": [ [ "0.0026", "100" ] ] } ) self.tracking_task = self.ev_loop.create_task( self.tracker._track_single_book(self.trading_pair) ) self.ev_loop.run_until_complete(asyncio.sleep(0.5)) self._simulate_message_enqueue(self.tracker._past_diffs_windows[self.trading_pair], past_diff_msg) self._simulate_message_enqueue(self.tracker._tracking_message_queues[self.trading_pair], snapshot_msg) self.ev_loop.run_until_complete(asyncio.sleep(0.5)) self.assertEqual(1, self.tracker.order_books[self.trading_pair].snapshot_uid) self.assertEqual(2, self.tracker.order_books[self.trading_pair].last_diff_uid)
async def get_new_order_book(self, trading_pair: str) -> OrderBook: """ Creates a local instance of the exchange order book for a particular trading pair :param trading_pair: the trading pair for which the order book has to be retrieved :return: a local copy of the current order book in the exchange """ snapshot: Dict[str, Any] = await self.get_snapshot(trading_pair, 1000) snapshot_timestamp: float = time.time() snapshot_msg: OrderBookMessage = BinanceOrderBook.snapshot_message_from_exchange( snapshot, snapshot_timestamp, metadata={"trading_pair": trading_pair}) order_book = self.order_book_create_function() order_book.apply_snapshot(snapshot_msg.bids, snapshot_msg.asks, snapshot_msg.update_id) return order_book
def test_track_single_book_snapshot_message_no_past_diffs(self): snapshot_msg: OrderBookMessage = BinanceOrderBook.snapshot_message_from_exchange( msg={ "trading_pair": self.trading_pair, "lastUpdateId": 1, "bids": [["4.00000000", "431.00000000"]], "asks": [["4.00000200", "12.00000000"]] }, timestamp=time.time()) self._simulate_message_enqueue( self.tracker._tracking_message_queues[self.trading_pair], snapshot_msg) self.tracking_task = self.ev_loop.create_task( self.tracker._track_single_book(self.trading_pair)) self.ev_loop.run_until_complete(asyncio.sleep(0.5)) self.assertEqual( 1, self.tracker.order_books[self.trading_pair].snapshot_uid)
def test_snapshot_message_from_exchange(self): snapshot_message = BinanceOrderBook.snapshot_message_from_exchange( msg={ "lastUpdateId": 1, "bids": [["4.00000000", "431.00000000"]], "asks": [["4.00000200", "12.00000000"]] }, timestamp=1640000000.0, metadata={"trading_pair": "COINALPHA-HBOT"}) self.assertEqual("COINALPHA-HBOT", snapshot_message.trading_pair) self.assertEqual(OrderBookMessageType.SNAPSHOT, snapshot_message.type) self.assertEqual(1640000000.0, snapshot_message.timestamp) self.assertEqual(1, snapshot_message.update_id) self.assertEqual(-1, snapshot_message.trade_id) self.assertEqual(1, len(snapshot_message.bids)) self.assertEqual(4.0, snapshot_message.bids[0].price) self.assertEqual(431.0, snapshot_message.bids[0].amount) self.assertEqual(1, snapshot_message.bids[0].update_id) self.assertEqual(1, len(snapshot_message.asks)) self.assertEqual(4.000002, snapshot_message.asks[0].price) self.assertEqual(12.0, snapshot_message.asks[0].amount) self.assertEqual(1, snapshot_message.asks[0].update_id)
async def listen_for_order_book_snapshots( self, ev_loop: asyncio.AbstractEventLoop, output: asyncio.Queue): """ This method runs continuously and request the full order book content from the exchange every hour. The method uses the REST API from the exchange because it does not provide an endpoint to get the full order book through websocket. With the information creates a snapshot messages that is added to the output queue :param ev_loop: the event loop the method will run in :param output: a queue to add the created snapshot messages """ while True: try: for trading_pair in self._trading_pairs: try: snapshot: Dict[str, Any] = await self.get_snapshot( trading_pair=trading_pair) snapshot_timestamp: float = time.time() snapshot_msg: OrderBookMessage = BinanceOrderBook.snapshot_message_from_exchange( snapshot, snapshot_timestamp, metadata={"trading_pair": trading_pair}) output.put_nowait(snapshot_msg) self.logger().debug( f"Saved order book snapshot for {trading_pair}") except asyncio.CancelledError: raise except Exception: self.logger().error( f"Unexpected error fetching order book snapshot for {trading_pair}.", exc_info=True) await self._sleep(5.0) await self._sleep(self.ONE_HOUR) except asyncio.CancelledError: raise except Exception: self.logger().error("Unexpected error.", exc_info=True) await self._sleep(5.0)