Exemple #1
0
    async def _ping_listen_key(self) -> bool:
        rest_assistant = await self._api_factory.get_rest_assistant()
        try:
            data = await rest_assistant.execute_request(
                url=web_utils.public_rest_url(
                    path_url=CONSTANTS.BINANCE_USER_STREAM_PATH_URL,
                    domain=self._domain),
                params={"listenKey": self._current_listen_key},
                method=RESTMethod.PUT,
                return_err=True,
                throttler_limit_id=CONSTANTS.BINANCE_USER_STREAM_PATH_URL,
                headers=self._auth.header_for_authentication())

            if "code" in data:
                self.logger().warning(
                    f"Failed to refresh the listen key {self._current_listen_key}: {data}"
                )
                return False

        except asyncio.CancelledError:
            raise
        except Exception as exception:
            self.logger().warning(
                f"Failed to refresh the listen key {self._current_listen_key}: {exception}"
            )
            return False

        return True
    def test_get_last_price(self, mock_api, connector_creator_mock):
        client_config_map = ClientConfigAdapter(ClientConfigMap())
        connector = BinanceExchange(
            client_config_map,
            binance_api_key="",
            binance_api_secret="",
            trading_pairs=[],
            trading_required=False)
        connector._set_trading_pair_symbol_map(bidict({f"{self.binance_ex_trading_pair}": self.trading_pair}))
        connector_creator_mock.return_value = connector

        url = web_utils.public_rest_url(path_url=CONSTANTS.TICKER_PRICE_CHANGE_PATH_URL)
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_response: Dict[str, Any] = {
            # truncated response
            "symbol": self.binance_ex_trading_pair,
            "lastPrice": "1",
        }
        mock_api.get(regex_url, body=ujson.dumps(mock_response))

        result = self.async_run_with_timeout(market_price.get_last_price(
            exchange="binance",
            trading_pair=self.trading_pair,
        ))

        self.assertEqual(result, Decimal("1.0"))
Exemple #3
0
 def test_public_rest_url(self):
     path_url = "/TEST_PATH"
     domain = "com"
     expected_url = CONSTANTS.REST_URL.format(
         domain) + CONSTANTS.PUBLIC_API_VERSION + path_url
     self.assertEqual(expected_url,
                      web_utils.public_rest_url(path_url, domain))
    async def _request_order_book_snapshot(
            self, trading_pair: str) -> Dict[str, Any]:
        """
        Retrieves a copy of the full order book from the exchange, for a particular trading pair.

        :param trading_pair: the trading pair for which the order book will be retrieved

        :return: the response from the exchange (JSON dictionary)
        """
        params = {
            "symbol":
            await self._connector.exchange_symbol_associated_to_pair(
                trading_pair=trading_pair),
            "limit":
            "1000"
        }

        rest_assistant = await self._api_factory.get_rest_assistant()
        data = await rest_assistant.execute_request(
            url=web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL,
                                          domain=self._domain),
            params=params,
            method=RESTMethod.GET,
            throttler_limit_id=CONSTANTS.SNAPSHOT_PATH_URL,
        )

        return data
Exemple #5
0
    def test_get_new_order_book_successful(self, mock_api):
        url = web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL,
                                        domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        resp = self._snapshot_response()

        mock_api.get(regex_url, body=json.dumps(resp))

        order_book: OrderBook = self.async_run_with_timeout(
            self.data_source.get_new_order_book(self.trading_pair))

        expected_update_id = resp["lastUpdateId"]

        self.assertEqual(expected_update_id, order_book.snapshot_uid)
        bids = list(order_book.bid_entries())
        asks = list(order_book.ask_entries())
        self.assertEqual(1, len(bids))
        self.assertEqual(4, bids[0].price)
        self.assertEqual(431, bids[0].amount)
        self.assertEqual(expected_update_id, bids[0].update_id)
        self.assertEqual(1, len(asks))
        self.assertEqual(4.000002, asks[0].price)
        self.assertEqual(12, asks[0].amount)
        self.assertEqual(expected_update_id, asks[0].update_id)
Exemple #6
0
    def test_get_snapshot_catch_exception(self, mock_api):
        url = web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL,
                                        domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        mock_api.get(regex_url, status=400)
        with self.assertRaises(IOError):
            self.async_run_with_timeout(
                self.data_source.get_snapshot(self.trading_pair))
Exemple #7
0
    def test_get_snapshot_successful(self, mock_api):
        url = web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL,
                                        domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        mock_api.get(regex_url, body=json.dumps(self._snapshot_response()))

        result: Dict[str, Any] = self.async_run_with_timeout(
            self.data_source.get_snapshot(self.trading_pair))

        self.assertEqual(self._snapshot_response(), result)
Exemple #8
0
    def test_listen_for_order_book_snapshots_cancelled_when_fetching_snapshot(
            self, mock_api):
        url = web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL,
                                        domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        mock_api.get(regex_url, exception=asyncio.CancelledError)

        with self.assertRaises(asyncio.CancelledError):
            self.async_run_with_timeout(
                self.data_source.listen_for_order_book_snapshots(
                    self.ev_loop, asyncio.Queue()))
Exemple #9
0
    def test_get_all_mid_prices(self, mock_api):
        url = web_utils.public_rest_url(CONSTANTS.SERVER_TIME_PATH_URL,
                                        domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        response = {"serverTime": 1640000003000}

        mock_api.get(regex_url, body=json.dumps(response))

        url = web_utils.public_rest_url(
            path_url=CONSTANTS.TICKER_PRICE_CHANGE_PATH_URL,
            domain=self.domain)

        mock_response: List[Dict[str, Any]] = [
            {
                # Truncated Response
                "symbol": self.ex_trading_pair,
                "bidPrice": "99",
                "askPrice": "101",
            },
            {
                # Truncated Response for unrecognized pair
                "symbol": "BCCBTC",
                "bidPrice": "99",
                "askPrice": "101",
            }
        ]

        mock_api.get(url, body=json.dumps(mock_response))

        result: Dict[str, float] = self.async_run_with_timeout(
            self.data_source.get_all_mid_prices())

        self.assertEqual(1, len(result))
        self.assertEqual(100, result[self.trading_pair])
Exemple #10
0
    def test_fetch_trading_pairs_exception_raised(self, mock_api):
        BinanceAPIOrderBookDataSource._trading_pair_symbol_map = {}

        url = web_utils.public_rest_url(
            path_url=CONSTANTS.EXCHANGE_INFO_PATH_URL, domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        mock_api.get(regex_url, exception=Exception)

        result: Dict[str] = self.async_run_with_timeout(
            self.data_source.fetch_trading_pairs(
                time_synchronizer=self.time_synchronizer))

        self.assertEqual(0, len(result))
Exemple #11
0
    async def _get_listen_key(self):
        rest_assistant = await self._api_factory.get_rest_assistant()
        try:
            data = await rest_assistant.execute_request(
                url=web_utils.public_rest_url(
                    path_url=CONSTANTS.BINANCE_USER_STREAM_PATH_URL,
                    domain=self._domain),
                method=RESTMethod.POST,
                throttler_limit_id=CONSTANTS.BINANCE_USER_STREAM_PATH_URL,
                headers=self._auth.header_for_authentication())
        except asyncio.CancelledError:
            raise
        except Exception as exception:
            raise IOError(
                f"Error fetching user stream listen key. Error: {exception}")

        return data["listenKey"]
Exemple #12
0
    def test_get_new_order_book(self, mock_api):
        url = web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL,
                                        domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        mock_response: Dict[str, Any] = {
            "lastUpdateId": 1,
            "bids": [["4.00000000", "431.00000000"]],
            "asks": [["4.00000200", "12.00000000"]]
        }
        mock_api.get(regex_url, body=json.dumps(mock_response))

        result: OrderBook = self.async_run_with_timeout(
            self.data_source.get_new_order_book(self.trading_pair))

        self.assertEqual(1, result.snapshot_uid)
Exemple #13
0
    def test_listen_for_order_book_snapshots_successful(
        self,
        mock_api,
    ):
        msg_queue: asyncio.Queue = asyncio.Queue()
        url = web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL,
                                        domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        mock_api.get(regex_url, body=json.dumps(self._snapshot_response()))

        self.listening_task = self.ev_loop.create_task(
            self.data_source.listen_for_order_book_snapshots(
                self.ev_loop, msg_queue))

        msg: OrderBookMessage = self.async_run_with_timeout(msg_queue.get())

        self.assertEqual(1027024, msg.update_id)
Exemple #14
0
    def test_get_last_trade_prices(self, mock_api):
        url = web_utils.public_rest_url(
            path_url=CONSTANTS.TICKER_PRICE_CHANGE_PATH_URL,
            domain=self.domain)
        url = f"{url}?symbol={self.base_asset}{self.quote_asset}"
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        mock_response = {
            "symbol": "BNBBTC",
            "priceChange": "-94.99999800",
            "priceChangePercent": "-95.960",
            "weightedAvgPrice": "0.29628482",
            "prevClosePrice": "0.10002000",
            "lastPrice": "100.0",
            "lastQty": "200.00000000",
            "bidPrice": "4.00000000",
            "bidQty": "100.00000000",
            "askPrice": "4.00000200",
            "askQty": "100.00000000",
            "openPrice": "99.00000000",
            "highPrice": "100.00000000",
            "lowPrice": "0.10000000",
            "volume": "8913.30000000",
            "quoteVolume": "15.30000000",
            "openTime": 1499783499040,
            "closeTime": 1499869899040,
            "firstId": 28385,
            "lastId": 28460,
            "count": 76,
        }

        mock_api.get(regex_url, body=json.dumps(mock_response))

        result: Dict[str, float] = self.async_run_with_timeout(
            self.data_source.get_last_traded_prices(
                trading_pairs=[self.trading_pair],
                throttler=self.throttler,
                time_synchronizer=self.time_synchronizer))

        self.assertEqual(1, len(result))
        self.assertEqual(100, result[self.trading_pair])
    def test_get_last_price(self, mock_api):
        BinanceAPIOrderBookDataSource._trading_pair_symbol_map = {
            "com":
            bidict({f"{self.binance_ex_trading_pair}": self.trading_pair})
        }

        url = web_utils.public_rest_url(
            path_url=CONSTANTS.TICKER_PRICE_CHANGE_PATH_URL)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_response: Dict[str, Any] = {
            # truncated response
            "symbol": self.binance_ex_trading_pair,
            "lastPrice": "1",
        }
        mock_api.get(regex_url, body=ujson.dumps(mock_response))

        result = self.async_run_with_timeout(
            market_price.get_last_price(exchange="binance",
                                        trading_pair=self.trading_pair))

        self.assertEqual(result, Decimal("1.0"))
Exemple #16
0
    def test_listen_for_order_book_snapshots_log_exception(
            self, mock_api, sleep_mock):
        msg_queue: asyncio.Queue = asyncio.Queue()
        sleep_mock.side_effect = lambda _: self._create_exception_and_unlock_test_with_event(
            asyncio.CancelledError())

        url = web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL,
                                        domain=self.domain)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))

        mock_api.get(regex_url, exception=Exception)

        self.listening_task = self.ev_loop.create_task(
            self.data_source.listen_for_order_book_snapshots(
                self.ev_loop, msg_queue))
        self.async_run_with_timeout(self.resume_test_event.wait())

        self.assertTrue(
            self._is_logged(
                "ERROR",
                f"Unexpected error fetching order book snapshot for {self.trading_pair}."
            ))
    def test_get_binance_mid_price(self, mock_api):
        BinanceAPIOrderBookDataSource._trading_pair_symbol_map = {
            "com":
            bidict({f"{self.base_asset}{self.quote_asset}": self.trading_pair})
        }

        url = web_utils.public_rest_url(
            path_url=CONSTANTS.TICKER_PRICE_CHANGE_PATH_URL)
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_response: List[Dict[str, Any]] = [
            {
                # Truncated Response
                "symbol": self.binance_ex_trading_pair,
                "bidPrice": "1.0",
                "askPrice": "2.0",
            },
        ]
        mock_api.get(regex_url, body=ujson.dumps(mock_response))

        result = self.async_run_with_timeout(
            market_price.get_binance_mid_price(trading_pair=self.trading_pair))

        self.assertEqual(result, Decimal("1.5"))
 def all_symbols_url(self):
     return web_utils.public_rest_url(path_url=CONSTANTS.EXCHANGE_INFO_PATH_URL, domain=self.exchange._domain)
 def latest_prices_url(self):
     url = web_utils.public_rest_url(path_url=CONSTANTS.TICKER_PRICE_CHANGE_PATH_URL, domain=self.exchange._domain)
     url = f"{url}?symbol={self.exchange_symbol_for_tokens(self.base_asset, self.quote_asset)}"
     return url
Exemple #20
0
    def test_fetch_trading_pairs(self, mock_api):
        BinanceAPIOrderBookDataSource._trading_pair_symbol_map = {}
        url = web_utils.public_rest_url(
            path_url=CONSTANTS.EXCHANGE_INFO_PATH_URL, domain=self.domain)

        mock_response: Dict[str, Any] = {
            "timezone":
            "UTC",
            "serverTime":
            1639598493658,
            "rateLimits": [],
            "exchangeFilters": [],
            "symbols": [
                {
                    "symbol":
                    "ETHBTC",
                    "status":
                    "TRADING",
                    "baseAsset":
                    "ETH",
                    "baseAssetPrecision":
                    8,
                    "quoteAsset":
                    "BTC",
                    "quotePrecision":
                    8,
                    "quoteAssetPrecision":
                    8,
                    "baseCommissionPrecision":
                    8,
                    "quoteCommissionPrecision":
                    8,
                    "orderTypes": [
                        "LIMIT", "LIMIT_MAKER", "MARKET", "STOP_LOSS_LIMIT",
                        "TAKE_PROFIT_LIMIT"
                    ],
                    "icebergAllowed":
                    True,
                    "ocoAllowed":
                    True,
                    "quoteOrderQtyMarketAllowed":
                    True,
                    "isSpotTradingAllowed":
                    True,
                    "isMarginTradingAllowed":
                    True,
                    "filters": [],
                    "permissions": ["SPOT", "MARGIN"]
                },
                {
                    "symbol":
                    "LTCBTC",
                    "status":
                    "TRADING",
                    "baseAsset":
                    "LTC",
                    "baseAssetPrecision":
                    8,
                    "quoteAsset":
                    "BTC",
                    "quotePrecision":
                    8,
                    "quoteAssetPrecision":
                    8,
                    "baseCommissionPrecision":
                    8,
                    "quoteCommissionPrecision":
                    8,
                    "orderTypes": [
                        "LIMIT", "LIMIT_MAKER", "MARKET", "STOP_LOSS_LIMIT",
                        "TAKE_PROFIT_LIMIT"
                    ],
                    "icebergAllowed":
                    True,
                    "ocoAllowed":
                    True,
                    "quoteOrderQtyMarketAllowed":
                    True,
                    "isSpotTradingAllowed":
                    True,
                    "isMarginTradingAllowed":
                    True,
                    "filters": [],
                    "permissions": ["SPOT", "MARGIN"]
                },
                {
                    "symbol":
                    "BNBBTC",
                    "status":
                    "TRADING",
                    "baseAsset":
                    "BNB",
                    "baseAssetPrecision":
                    8,
                    "quoteAsset":
                    "BTC",
                    "quotePrecision":
                    8,
                    "quoteAssetPrecision":
                    8,
                    "baseCommissionPrecision":
                    8,
                    "quoteCommissionPrecision":
                    8,
                    "orderTypes": [
                        "LIMIT", "LIMIT_MAKER", "MARKET", "STOP_LOSS_LIMIT",
                        "TAKE_PROFIT_LIMIT"
                    ],
                    "icebergAllowed":
                    True,
                    "ocoAllowed":
                    True,
                    "quoteOrderQtyMarketAllowed":
                    True,
                    "isSpotTradingAllowed":
                    True,
                    "isMarginTradingAllowed":
                    True,
                    "filters": [],
                    "permissions": ["MARGIN"]
                },
            ]
        }

        mock_api.get(url, body=json.dumps(mock_response))

        result: Dict[str] = self.async_run_with_timeout(
            self.data_source.fetch_trading_pairs(
                time_synchronizer=self.time_synchronizer))

        self.assertEqual(2, len(result))
        self.assertIn("ETH-BTC", result)
        self.assertIn("LTC-BTC", result)
        self.assertNotIn("BNB-BTC", result)
    def test_fetch_all(self, mock_api, all_connector_settings_mock):
        all_connector_settings_mock.return_value = {
            "binance":
            ConnectorSetting(name='binance',
                             type=ConnectorType.Exchange,
                             example_pair='ZRX-ETH',
                             centralised=True,
                             use_ethereum_wallet=False,
                             trade_fee_schema=TradeFeeSchema(
                                 percent_fee_token=None,
                                 maker_percent_fee_decimal=Decimal('0.001'),
                                 taker_percent_fee_decimal=Decimal('0.001'),
                                 buy_percent_fee_deducted_from_returns=False,
                                 maker_fixed_fees=[],
                                 taker_fixed_fees=[]),
                             config_keys={
                                 'binance_api_key':
                                 ConfigVar(key='binance_api_key', prompt=""),
                                 'binance_api_secret':
                                 ConfigVar(key='binance_api_secret', prompt="")
                             },
                             is_sub_domain=False,
                             parent_name=None,
                             domain_parameter=None,
                             use_eth_gas_lookup=False)
        }

        url = binance_web_utils.public_rest_url(
            path_url=CONSTANTS.EXCHANGE_INFO_PATH_URL)

        mock_response: Dict[str, Any] = {
            "timezone":
            "UTC",
            "serverTime":
            1639598493658,
            "rateLimits": [],
            "exchangeFilters": [],
            "symbols": [
                {
                    "symbol":
                    "ETHBTC",
                    "status":
                    "TRADING",
                    "baseAsset":
                    "ETH",
                    "baseAssetPrecision":
                    8,
                    "quoteAsset":
                    "BTC",
                    "quotePrecision":
                    8,
                    "quoteAssetPrecision":
                    8,
                    "baseCommissionPrecision":
                    8,
                    "quoteCommissionPrecision":
                    8,
                    "orderTypes": [
                        "LIMIT", "LIMIT_MAKER", "MARKET", "STOP_LOSS_LIMIT",
                        "TAKE_PROFIT_LIMIT"
                    ],
                    "icebergAllowed":
                    True,
                    "ocoAllowed":
                    True,
                    "quoteOrderQtyMarketAllowed":
                    True,
                    "isSpotTradingAllowed":
                    True,
                    "isMarginTradingAllowed":
                    True,
                    "filters": [],
                    "permissions": ["SPOT", "MARGIN"]
                },
                {
                    "symbol":
                    "LTCBTC",
                    "status":
                    "TRADING",
                    "baseAsset":
                    "LTC",
                    "baseAssetPrecision":
                    8,
                    "quoteAsset":
                    "BTC",
                    "quotePrecision":
                    8,
                    "quoteAssetPrecision":
                    8,
                    "baseCommissionPrecision":
                    8,
                    "quoteCommissionPrecision":
                    8,
                    "orderTypes": [
                        "LIMIT", "LIMIT_MAKER", "MARKET", "STOP_LOSS_LIMIT",
                        "TAKE_PROFIT_LIMIT"
                    ],
                    "icebergAllowed":
                    True,
                    "ocoAllowed":
                    True,
                    "quoteOrderQtyMarketAllowed":
                    True,
                    "isSpotTradingAllowed":
                    True,
                    "isMarginTradingAllowed":
                    True,
                    "filters": [],
                    "permissions": ["SPOT", "MARGIN"]
                },
                {
                    "symbol":
                    "BNBBTC",
                    "status":
                    "TRADING",
                    "baseAsset":
                    "BNB",
                    "baseAssetPrecision":
                    8,
                    "quoteAsset":
                    "BTC",
                    "quotePrecision":
                    8,
                    "quoteAssetPrecision":
                    8,
                    "baseCommissionPrecision":
                    8,
                    "quoteCommissionPrecision":
                    8,
                    "orderTypes": [
                        "LIMIT", "LIMIT_MAKER", "MARKET", "STOP_LOSS_LIMIT",
                        "TAKE_PROFIT_LIMIT"
                    ],
                    "icebergAllowed":
                    True,
                    "ocoAllowed":
                    True,
                    "quoteOrderQtyMarketAllowed":
                    True,
                    "isSpotTradingAllowed":
                    True,
                    "isMarginTradingAllowed":
                    True,
                    "filters": [],
                    "permissions": ["MARGIN"]
                },
            ]
        }

        mock_api.get(url, body=json.dumps(mock_response))

        client_config_map = ClientConfigAdapter(ClientConfigMap())
        fetcher = TradingPairFetcher(client_config_map)
        asyncio.get_event_loop().run_until_complete(fetcher._fetch_task)
        trading_pairs = fetcher.trading_pairs

        self.assertEqual(1, len(trading_pairs.keys()))
        self.assertIn("binance", trading_pairs)
        binance_pairs = trading_pairs["binance"]
        self.assertEqual(2, len(binance_pairs))
        self.assertIn("ETH-BTC", binance_pairs)
        self.assertIn("LTC-BTC", binance_pairs)
        self.assertNotIn("BNB-BTC", binance_pairs)