async def listen_for_order_book_snapshots(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue): while True: try: # at Beaxy all pairs listed without splitter trading_pairs = [trading_pair_to_symbol(p) for p in self._trading_pairs] ws_path: str = '/'.join([f'{trading_pair}@depth20' for trading_pair in trading_pairs]) stream_url: str = f'{BeaxyConstants.PublicApi.WS_BASE_URL}/book/{ws_path}' async with websockets.connect(stream_url) as ws: ws: websockets.WebSocketClientProtocol = ws async for raw_msg in self._inner_messages(ws): msg = json.loads(raw_msg) # ujson may round floats uncorrectly msg_type = msg['type'] if msg_type == ORDERBOOK_MESSAGE_DIFF: order_book_message: OrderBookMessage = BeaxyOrderBook.diff_message_from_exchange( msg, msg['timestamp']) output.put_nowait(order_book_message) if msg_type == ORDERBOOK_MESSAGE_SNAPSHOT: order_book_message: OrderBookMessage = BeaxyOrderBook.snapshot_message_from_exchange( msg, msg['timestamp']) output.put_nowait(order_book_message) except asyncio.CancelledError: raise except Exception: self.logger().error('Unexpected error with WebSocket connection. Retrying after 30 seconds...', exc_info=True) await asyncio.sleep(30.0)
def test_delete_through(self): active_tracker = BeaxyActiveOrderTracker() # receive INSERT message to be added to active orders first_insert: Dict[str, Any] = { 'timestamp': 1, "sequenceNumber": 1, 'entries': [{ "action": "INSERT", "quantity": 1, "price": 133, "side": "BID", "sequrity": test_trading_pair, }] } second_insert: Dict[str, Any] = { 'timestamp': 1, "sequenceNumber": 2, 'entries': [{ "action": "INSERT", "quantity": 2, "price": 134, "side": "BID", "sequrity": test_trading_pair }] } third_insert: Dict[str, Any] = { 'timestamp': 1, "sequenceNumber": 1, 'entries': [{ "action": "INSERT", "quantity": 3, "price": 135, "side": "BID", "sequrity": test_trading_pair }] } inserts = [first_insert, second_insert, third_insert] for msg in inserts: insert_message = BeaxyOrderBook.diff_message_from_exchange(msg, float(12345)) active_tracker.convert_diff_message_to_order_book_row(insert_message) delete_through_dict: Dict[str, Any] = { 'timestamp': 1, "sequenceNumber": 1, 'entries': [{ "action": "DELETE_THROUGH", "quantity": 3, "price": 134, "side": "BID", "sequrity": test_trading_pair }] } msg = BeaxyOrderBook.diff_message_from_exchange(delete_through_dict, float(12345)) active_tracker.convert_diff_message_to_order_book_row(msg) self.assertEqual(len(active_tracker.active_bids), 2) self.assertEqual(next(iter(active_tracker.active_bids)), 134)
def test_insert_update_delete_messages(self): active_tracker = BeaxyActiveOrderTracker() # receive INSERT message to be added to active orders side = "BID" price = 1337.4423423404 quantity: float = 1 update_id = 123 message_dict: Dict[str, Any] = { 'timestamp': 1, 'sequenceNumber': update_id, 'entries': [{ "action": "INSERT", "quantity": quantity, "price": price, "side": side, "sequrity": test_trading_pair }] } insert_message = BeaxyOrderBook.diff_message_from_exchange(message_dict, float(12345)) insert_ob_row: OrderBookRow = active_tracker.convert_diff_message_to_order_book_row(insert_message) self.assertEqual(insert_ob_row[0], [OrderBookRow(price, quantity, update_id)]) # receive UPDATE message updated_quantity: float = 3.2 update_message_dict: Dict[str, Any] = { 'timestamp': 1, "sequenceNumber": update_id + 1, 'entries': [{ "action": "UPDATE", "quantity": updated_quantity, "price": price, "side": side, "sequrity": test_trading_pair }] } change_message = BeaxyOrderBook.diff_message_from_exchange(update_message_dict, float(12345)) change_ob_row: OrderBookRow = active_tracker.convert_diff_message_to_order_book_row(change_message) self.assertEqual(change_ob_row[0], [OrderBookRow(price, float(updated_quantity), update_id + 1)]) # receive DELETE message delete_quantity = 1 delete_message_dict: Dict[str, Any] = { 'timestamp': 1, "sequenceNumber": update_id + 2, 'entries': [{ "action": "DELETE", "quantity": delete_quantity, "price": price, "side": side, "sequrity": test_trading_pair }] } delete_message: BeaxyOrderBookMessage = BeaxyOrderBook.diff_message_from_exchange(delete_message_dict, float(12345)) delete_ob_row: OrderBookRow = active_tracker.convert_diff_message_to_order_book_row(delete_message) self.assertEqual(delete_ob_row[0], [OrderBookRow(price, float(0), update_id + 1 + 1)])
async def listen_for_trades(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue): while True: try: # at Beaxy all pairs listed without splitter trading_pairs = [ trading_pair_to_symbol(p) for p in self._trading_pairs ] ws_path: str = '/'.join( [trading_pair for trading_pair in trading_pairs]) stream_url: str = f'{BeaxyConstants.PublicApi.WS_BASE_URL}/trades/{ws_path}' async with websockets.connect(stream_url) as ws: ws: websockets.WebSocketClientProtocol = ws async for raw_msg in self._inner_messages(ws): msg = ujson.loads(raw_msg) trade_msg: OrderBookMessage = BeaxyOrderBook.trade_message_from_exchange( msg) output.put_nowait(trade_msg) except asyncio.CancelledError: raise except Exception: self.logger().error( 'Unexpected error with WebSocket connection. Retrying after 30 seconds...', exc_info=True) await asyncio.sleep(30.0)
async def get_tracking_pairs(self) -> Dict[str, OrderBookTrackerEntry]: async with aiohttp.ClientSession() as client: trading_pairs: Optional[List[str]] = await self.get_trading_pairs() assert trading_pairs is not None retval: Dict[str, OrderBookTrackerEntry] = {} number_of_pairs: int = len(trading_pairs) for index, trading_pair in enumerate(trading_pairs): try: snapshot: Dict[str, Any] = await self.get_snapshot( client, trading_pair, 20) snapshot_timestamp = snapshot['timestamp'] snapshot_msg: OrderBookMessage = BeaxyOrderBook.snapshot_message_from_exchange( snapshot, snapshot_timestamp, metadata={'trading_pair': trading_pair}) order_book: OrderBook = self.order_book_create_function() active_order_tracker: BeaxyActiveOrderTracker = BeaxyActiveOrderTracker( ) bids, asks = active_order_tracker.convert_snapshot_message_to_order_book_row( snapshot_msg) order_book.apply_snapshot(bids, asks, snapshot_msg.update_id) retval[trading_pair] = BeaxyOrderBookTrackerEntry( trading_pair, snapshot_timestamp, order_book, active_order_tracker) self.logger().info( f'Initialized order book for {trading_pair}. ' f'{index+1}/{number_of_pairs} completed.') except Exception: self.logger().error( f'Error getting snapshot for {trading_pair}. ', exc_info=True) await asyncio.sleep(5.0) return retval
def test_snapshot(self): active_tracker = BeaxyActiveOrderTracker() insert_message = BeaxyOrderBook.snapshot_message_from_exchange(FixtureBeaxy.SNAPSHOT_MSG, float(12345)) active_tracker.convert_snapshot_message_to_order_book_row(insert_message) self.assertEqual(len(active_tracker.active_asks), 9)
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, 20) snapshot_timestamp = snapshot['timestamp'] snapshot_msg: OrderBookMessage = BeaxyOrderBook.snapshot_message_from_exchange( snapshot, snapshot_timestamp, metadata={'trading_pair': trading_pair} ) order_book: OrderBook = self.order_book_create_function() active_order_tracker: BeaxyActiveOrderTracker = BeaxyActiveOrderTracker() bids, asks = active_order_tracker.convert_snapshot_message_to_order_book_row(snapshot_msg) order_book.apply_snapshot(bids, asks, snapshot_msg.update_id) return order_book