async def _subscribe_channels(self, ws: WSAssistant): try: for pair in self._trading_pairs: subscribe_orderbook_request: WSJSONRequest = WSJSONRequest({ "type": "subscribe", "channel": self.ORDERBOOK_CHANNEL, "id": pair, }) subscribe_trade_request: WSJSONRequest = WSJSONRequest({ "type": "subscribe", "channel": self.TRADE_CHANNEL, "id": pair, }) await ws.send(subscribe_orderbook_request) await ws.send(subscribe_trade_request) self.logger().info( "Subscribed to public orderbook and trade channels...") except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error occurred subscribing to order book trading and delta streams...", exc_info=True) raise
async def _subscribe_channels(self, ws: WSAssistant): """ Subscribes to the trade events and diff orders events through the provided websocket connection. :param ws: the websocket assistant used to connect to the exchange """ try: trade_params = [] depth_params = [] for trading_pair in self._trading_pairs: symbol = await self._connector.exchange_symbol_associated_to_pair( trading_pair=trading_pair) trade_params.append(f"{symbol.lower()}@trade") depth_params.append(f"{symbol.lower()}@depth@100ms") payload = {"method": "SUBSCRIBE", "params": trade_params, "id": 1} subscribe_trade_request: WSJSONRequest = WSJSONRequest( payload=payload) payload = {"method": "SUBSCRIBE", "params": depth_params, "id": 2} subscribe_orderbook_request: WSJSONRequest = WSJSONRequest( payload=payload) await ws.send(subscribe_trade_request) await ws.send(subscribe_orderbook_request) self.logger().info( "Subscribed to public order book and trade channels...") except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error occurred subscribing to order book trading and delta streams...", exc_info=True) raise
def test_ws_assistant_authenticates(self, send_mock): class Auth(AuthBase): async def rest_authenticate(self, request: RESTRequest) -> RESTRequest: pass async def ws_authenticate(self, request: WSRequest) -> WSRequest: request.payload["authenticated"] = True return request ws_assistant = WSAssistant(connection=self.ws_connection, auth=Auth()) sent_requests = [] send_mock.side_effect = lambda r: sent_requests.append(r) payload = {"one": 1} req = WSJSONRequest(payload) auth_req = WSJSONRequest(payload, is_auth_required=True) self.async_run_with_timeout(ws_assistant.send(req)) self.async_run_with_timeout(ws_assistant.send(auth_req)) sent_request = sent_requests[0] auth_sent_request = sent_requests[1] expected = {"one": 1} auth_expected = {"one": 1, "authenticated": True} self.assertEqual(expected, sent_request.payload) self.assertEqual(auth_expected, auth_sent_request.payload)
async def _subscribe_channels(self, ws: WSAssistant): try: symbols = ",".join([await self._connector.exchange_symbol_associated_to_pair(trading_pair=pair) for pair in self._trading_pairs]) trades_payload = { "id": web_utils.next_message_id(), "type": "subscribe", "topic": f"/market/match:{symbols}", "privateChannel": False, "response": False, } subscribe_trade_request: WSJSONRequest = WSJSONRequest(payload=trades_payload) order_book_payload = { "id": web_utils.next_message_id(), "type": "subscribe", "topic": f"/market/level2:{symbols}", "privateChannel": False, "response": False, } subscribe_orderbook_request: WSJSONRequest = WSJSONRequest(payload=order_book_payload) await ws.send(subscribe_trade_request) await ws.send(subscribe_orderbook_request) self._last_ws_message_sent_timestamp = self._time() self.logger().info("Subscribed to public order book and trade channels...") except asyncio.CancelledError: raise except Exception: self.logger().exception("Unexpected error occurred subscribing to order book trading and delta streams...") raise
async def listen_for_user_stream(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue): ws = None while True: try: expires = int(time.time()) + 25 url = web_utils.wss_url("", self._domain) # # establish initial connection to websocket ws: WSAssistant = await self._get_ws_assistant() await ws.connect(ws_url=url) # # send auth request API_KEY = self._auth.api_key signature = await self._auth.generate_ws_signature(str(expires) ) auth_payload = { "op": "authKeyExpires", "args": [API_KEY, expires, signature] } auth_request: WSJSONRequest = WSJSONRequest( payload=auth_payload, is_auth_required=False) await ws.send(auth_request) # await ws.ping() # to update last_recv_timestamp # # send subscribe # position - Updates on your positions # order - Live updates on your orders # margin - Updates on your current account balance and margin requirements # wallet - Bitcoin address balance data, including total deposits & withdrawals subscribe_payload = { "op": "subscribe", "args": ["position", "order", "margin", "wallet"] } subscribe_request: WSJSONRequest = WSJSONRequest( payload=subscribe_payload, is_auth_required=False) await ws.send(subscribe_request) async for msg in ws.iter_messages(): if len(msg.data) > 0: output.put_nowait(msg.data) except asyncio.CancelledError: raise except Exception as e: self.logger().error( f"Unexpected error while listening to user stream. Retrying after 5 seconds... " f"Error: {e}", exc_info=True, ) finally: # Make sure no background task is leaked. ws and await ws.disconnect() await self._sleep(5)
async def _subscribe_channels(self, websocket_assistant: WSAssistant): """ Subscribes to order events and balance events. :param websocket_assistant: the websocket assistant used to connect to the exchange """ try: symbols = [ await self._connector.exchange_symbol_associated_to_pair( trading_pair=trading_pair) for trading_pair in self._trading_pairs ] orders_change_payload = { "time": int(self._time()), "channel": CONSTANTS.USER_ORDERS_ENDPOINT_NAME, "event": "subscribe", "payload": symbols } subscribe_order_change_request: WSJSONRequest = WSJSONRequest( payload=orders_change_payload, is_auth_required=True) trades_payload = { "time": int(self._time()), "channel": CONSTANTS.USER_TRADES_ENDPOINT_NAME, "event": "subscribe", "payload": symbols } subscribe_trades_request: WSJSONRequest = WSJSONRequest( payload=trades_payload, is_auth_required=True) balance_payload = { "time": int(self._time()), "channel": CONSTANTS.USER_BALANCE_ENDPOINT_NAME, "event": "subscribe", # "unsubscribe" for unsubscription } subscribe_balance_request: WSJSONRequest = WSJSONRequest( payload=balance_payload, is_auth_required=True) await websocket_assistant.send(subscribe_order_change_request) await websocket_assistant.send(subscribe_trades_request) await websocket_assistant.send(subscribe_balance_request) self.logger().info( "Subscribed to private order changes and balance updates channels..." ) except asyncio.CancelledError: raise except Exception: self.logger().exception( "Unexpected error occurred subscribing to user streams...") raise
async def _subscribe_channels(self, ws: WSAssistant): """ Subscribes to the trade events and diff orders events through the provided websocket connection. :param ws: the websocket assistant used to connect to the exchange """ try: for trading_pair in self._trading_pairs: symbol = await self.exchange_symbol_associated_to_pair( trading_pair=trading_pair, domain=self._domain, api_factory=self._api_factory, throttler=self._throttler, time_synchronizer=self._time_synchronizer) trade_payload = { "topic": "trade", "event": "sub", "symbol": symbol, "params": { "binary": False } } subscribe_trade_request: WSJSONRequest = WSJSONRequest( payload=trade_payload) depth_payload = { "topic": "diffDepth", "event": "sub", "symbol": symbol, "params": { "binary": False } } subscribe_orderbook_request: WSJSONRequest = WSJSONRequest( payload=depth_payload) await ws.send(subscribe_trade_request) await ws.send(subscribe_orderbook_request) self.logger().info( f"Subscribed to public order book and trade channels of {trading_pair}..." ) except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error occurred subscribing to order book trading and delta streams...", exc_info=True) raise
async def _connected_websocket_assistant(self) -> WSAssistant: """ Creates an instance of WSAssistant connected to the exchange """ ws: WSAssistant = await self._get_ws_assistant() await ws.connect(ws_url=CONSTANTS.WSS_PRIVATE_URL, ping_timeout=CONSTANTS.WS_PING_TIMEOUT) payload = { "op": "login", "args": self._auth.websocket_login_parameters() } login_request: WSJSONRequest = WSJSONRequest(payload=payload) async with self._api_factory.throttler.execute_task( limit_id=CONSTANTS.WS_SUBSCRIBE): await ws.send(login_request) response: WSResponse = await ws.receive() message = response.data if "errorCode" in message or "error_code" in message or message.get( "event") != "login": self.logger().error( "Error authenticating the private websocket connection") raise IOError( f"Private websocket connection authentication failed ({message})" ) return ws
async def _authenticate_connection(self, ws: WSAssistant): """ Sends the authentication message. :param ws: the websocket assistant used to connect to the exchange """ auth_message: WSJSONRequest = WSJSONRequest(payload=self._auth.generate_ws_authentication_message()) await ws.send(auth_message)
async def _subscribe_channels(self, ws: WSAssistant): """ Subscribes to the trade events and diff orders events through the provided websocket connection. :param ws: the websocket assistant used to connect to the exchange """ try: trade_params = [] depth_params = [] for trading_pair in self._trading_pairs: symbol = await self.convert_to_exchange_trading_pair( hb_trading_pair=trading_pair, domain=self._domain, api_factory=self._api_factory, throttler=self._throttler) trade_params.append(f"trade:{symbol}") depth_params.append(f"depth:{symbol}") payload: Dict[str, str] = { "op": "subscribe", "args": trade_params + depth_params, } subscribe_request: WSJSONRequest = WSJSONRequest(payload=payload) await ws.send(subscribe_request) self.logger().info( "Subscribed to public order book and trade channels...") except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error occurred subscribing to order book trading and delta streams...", exc_info=True) raise
def test_send_when_disconnected_raises(self): request = WSJSONRequest(payload={"one": 1}) with self.assertRaises(RuntimeError) as e: self.async_run_with_timeout(self.ws_connection.send(request)) self.assertEqual("WS is not connected.", str(e.exception))
async def _subscribe_channels(self, websocket_assistant: WSAssistant): try: symbols = [ await self._connector.exchange_symbol_associated_to_pair( trading_pair=trading_pair) for trading_pair in self._trading_pairs ] payload = { "op": "subscribe", "args": [ f"{CONSTANTS.PRIVATE_ORDER_PROGRESS_CHANNEL_NAME}:{symbol}" for symbol in symbols ] } subscribe_request: WSJSONRequest = WSJSONRequest(payload=payload) async with self._api_factory.throttler.execute_task( limit_id=CONSTANTS.WS_SUBSCRIBE): await websocket_assistant.send(subscribe_request) self.logger().info( "Subscribed to private account and orders channels...") except asyncio.CancelledError: raise except Exception: self.logger().exception( "Unexpected error occurred subscribing to order book trading and delta streams..." ) raise
async def _connected_websocket_assistant(self) -> WSAssistant: """ Creates an instance of WSAssistant connected to the exchange """ ws: WSAssistant = await self._get_ws_assistant() async with self._api_factory.throttler.execute_task( limit_id=CONSTANTS.WS_CONNECTION_LIMIT_ID): await ws.connect( ws_url=CONSTANTS.OKX_WS_URI_PRIVATE, message_timeout=CONSTANTS.SECONDS_TO_WAIT_TO_RECEIVE_MESSAGE) payload = { "op": "login", "args": [self._auth.websocket_login_parameters()] } login_request: WSJSONRequest = WSJSONRequest(payload=payload) async with self._api_factory.throttler.execute_task( limit_id=CONSTANTS.WS_LOGIN_LIMIT_ID): await ws.send(login_request) response: WSResponse = await ws.receive() message = response.data if message.get("event") != "login": self.logger().error( "Error authenticating the private websocket connection") raise IOError("Private websocket connection authentication failed") return ws
async def listen_for_user_stream(self, output: asyncio.Queue): ws = None while True: try: ws: WSAssistant = await self._get_ws_assistant() await ws.connect( ws_url=web_utils.websocket_url(domain=self._domain), ping_timeout=CONSTANTS.HEARTBEAT_TIME_INTERVAL) await ws.send(WSJSONRequest({}, is_auth_required=True)) await self._subscribe_channels(ws) await ws.ping() # to update last_recv_timestamp async for ws_response in ws.iter_messages(): data = ws_response.data event_type = data.get("event") if event_type == "subscribe" and data.get("channel"): self._subscribed_channels.append(data.get("channel")) self.logger().info( f"Subscribed to private channel - {data.get('channel')}..." ) elif len(data) > 0: output.put_nowait(data) except asyncio.CancelledError: raise except Exception as e: self.logger().error( f"Unexpected error while listening to user stream. Retrying after 5 seconds... " f"Error: {e}", exc_info=True, ) finally: # Make sure no background task is leaked. ws and await ws.disconnect() self._subscribed_channels = [] await self._sleep(5)
async def _subscribe_to_order_book_streams(self) -> WSAssistant: url = web_utils.wss_url("", self._domain) ws: WSAssistant = await self._get_ws_assistant() await ws.connect(ws_url=url) stream_channels = [ "trade", "orderBookL2", ] for channel in stream_channels: params = [] for trading_pair in self._trading_pairs: symbol = await self.convert_to_exchange_trading_pair( hb_trading_pair=trading_pair, domain=self._domain, throttler=self._throttler) params.append(f"{channel}:{symbol}") payload = { "op": "subscribe", "args": params, } subscribe_request: WSJSONRequest = WSJSONRequest( payload=payload, is_auth_required=False) await ws.send(subscribe_request) return ws
def test_no_auth_added_to_wsrequest(self): payload = {"param1": "value_param_1"} request = WSJSONRequest(payload=payload, is_auth_required=True) self.async_run_with_timeout(self.auth.ws_authenticate(request)) self.assertEqual(payload, request.payload)
async def listen_for_user_stream(self, output: asyncio.Queue): ws = None while True: try: self.logger().info(f"Connecting to {CONSTANTS.DYDX_WS_URL}") ws: WSAssistant = await self._get_ws_assistant() await ws.connect(ws_url=CONSTANTS.DYDX_WS_URL, ping_timeout=self.HEARTBEAT_INTERVAL) auth_params = self._dydx_auth.get_ws_auth_params() auth_request: WSJSONRequest = WSJSONRequest(auth_params) await ws.send(auth_request) self.logger().info("Authenticated user stream...") async for ws_response in ws.iter_messages(): data = ws_response.data if data.get("type", "") in ["subscribed", "channel_data"]: output.put_nowait(data) except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error with dydx WebSocket connection. " "Retrying after 30 seconds...", exc_info=True) finally: # Make sure no background tasks is leaked ws and await ws.disconnect() await self._sleep(30.0)
async def listen_for_user_stream(self, output: asyncio.Queue): while True: try: # Initialize Websocket Connection self.logger().info(f"Connecting to {CONSTANTS.WS_PRIVATE_URL}") await self._get_ws_assistant() await self._ws_assistant.connect( ws_url=CONSTANTS.WS_PRIVATE_URL, ping_timeout=self.HEARTBEAT_INTERVAL) await self._authenticate_client() await self._subscribe_channels( websocket_assistant=self._ws_assistant) async for ws_response in self._ws_assistant.iter_messages(): data = ws_response.data if data["action"] == "ping": pong_request = WSJSONRequest(payload={ "action": "pong", "data": data["data"] }) await self._ws_assistant.send(request=pong_request) continue output.put_nowait(data) except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error with Huobi WebSocket connection. " "Retrying after 30 seconds...", exc_info=True) finally: self._ws_assistant and await self._ws_assistant.disconnect() await self._sleep(30.0)
async def _subscribe_to_order_book_streams(self) -> WSAssistant: url = f"{web_utils.wss_url(CONSTANTS.PUBLIC_WS_ENDPOINT, self._domain)}" ws: WSAssistant = await self._api_factory.get_ws_assistant() await ws.connect(ws_url=url, ping_timeout=CONSTANTS.HEARTBEAT_TIME_INTERVAL) stream_id_channel_pairs = [ (CONSTANTS.DIFF_STREAM_ID, "@depth"), (CONSTANTS.TRADE_STREAM_ID, "@aggTrade"), (CONSTANTS.FUNDING_INFO_STREAM_ID, "@markPrice"), ] for stream_id, channel in stream_id_channel_pairs: params = [] for trading_pair in self._trading_pairs: symbol = await self.convert_to_exchange_trading_pair( hb_trading_pair=trading_pair, domain=self._domain, throttler=self._throttler, api_factory=self._api_factory, time_synchronizer=self._time_synchronizer) params.append(f"{symbol.lower()}{channel}") payload = { "method": "SUBSCRIBE", "params": params, "id": stream_id, } subscribe_request: WSJSONRequest = WSJSONRequest(payload) await ws.send(subscribe_request) return ws
async def listen_for_subscriptions(self): ws = None while True: try: ws: WSAssistant = await self._get_ws_assistant() await ws.connect(ws_url=CONSTANTS.WS_PUBLIC_URL, ping_timeout=self.HEARTBEAT_INTERVAL) await self._subscribe_channels(ws) async for ws_response in ws.iter_messages(): data = ws_response.data if "subbed" in data: continue if "ping" in data: ping_request = WSJSONRequest(payload={ "pong": data["ping"] }) await ws.send(request=ping_request) channel = data.get("ch", "") if channel.endswith(self.TRADE_CHANNEL_SUFFIX): self._message_queue[self.TRADE_CHANNEL_SUFFIX].put_nowait(data) if channel.endswith(self.ORDERBOOK_CHANNEL_SUFFIX): self._message_queue[self.ORDERBOOK_CHANNEL_SUFFIX].put_nowait(data) except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error occurred when listening to order book streams. Retrying in 5 seconds...", exc_info=True, ) await self._sleep(5.0) finally: ws and await ws.disconnect()
async def listen_for_user_stream(self, output: asyncio.Queue): """ *required Subscribe to user stream via web socket, and keep the connection open for incoming messages :param output: an async queue where the incoming messages are stored """ ws = None while True: try: rest_assistant = await self._api_factory.get_rest_assistant() url = f"{CONSTANTS.REST_URL}/{CONSTANTS.INFO_PATH_URL}" request = RESTRequest(method=RESTMethod.GET, url=url, endpoint_url=CONSTANTS.INFO_PATH_URL, is_auth_required=True) async with self._throttler.execute_task( CONSTANTS.INFO_PATH_URL): response: RESTResponse = await rest_assistant.call( request=request) info = await response.json() accountGroup = info.get("data").get("accountGroup") headers = self._ascend_ex_auth.get_auth_headers( CONSTANTS.STREAM_PATH_URL) payload = { "op": CONSTANTS.SUB_ENDPOINT_NAME, "ch": "order:cash" } ws: WSAssistant = await self._get_ws_assistant() url = f"{get_ws_url_private(accountGroup)}/{CONSTANTS.STREAM_PATH_URL}" await ws.connect(ws_url=url, ws_headers=headers, ping_timeout=self.HEARTBEAT_PING_INTERVAL) subscribe_request: WSJSONRequest = WSJSONRequest(payload) async with self._throttler.execute_task( CONSTANTS.SUB_ENDPOINT_NAME): await ws.send(subscribe_request) async for raw_msg in ws.iter_messages(): msg = raw_msg.data if msg is None: continue self._last_recv_time = time.time() output.put_nowait(msg) except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error with AscendEx WebSocket connection. " "Retrying after 30 seconds...", exc_info=True) await self._sleep(30.0) finally: ws and await ws.disconnect()
async def listen_for_order_book_diffs(self, ev_loop: asyncio.AbstractEventLoop, output: asyncio.Queue): """ *required Subscribe to diff channel via web socket, and keep the connection open for incoming messages :param ev_loop: ev_loop to execute this function in :param output: an async queue where the incoming messages are stored """ while True: try: trading_pairs: List[str] = self._trading_pairs ws_assistant = await self._web_assistants_factory.get_ws_assistant( ) await ws_assistant.connect( CONSTANTS.WS_URL, message_timeout=CONSTANTS.WS_MESSAGE_TIMEOUT) subscribe_payload = { "type": "subscribe", "product_ids": trading_pairs, "channels": [CONSTANTS.FULL_CHANNEL_NAME] } subscribe_request = WSJSONRequest(payload=subscribe_payload) await ws_assistant.subscribe(subscribe_request) async for msg in self._iter_messages(ws_assistant): msg_type: str = msg.get("type", None) if msg_type is None: raise ValueError( f"Coinbase Pro Websocket message does not contain a type - {msg}" ) elif msg_type == "error": raise ValueError( f"Coinbase Pro Websocket received error message - {msg['message']}" ) elif msg_type in ["open", "match", "change", "done"]: if msg_type == "done" and "price" not in msg: # done messages with no price are completed market orders which can be ignored continue order_book_message: OrderBookMessage = CoinbaseProOrderBook.diff_message_from_exchange( msg) output.put_nowait(order_book_message) elif msg_type in ["received", "activate", "subscriptions"]: # these messages are not needed to track the order book continue else: raise ValueError( f"Unrecognized Coinbase Pro Websocket message received - {msg}" ) except asyncio.CancelledError: raise except Exception: self.logger().network( "Unexpected error with WebSocket connection.", exc_info=True, app_warning_msg= f"Unexpected error with WebSocket connection." f" Retrying in {CONSTANTS.REST_API_LIMIT_COOLDOWN} seconds." f" Check network connection.") await self._sleep(CONSTANTS.WS_RECONNECT_COOLDOWN)
async def _handle_ping_message(self, ws: aiohttp.ClientWebSocketResponse): """ Responds with pong to a ping message send by a server to keep a websocket connection alive """ async with self._throttler.execute_task(CONSTANTS.PONG_ENDPOINT_NAME): payload = {"op": "pong"} pong_request: WSJSONRequest = WSJSONRequest(payload) await ws.send(pong_request)
def test_ws_authenticate(self): request: WSJSONRequest = WSJSONRequest( payload={"TEST": "SOME_TEST_PAYLOAD"}, throttler_limit_id="TEST_LIMIT_ID", is_auth_required=True) signed_request: WSJSONRequest = self.async_run_with_timeout( self.auth.ws_authenticate(request)) self.assertEqual(request, signed_request)
async def _subscribe_channels(self, ws: WSAssistant): """ Subscribes to the trade events and diff orders events through the provided websocket connection. :param ws: the websocket assistant used to connect to the exchange """ try: for trading_pair in self._trading_pairs: symbol = await self._connector.exchange_symbol_associated_to_pair( trading_pair=trading_pair) trades_payload = { "time": int(self._time()), "channel": CONSTANTS.TRADES_ENDPOINT_NAME, "event": "subscribe", "payload": [symbol] } subscribe_trade_request: WSJSONRequest = WSJSONRequest( payload=trades_payload) order_book_payload = { "time": int(self._time()), "channel": CONSTANTS.ORDERS_UPDATE_ENDPOINT_NAME, "event": "subscribe", "payload": [symbol, "100ms"] } subscribe_orderbook_request: WSJSONRequest = WSJSONRequest( payload=order_book_payload) await ws.send(subscribe_trade_request) await ws.send(subscribe_orderbook_request) self.logger().info( "Subscribed to public order book and trade channels...") except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error occurred subscribing to order book data streams." ) raise
async def _subscribe_channels(self, ws: WSAssistant): try: for trading_pair in self._trading_pairs: subscribe_orderbook_request: WSJSONRequest = WSJSONRequest({ "sub": f"market.{convert_to_exchange_trading_pair(trading_pair)}.depth.step0", "id": convert_to_exchange_trading_pair(trading_pair) }) subscribe_trade_request: WSJSONRequest = WSJSONRequest({ "sub": f"market.{convert_to_exchange_trading_pair(trading_pair)}.trade.detail", "id": convert_to_exchange_trading_pair(trading_pair) }) await ws.send(subscribe_orderbook_request) await ws.send(subscribe_trade_request) self.logger().info("Subscribed to public orderbook and trade channels...") except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error occurred subscribing to order book trading and delta streams...", exc_info=True ) raise
async def _subscribe_channels(self, websocket_assistant: WSAssistant): """ Subscribes to order events and balance events. :param ws: the websocket assistant used to connect to the exchange """ try: orders_change_payload = { "id": web_utils.next_message_id(), "type": "subscribe", "topic": "/spotMarket/tradeOrders", "privateChannel": True, "response": False, } subscribe_order_change_request: WSJSONRequest = WSJSONRequest( payload=orders_change_payload) balance_payload = { "id": web_utils.next_message_id(), "type": "subscribe", "topic": "/account/balance", "privateChannel": True, "response": False, } subscribe_balance_request: WSJSONRequest = WSJSONRequest( payload=balance_payload) await websocket_assistant.send(subscribe_order_change_request) await websocket_assistant.send(subscribe_balance_request) self._last_ws_message_sent_timestamp = self._time() self.logger().info( "Subscribed to private order changes and balance updates channels..." ) except asyncio.CancelledError: raise except Exception: self.logger().exception( "Unexpected error occurred subscribing to user streams...") raise
def test_send(self, ws_connect_mock): ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock( ) self.async_run_with_timeout(self.ws_connection.connect(self.ws_url)) request = WSJSONRequest(payload={"one": 1}) self.async_run_with_timeout(self.ws_connection.send(request)) json_msgs = self.mocking_assistant.json_messages_sent_through_websocket( ws_connect_mock.return_value) self.assertEqual(1, len(json_msgs)) self.assertEqual(request.payload, json_msgs[0])
def test_subscribe(self, send_mock): sent_requests = [] send_mock.side_effect = lambda r: sent_requests.append(r) payload = {"one": 1} request = WSJSONRequest(payload) self.async_run_with_timeout(self.ws_assistant.subscribe(request)) self.assertEqual(1, len(sent_requests)) sent_request = sent_requests[0] self.assertNotEqual(id(request), id(sent_request)) # has been cloned self.assertEqual(request, sent_request)
async def _process_websocket_messages(self, websocket_assistant: WSAssistant): while True: try: seconds_until_next_ping = self._ping_interval - (self._time() - self._last_ws_message_sent_timestamp) await asyncio.wait_for(super()._process_websocket_messages(websocket_assistant=websocket_assistant), timeout=seconds_until_next_ping) except asyncio.TimeoutError: payload = { "id": web_utils.next_message_id(), "type": "ping", } ping_request = WSJSONRequest(payload=payload) self._last_ws_message_sent_timestamp = self._time() await websocket_assistant.send(request=ping_request)