示例#1
0
    async def _user_stream_event_listener(self):
        """
        Listens to message in _user_stream_tracker.user_stream queue.
        """
        async for event_message in self._iter_user_event_queue():
            try:
                endpoint = NdaxWebSocketAdaptor.endpoint_from_message(
                    event_message)
                payload = NdaxWebSocketAdaptor.payload_from_message(
                    event_message)

                if endpoint == CONSTANTS.ACCOUNT_POSITION_EVENT_ENDPOINT_NAME:
                    self._process_account_position_event(payload)
                elif endpoint == CONSTANTS.ORDER_STATE_EVENT_ENDPOINT_NAME:
                    self._process_order_event_message(payload)
                elif endpoint == CONSTANTS.ORDER_TRADE_EVENT_ENDPOINT_NAME:
                    self._process_trade_event_message(payload)
                else:
                    self.logger().debug(
                        f"Unknown event received from the connector ({event_message})"
                    )
            except asyncio.CancelledError:
                raise
            except Exception as ex:
                self.logger().error(
                    f"Unexpected error in user stream listener loop ({ex})",
                    exc_info=True)
                await asyncio.sleep(5.0)
示例#2
0
    def test_sending_messages_increment_message_number(self, mock_ws):
        sent_messages = []
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        mock_ws.return_value = self.mocking_assistant.create_websocket_mock()
        mock_ws.return_value.send_json.side_effect = lambda sent_message: sent_messages.append(
            sent_message)

        adaptor = NdaxWebSocketAdaptor(throttler,
                                       websocket=mock_ws.return_value)
        payload = {}
        self.async_run_with_timeout(
            adaptor.send_request(endpoint_name=CONSTANTS.WS_PING_REQUEST,
                                 payload=payload,
                                 limit_id=CONSTANTS.WS_PING_ID))
        self.async_run_with_timeout(
            adaptor.send_request(endpoint_name=CONSTANTS.WS_PING_REQUEST,
                                 payload=payload,
                                 limit_id=CONSTANTS.WS_PING_ID))
        self.async_run_with_timeout(
            adaptor.send_request(endpoint_name=CONSTANTS.WS_ORDER_BOOK_CHANNEL,
                                 payload=payload))
        self.assertEqual(3, len(sent_messages))

        message = sent_messages[0]
        self.assertEqual(1, message.get('i'))
        message = sent_messages[1]
        self.assertEqual(2, message.get('i'))
        message = sent_messages[2]
        self.assertEqual(3, message.get('i'))
    def test_close(self):
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        ws = AsyncMock()

        adaptor = NdaxWebSocketAdaptor(throttler, websocket=ws)
        asyncio.get_event_loop().run_until_complete(adaptor.close())

        self.assertEquals(1, ws.close.await_count)
示例#4
0
    def test_close(self, mock_ws):
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        mock_ws.return_value = self.mocking_assistant.create_websocket_mock()

        adaptor = NdaxWebSocketAdaptor(throttler,
                                       websocket=mock_ws.return_value)
        self.async_run_with_timeout(adaptor.close())

        self.assertEquals(1, mock_ws.return_value.close.await_count)
    def test_receive_message(self):
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        ws = AsyncMock()
        ws.recv.return_value = 'test message'

        adaptor = NdaxWebSocketAdaptor(throttler, websocket=ws)
        received_message = asyncio.get_event_loop().run_until_complete(adaptor.recv())

        self.assertEqual('test message', received_message)
    def test_close(self, mock_ws):
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        mock_ws.return_value = self.mocking_assistant.create_websocket_mock()

        adaptor = NdaxWebSocketAdaptor(throttler,
                                       websocket=mock_ws.return_value)
        asyncio.get_event_loop().run_until_complete(adaptor.close())

        self.assertEquals(1, mock_ws.return_value.close.await_count)
示例#7
0
    def test_receive_message(self, mock_ws):
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        mock_ws.return_value = self.mocking_assistant.create_websocket_mock()
        self.mocking_assistant.add_websocket_aiohttp_message(
            mock_ws.return_value, 'test message')

        adaptor = NdaxWebSocketAdaptor(throttler,
                                       websocket=mock_ws.return_value)
        received_message = self.async_run_with_timeout(adaptor.receive())

        self.assertEqual('test message', received_message.data)
示例#8
0
    def test_get_endpoint_from_raw_received_message(self, mock_ws):
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        mock_ws.return_value = self.mocking_assistant.create_websocket_mock()
        payload = {"Key1": True, "Key2": "Value2"}
        message = {"m": 1, "i": 1, "n": "Endpoint", "o": json.dumps(payload)}
        raw_message = json.dumps(message)

        adaptor = NdaxWebSocketAdaptor(throttler,
                                       websocket=mock_ws.return_value)
        extracted_endpoint = adaptor.endpoint_from_raw_message(
            raw_message=raw_message)

        self.assertEqual("Endpoint", extracted_endpoint)
示例#9
0
    def test_listening_process_authenticates_and_subscribes_to_events(
            self, ws_connect_mock):
        messages = asyncio.Queue()
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        initial_last_recv_time = self.data_source.last_recv_time

        self.listening_task = asyncio.get_event_loop().create_task(
            self.data_source.listen_for_user_stream(asyncio.get_event_loop(),
                                                    messages))
        # Add the authentication response for the websocket
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value, self._authentication_response(True))
        # Add a dummy message for the websocket to read and include in the "messages" queue
        self.mocking_assistant.add_websocket_aiohttp_message(
            ws_connect_mock.return_value, json.dumps('dummyMessage'))

        first_received_message = asyncio.get_event_loop().run_until_complete(
            messages.get())

        self.assertEqual('dummyMessage', first_received_message)

        self.assertTrue(
            self._is_logged('INFO', "Authenticating to User Stream..."))
        self.assertTrue(
            self._is_logged('INFO',
                            "Successfully authenticated to User Stream."))
        self.assertTrue(
            self._is_logged('INFO', "Successfully subscribed to user events."))

        sent_messages = self.mocking_assistant.json_messages_sent_through_websocket(
            ws_connect_mock.return_value)
        self.assertEqual(2, len(sent_messages))
        authentication_request = sent_messages[0]
        subscription_request = sent_messages[1]
        self.assertEqual(
            CONSTANTS.AUTHENTICATE_USER_ENDPOINT_NAME,
            NdaxWebSocketAdaptor.endpoint_from_raw_message(
                json.dumps(authentication_request)))
        self.assertEqual(
            CONSTANTS.SUBSCRIBE_ACCOUNT_EVENTS_ENDPOINT_NAME,
            NdaxWebSocketAdaptor.endpoint_from_raw_message(
                json.dumps(subscription_request)))
        subscription_payload = NdaxWebSocketAdaptor.payload_from_raw_message(
            json.dumps(subscription_request))
        expected_payload = {"AccountId": self.account_id, "OMSId": self.oms_id}
        self.assertEqual(expected_payload, subscription_payload)

        self.assertGreater(self.data_source.last_recv_time,
                           initial_last_recv_time)
    def test_get_endpoint_from_raw_received_message(self):
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        ws = AsyncMock()
        payload = {"Key1": True,
                   "Key2": "Value2"}
        message = {"m": 1,
                   "i": 1,
                   "n": "Endpoint",
                   "o": json.dumps(payload)}
        raw_message = json.dumps(message)

        adaptor = NdaxWebSocketAdaptor(throttler, websocket=ws)
        extracted_endpoint = adaptor.endpoint_from_raw_message(raw_message=raw_message)

        self.assertEqual("Endpoint", extracted_endpoint)
示例#11
0
    async def _authenticate(self, ws: NdaxWebSocketAdaptor):
        """
        Authenticates user to websocket
        """
        try:
            auth_payload: Dict[
                str, Any] = self._auth_assistant.get_ws_auth_payload()
            async with self._throttler.execute_task(
                    CONSTANTS.AUTHENTICATE_USER_ENDPOINT_NAME):
                await ws.send_request(
                    CONSTANTS.AUTHENTICATE_USER_ENDPOINT_NAME, auth_payload)
            auth_resp = await ws.receive()
            auth_payload: Dict[str, Any] = ws.payload_from_raw_message(
                auth_resp.data)

            if not auth_payload["Authenticated"]:
                self.logger().error(f"Response: {auth_payload}", exc_info=True)
                raise Exception(
                    "Could not authenticate websocket connection with NDAX")

            auth_user = auth_payload.get("User")
            self._account_id = auth_user.get("AccountId")
            self._oms_id = auth_user.get("OMSId")

        except asyncio.CancelledError:
            raise
        except Exception as ex:
            self.logger().error(
                f"Error occurred when authenticating to user stream ({ex})",
                exc_info=True)
            raise
    def test_request_message_structure(self):
        sent_messages = []
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        ws = AsyncMock()
        ws.send.side_effect = lambda sent_message: sent_messages.append(sent_message)

        adaptor = NdaxWebSocketAdaptor(throttler, websocket=ws)
        payload = {"TestElement1": "Value1", "TestElement2": "Value2"}
        asyncio.get_event_loop().run_until_complete(adaptor.send_request(endpoint_name=CONSTANTS.WS_PING_REQUEST,
                                                                         payload=payload,
                                                                         limit_id=CONSTANTS.WS_PING_ID))

        self.assertEqual(1, len(sent_messages))
        message = json.loads(sent_messages[0])

        self.assertEqual(0, message.get('m'))
        self.assertEqual(1, message.get('i'))
        self.assertEqual(CONSTANTS.WS_PING_REQUEST, message.get('n'))
        message_payload = json.loads(message.get('o'))
        self.assertEqual(payload, message_payload)
 async def _create_websocket_connection(self) -> NdaxWebSocketAdaptor:
     """
     Initialize WebSocket client for UserStreamDataSource
     """
     try:
         ws = await websockets.connect(ndax_utils.wss_url(self._domain))
         return NdaxWebSocketAdaptor(throttler=self._throttler, websocket=ws)
     except asyncio.CancelledError:
         raise
     except Exception as ex:
         self.logger().network(f"Unexpected error occurred during {CONSTANTS.EXCHANGE_NAME} WebSocket Connection "
                               f"({ex})")
         raise
示例#14
0
    def test_request_message_structure(self, mock_ws):
        sent_messages = []
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        mock_ws.return_value = self.mocking_assistant.create_websocket_mock()
        mock_ws.return_value.send_json.side_effect = lambda sent_message: sent_messages.append(
            sent_message)

        adaptor = NdaxWebSocketAdaptor(throttler,
                                       websocket=mock_ws.return_value)
        payload = {"TestElement1": "Value1", "TestElement2": "Value2"}
        self.async_run_with_timeout(
            adaptor.send_request(endpoint_name=CONSTANTS.WS_PING_REQUEST,
                                 payload=payload,
                                 limit_id=CONSTANTS.WS_PING_ID))

        self.assertEqual(1, len(sent_messages))
        message = sent_messages[0]

        self.assertEqual(0, message.get('m'))
        self.assertEqual(1, message.get('i'))
        self.assertEqual(CONSTANTS.WS_PING_REQUEST, message.get('n'))
        message_payload = json.loads(message.get('o'))
        self.assertEqual(payload, message_payload)
    def test_sending_messages_increment_message_number(self):
        sent_messages = []
        throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
        ws = AsyncMock()
        ws.send.side_effect = lambda sent_message: sent_messages.append(sent_message)

        adaptor = NdaxWebSocketAdaptor(throttler, websocket=ws)
        payload = {}
        asyncio.get_event_loop().run_until_complete(adaptor.send_request(endpoint_name=CONSTANTS.WS_PING_REQUEST,
                                                                         payload=payload,
                                                                         limit_id=CONSTANTS.WS_PING_ID))
        asyncio.get_event_loop().run_until_complete(adaptor.send_request(endpoint_name=CONSTANTS.WS_PING_REQUEST,
                                                                         payload=payload,
                                                                         limit_id=CONSTANTS.WS_PING_ID))
        asyncio.get_event_loop().run_until_complete(adaptor.send_request(endpoint_name=CONSTANTS.WS_ORDER_BOOK_CHANNEL,
                                                                         payload=payload))
        self.assertEqual(3, len(sent_messages))

        message = json.loads(sent_messages[0])
        self.assertEqual(1, message.get('i'))
        message = json.loads(sent_messages[1])
        self.assertEqual(2, message.get('i'))
        message = json.loads(sent_messages[2])
        self.assertEqual(3, message.get('i'))
    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)