Beispiel #1
0
    async def collect_metrics(self, events: List[OrderFilledEvent]):
        try:
            total_volume = Decimal("0")

            for fill_event in events:
                trade_base, trade_quote = split_hb_trading_pair(
                    fill_event.trading_pair)
                from_quote_conversion_pair = combine_to_hb_trading_pair(
                    base=trade_quote, quote=self._valuation_token)
                rate = await self._rate_provider.stored_or_live_rate(
                    from_quote_conversion_pair)

                if rate is not None:
                    total_volume += fill_event.amount * fill_event.price * rate
                else:
                    from_base_conversion_pair = combine_to_hb_trading_pair(
                        base=trade_base, quote=self._valuation_token)
                    rate = await self._rate_provider.stored_or_live_rate(
                        from_base_conversion_pair)
                    if rate is not None:
                        total_volume += fill_event.amount * rate
                    else:
                        self.logger().debug(
                            f"Could not find a conversion rate rate using Rate Oracle for any of "
                            f"the pairs {from_quote_conversion_pair} or {from_base_conversion_pair}"
                        )

            if total_volume > Decimal("0"):
                self._dispatch_trade_volume(total_volume)
        except asyncio.CancelledError:
            raise
        except Exception:
            self._collected_events.extend(events)
Beispiel #2
0
    def fee_amount_in_token(self,
                            trading_pair: str,
                            price: Decimal,
                            order_amount: Decimal,
                            token: str,
                            exchange: Optional['ExchangeBase'] = None,
                            rate_source: Optional[Any] = None) -> Decimal:

        base, quote = split_hb_trading_pair(trading_pair)
        fee_amount = Decimal("0")
        if self.percent != 0:
            amount_from_percentage = (price * order_amount) * self.percent
            if self._are_tokens_interchangeable(quote, token):
                fee_amount += amount_from_percentage
            else:
                conversion_pair = combine_to_hb_trading_pair(base=quote,
                                                             quote=token)
                conversion_rate = self._get_exchange_rate(
                    conversion_pair, exchange, rate_source)
                fee_amount += amount_from_percentage * conversion_rate
        for flat_fee in self.flat_fees:
            if self._are_tokens_interchangeable(flat_fee.token, token):
                # No need to convert the value
                fee_amount += flat_fee.amount
            elif (self._are_tokens_interchangeable(flat_fee.token, base)
                  and (self._are_tokens_interchangeable(quote, token))):
                # In this case instead of looking for the rate we use directly the price in the parameters
                fee_amount += flat_fee.amount * price
            else:
                conversion_pair = combine_to_hb_trading_pair(
                    base=flat_fee.token, quote=token)
                conversion_rate = self._get_exchange_rate(
                    conversion_pair, exchange, rate_source)
                fee_amount += (flat_fee.amount * conversion_rate)
        return fee_amount
Beispiel #3
0
    def _get_fee(self,
                 base_currency: str,
                 quote_currency: str,
                 order_type: OrderType,
                 order_side: TradeType,
                 amount: Decimal,
                 price: Decimal = s_decimal_NaN,
                 is_maker: Optional[bool] = None) -> AddedToCostTradeFee:

        is_maker = is_maker or (order_type is OrderType.LIMIT_MAKER)
        trading_pair = combine_to_hb_trading_pair(base=base_currency,
                                                  quote=quote_currency)
        if trading_pair in self._trading_fees:
            fees_data = self._trading_fees[trading_pair]
            fee_value = Decimal(
                fees_data["makerFeeRate"]) if is_maker else Decimal(
                    fees_data["takerFeeRate"])
            fee = AddedToCostTradeFee(percent=fee_value)
        else:
            fee = build_trade_fee(
                self.name,
                is_maker,
                base_currency=base_currency,
                quote_currency=quote_currency,
                order_type=order_type,
                order_side=order_side,
                amount=amount,
                price=price,
            )
        return fee
Beispiel #4
0
    async def init_trading_pair_symbols(
            cls,
            domain: str = CONSTANTS.DOMAIN,
            throttler: Optional[AsyncThrottler] = None,
            api_factory: WebAssistantsFactory = None,
            time_synchronizer: Optional[TimeSynchronizer] = None):
        """Initialize _trading_pair_symbol_map class variable"""
        mapping = bidict()

        try:
            data = await web_utils.api_request(
                path=CONSTANTS.EXCHANGE_INFO_URL,
                api_factory=api_factory,
                throttler=throttler,
                time_synchronizer=time_synchronizer,
                domain=domain,
                method=RESTMethod.GET,
                timeout=10)

            for symbol_data in filter(utils.is_exchange_information_valid,
                                      data["symbols"]):
                try:
                    mapping[symbol_data["pair"]] = combine_to_hb_trading_pair(
                        symbol_data["baseAsset"], symbol_data["quoteAsset"])
                except ValueDuplicationError:
                    continue
        except Exception as ex:
            cls.logger().exception(
                f"There was an error requesting exchange info ({str(ex)})")

        cls._trading_pair_symbol_map[domain] = mapping
    def setUp(self) -> None:
        super().setUp()
        self.strategy = None
        self.markets = {"binance": ExchangeBase(client_config_map=ClientConfigAdapter(ClientConfigMap()))}
        self.notifications = []
        self.log_records = []
        self.base = "ETH"
        self.quote = "BTC"
        self.strategy_config_map = ClientConfigAdapter(
            AvellanedaMarketMakingConfigMap(
                exchange="binance",
                market=combine_to_hb_trading_pair(self.base, self.quote),
                execution_timeframe_mode=FromDateToDateModel(
                    start_datetime="2021-11-18 15:00:00",
                    end_datetime="2021-11-18 16:00:00",
                ),
                order_amount=60,
                order_refresh_time=60,
                hanging_orders_mode=TrackHangingOrdersModel(
                    hanging_orders_cancel_pct=1,
                ),
                order_levels_mode=MultiOrderLevelModel(
                    order_levels=4,
                    level_distances=1,
                ),
                min_spread=2,
                risk_factor=1.11,
                order_amount_shape_factor=0.33,
            )
        )

        self.raise_exception_for_market_initialization = False
        self._logger = None
Beispiel #6
0
    def get_fee_impact_on_order_cost(
            self, order_candidate: 'OrderCandidate',
            exchange: 'ExchangeBase') -> Optional[TokenAmount]:
        """
        WARNING: Do not use this method for sizing. Instead, use the `BudgetChecker`.

        Returns the impact of the fee on the cost requirements for the candidate order.
        """
        ret = None
        if self.percent != Decimal("0"):
            fee_token = self.percent_token or order_candidate.order_collateral.token
            if order_candidate.order_collateral is None or fee_token != order_candidate.order_collateral.token:
                token, size = order_candidate.get_size_token_and_order_size()
                if fee_token == token:
                    exchange_rate = Decimal("1")
                else:
                    exchange_pair = combine_to_hb_trading_pair(
                        token, fee_token)  # buy order token w/ pf token
                    exchange_rate = exchange.get_price(exchange_pair,
                                                       is_buy=True)
                fee_amount = size * exchange_rate * self.percent
            else:  # self.percent_token == order_candidate.order_collateral.token
                fee_amount = order_candidate.order_collateral.amount * self.percent
            ret = TokenAmount(fee_token, fee_amount)
        return ret
    async def init_trading_pair_symbols(
            cls,
            domain: str = CONSTANTS.DOMAIN,
            throttler: Optional[AsyncThrottler] = None,
            api_factory: WebAssistantsFactory = None):
        """Initialize _trading_pair_symbol_map class variable"""
        mapping = bidict()

        api_factory = api_factory or web_utils.build_api_factory()

        throttler = throttler or cls._get_throttler_instance()
        params = {"filter": json.dumps({"typ": "IFXXXP"})}

        try:
            data = await web_utils.api_request(CONSTANTS.EXCHANGE_INFO_URL,
                                               api_factory, throttler, domain,
                                               params)
            for symbol_data in data:
                try:
                    mapping[
                        symbol_data["symbol"]] = combine_to_hb_trading_pair(
                            symbol_data["rootSymbol"],
                            symbol_data["quoteCurrency"])
                except ValueDuplicationError:
                    continue

        except Exception as ex:
            cls.logger().error(
                f"There was an error requesting exchange info ({str(ex)})")

        cls._trading_pair_symbol_map[domain] = mapping
    async def _init_trading_pair_symbols(
            cls,
            domain: str = CONSTANTS.DEFAULT_DOMAIN,
            api_factory: Optional[WebAssistantsFactory] = None,
            throttler: Optional[AsyncThrottler] = None,
            time_synchronizer: Optional[TimeSynchronizer] = None):
        """
        Initialize mapping of trade symbols in exchange notation to trade symbols in client notation
        """
        mapping = bidict()

        try:
            data = await web_utils.api_request(
                path=CONSTANTS.EXCHANGE_INFO_PATH_URL,
                api_factory=api_factory,
                throttler=throttler,
                time_synchronizer=time_synchronizer,
                domain=domain,
                method=RESTMethod.GET,
            )

            for symbol_data in data["result"]:
                mapping[symbol_data["name"]] = combine_to_hb_trading_pair(
                    base=symbol_data["baseCurrency"],
                    quote=symbol_data["quoteCurrency"])
        except Exception as ex:
            cls.logger().error(
                f"There was an error requesting exchange info ({str(ex)})")
        cls._trading_pair_symbol_map[domain] = mapping
    def test_adjust_candidate_insufficient_funds_for_flat_fees_and_percent_fees_third_token(
            self):
        fc_token = "PFC"
        trade_fee_schema = TradeFeeSchema(
            percent_fee_token=fc_token,
            maker_percent_fee_decimal=Decimal("0.01"),
            taker_percent_fee_decimal=Decimal("0.01"),
            maker_fixed_fees=[TokenAmount(fc_token, Decimal("1"))])
        exchange = MockPaperExchange(client_config_map=ClientConfigAdapter(
            ClientConfigMap()),
                                     trade_fee_schema=trade_fee_schema)
        pfc_quote_pair = combine_to_hb_trading_pair(self.quote_asset, fc_token)
        exchange.set_balanced_order_book(  # the quote to pfc price will be 1:2
            trading_pair=pfc_quote_pair,
            mid_price=1.5,
            min_price=1,
            max_price=2,
            price_step_size=1,
            volume_step_size=1,
        )
        budget_checker: BudgetChecker = exchange.budget_checker
        exchange.set_balance(self.quote_asset, Decimal("20"))
        exchange.set_balance(
            fc_token, Decimal("1.2")
        )  # 0.2 less than required; will result in 50% order reduction

        order_candidate = OrderCandidate(
            trading_pair=self.trading_pair,
            is_maker=True,
            order_type=OrderType.LIMIT,
            order_side=TradeType.BUY,
            amount=Decimal("10"),
            price=Decimal("2"),
        )
        adjusted_candidate = budget_checker.adjust_candidate(order_candidate,
                                                             all_or_none=False)

        self.assertEqual(Decimal("5"), adjusted_candidate.amount)
        self.assertEqual(self.quote_asset,
                         adjusted_candidate.order_collateral.token)
        self.assertEqual(Decimal("10"),
                         adjusted_candidate.order_collateral.amount)
        self.assertEqual(fc_token,
                         adjusted_candidate.percent_fee_collateral.token)
        self.assertEqual(Decimal("0.2"),
                         adjusted_candidate.percent_fee_collateral.amount)
        self.assertEqual(fc_token, adjusted_candidate.percent_fee_value.token)
        self.assertEqual(Decimal("0.2"),
                         adjusted_candidate.percent_fee_value.amount)
        self.assertEqual(1, len(adjusted_candidate.fixed_fee_collaterals))

        fixed_fee_collateral = adjusted_candidate.fixed_fee_collaterals[0]

        self.assertEqual(fc_token, fixed_fee_collateral.token)
        self.assertEqual(Decimal("1"), fixed_fee_collateral.amount)
        self.assertEqual(self.base_asset,
                         adjusted_candidate.potential_returns.token)
        self.assertEqual(Decimal("5"),
                         adjusted_candidate.potential_returns.amount)
Beispiel #10
0
 def _initialize_trading_pair_symbols_from_exchange_info(
         self, exchange_info: Dict[str, Any]):
     mapping = bidict()
     for symbol_data in filter(okx_utils.is_exchange_information_valid,
                               exchange_info["data"]):
         mapping[symbol_data["instId"]] = combine_to_hb_trading_pair(
             base=symbol_data["baseCcy"], quote=symbol_data["quoteCcy"])
     self._set_trading_pair_symbol_map(mapping)
Beispiel #11
0
 def _initialize_trading_pair_symbols_from_exchange_info(
         self, exchange_info: Dict[str, Any]):
     mapping = bidict()
     for symbol_data in filter(utils.is_pair_information_valid,
                               exchange_info.get("data", [])):
         mapping[symbol_data["symbol"]] = combine_to_hb_trading_pair(
             base=symbol_data["baseCurrency"],
             quote=symbol_data["quoteCurrency"])
     self._set_trading_pair_symbol_map(mapping)
    def test_populate_collateral_fields_percent_fees_in_third_token(self):
        pfc_token = "PFC"
        trade_fee_schema = TradeFeeSchema(
            percent_fee_token=pfc_token,
            maker_percent_fee_decimal=Decimal("0.01"),
            taker_percent_fee_decimal=Decimal("0.01"),
        )
        exchange = MockPaperExchange(client_config_map=ClientConfigAdapter(
            ClientConfigMap()),
                                     trade_fee_schema=trade_fee_schema)
        pfc_quote_pair = combine_to_hb_trading_pair(self.quote_asset,
                                                    pfc_token)
        exchange.set_balanced_order_book(  # the quote to pfc price will be 1:2
            trading_pair=pfc_quote_pair,
            mid_price=1.5,
            min_price=1,
            max_price=2,
            price_step_size=1,
            volume_step_size=1,
        )
        budget_checker: BudgetChecker = exchange.budget_checker

        order_candidate = OrderCandidate(
            trading_pair=self.trading_pair,
            is_maker=True,
            order_type=OrderType.LIMIT,
            order_side=TradeType.BUY,
            amount=Decimal("10"),
            price=Decimal("2"),
        )
        populated_candidate = budget_checker.populate_collateral_entries(
            order_candidate)

        self.assertEqual(self.quote_asset,
                         populated_candidate.order_collateral.token)
        self.assertEqual(Decimal("20"),
                         populated_candidate.order_collateral.amount)
        self.assertEqual(pfc_token,
                         populated_candidate.percent_fee_collateral.token)
        self.assertEqual(Decimal("0.4"),
                         populated_candidate.percent_fee_collateral.amount)
        self.assertEqual(pfc_token,
                         populated_candidate.percent_fee_value.token)
        self.assertEqual(Decimal("0.4"),
                         populated_candidate.percent_fee_value.amount)
        self.assertEqual(0, len(populated_candidate.fixed_fee_collaterals))
        self.assertEqual(self.base_asset,
                         populated_candidate.potential_returns.token)
        self.assertEqual(Decimal("10"),
                         populated_candidate.potential_returns.amount)
Beispiel #13
0
    def _initialize_trading_pair_symbols_from_exchange_info(
            self, exchange_info: Dict[str, Any]):
        mapping = bidict()

        if not exchange_info:
            return

        for ticker in exchange_info:
            exchange_trading_pair = f"{ticker['baseCurrency']}/{ticker['quoteCurrency']}"
            if 'symbol' not in ticker:
                continue  # don't update with trading_rules data, check format_trading_rules for workaround
            base, quote = ticker["symbol"].split('/')
            mapping[exchange_trading_pair] = combine_to_hb_trading_pair(
                base=base, quote=quote)

        if mapping:
            self._set_trading_pair_symbol_map(mapping)
Beispiel #14
0
 def fee_amount_in_quote(
     self, trading_pair: str, price: Decimal, order_amount: Decimal, exchange: Optional['ExchangeBase'] = None
 ) -> Decimal:
     fee_amount = Decimal("0")
     if self.percent > 0:
         fee_amount = (price * order_amount) * self.percent
     base, quote = split_hb_trading_pair(trading_pair)
     for flat_fee in self.flat_fees:
         if interchangeable(flat_fee.token, base):
             fee_amount += (flat_fee.amount * price)
         elif interchangeable(flat_fee.token, quote):
             fee_amount += flat_fee.amount
         else:
             conversion_pair = combine_to_hb_trading_pair(base=flat_fee.token, quote=quote)
             conversion_rate = self._get_exchange_rate(conversion_pair, exchange)
             fee_amount = (flat_fee.amount * conversion_rate)
     return fee_amount
    def _get_size_collateral_price(
        self, exchange: 'ExchangeBase', order_collateral_token: str
    ) -> Decimal:
        size_token, _ = self.get_size_token_and_order_size()
        base, quote = split_hb_trading_pair(self.trading_pair)

        if order_collateral_token == size_token:
            price = Decimal("1")
        elif order_collateral_token == base:  # size_token == quote
            price = Decimal("1") / self.price
        elif order_collateral_token == quote:  # size_token == base
            price = self.price
        else:
            size_collateral_pair = combine_to_hb_trading_pair(size_token, order_collateral_token)
            price = exchange.get_price(size_collateral_pair, is_buy=True)  # we are buying

        return price
Beispiel #16
0
 def _get_fee(self,
              base_currency: str,
              quote_currency: str,
              order_type: OrderType,
              order_side: TradeType,
              amount: Decimal,
              price: Decimal = s_decimal_NaN,
              is_maker: Optional[bool] = None) -> TradeFeeBase:
     """
     Calculates the estimated fee an order would pay based on the connector configuration
     :param base_currency: the order base currency
     :param quote_currency: the order quote currency
     :param order_type: the type of order (MARKET, LIMIT, LIMIT_MAKER)
     :param order_side: if the order is for buying or selling
     :param amount: the order amount
     :param price: the order price
     :param is_maker: if we take into account maker fee (True) or taker fee (None, False)
     :return: the estimated fee for the order
     """
     trading_pair = combine_to_hb_trading_pair(base=base_currency,
                                               quote=quote_currency)
     fee_schema = self._trading_fees.get(trading_pair, None)
     if fee_schema is None:
         self.logger().warning(
             f"For trading pair = {trading_pair} there is no fee schema loaded, using presets!"
         )
         fee = build_trade_fee(exchange=self.name,
                               is_maker=is_maker,
                               base_currency=base_currency,
                               quote_currency=quote_currency,
                               order_type=order_type,
                               order_side=order_side,
                               amount=amount,
                               price=price)
     else:
         if fee_schema.type == LatokenTakeType.PROPORTION or fee_schema.take == LatokenCommissionType.PERCENT:
             pass  # currently not implemented but is nice to have in next release(s)
         percent = fee_schema.maker_fee if order_type is OrderType.LIMIT_MAKER or (
             is_maker is not None and is_maker) else fee_schema.taker_fee
         fee = AddedToCostTradeFee(
             percent=percent
         ) if order_side == TradeType.BUY else DeductedFromReturnsTradeFee(
             percent=percent)
     return fee
Beispiel #17
0
    async def _calculate_fees(self, quote: str, trades: List[Any]):
        for trade in trades:
            fee_percent = None
            trade_price = None
            trade_amount = None
            if self._is_trade_fill(trade):
                if trade.trade_fee.get("percent") is not None and Decimal(
                        trade.trade_fee["percent"]) > 0:
                    trade_price = Decimal(str(trade.price))
                    trade_amount = Decimal(str(trade.amount))
                    fee_percent = Decimal(str(trade.trade_fee["percent"]))
                flat_fees = [
                    TokenAmount(token=flat_fee["token"],
                                amount=Decimal(flat_fee["amount"]))
                    for flat_fee in trade.trade_fee.get("flat_fees", [])
                ]
            else:  # assume this is Trade object
                if trade.trade_fee.percent is not None and trade.trade_fee.percent > 0:
                    trade_price = Decimal(trade.price)
                    trade_amount = Decimal(trade.amount)
                    fee_percent = Decimal(trade.trade_fee.percent)
                flat_fees = trade.trade_fee.flat_fees

            if fee_percent is not None and fee_percent > 0:
                self.fees[quote] += trade_price * trade_amount * fee_percent
            for flat_fee in flat_fees:
                self.fees[flat_fee.token] += flat_fee.amount

        for fee_token, fee_amount in self.fees.items():
            if fee_token == quote:
                self.fee_in_quote += fee_amount
            else:
                rate_pair: str = combine_to_hb_trading_pair(fee_token, quote)
                last_price = await RateOracle.get_instance(
                ).stored_or_live_rate(rate_pair)
                if last_price is not None:
                    self.fee_in_quote += fee_amount * last_price
                else:
                    self.logger().warning(
                        f"Could not find exchange rate for {rate_pair} "
                        f"using {RateOracle.get_instance()}. PNL value will be inconsistent."
                    )
Beispiel #18
0
    def get_fee(self,
                base_currency: str,
                quote_currency: str,
                order_type: OrderType,
                order_side: TradeType,
                amount: Decimal,
                price: Decimal = s_decimal_NaN,
                is_maker: Optional[bool] = None) -> AddedToCostTradeFee:
        """
        Calculates the fee to pay based on the fee information provided by the exchange for the account and the token pair.
        If exchange info is not available it calculates the estimated fee an order would pay based on the connector
            configuration

        :param base_currency: the order base currency
        :param quote_currency: the order quote currency
        :param order_type: the type of order (MARKET, LIMIT, LIMIT_MAKER)
        :param order_side: if the order is for buying or selling
        :param amount: the order amount
        :param price: the order price
        :param is_maker: True if the order is a maker order, False if it is a taker order

        :return: the calculated or estimated fee
        """

        is_maker = is_maker or (order_type is OrderType.LIMIT_MAKER)
        trading_pair = combine_to_hb_trading_pair(base=base_currency, quote=quote_currency)
        if trading_pair in self._trading_fees:
            fees_data = self._trading_fees[trading_pair]
            fee_value = Decimal(fees_data["makerFeeRate"]) if is_maker else Decimal(fees_data["takerFeeRate"])
            fee = AddedToCostTradeFee(percent=fee_value)
        else:
            fee = build_trade_fee(
                self.name,
                is_maker,
                base_currency=base_currency,
                quote_currency=quote_currency,
                order_type=order_type,
                order_side=order_side,
                amount=amount,
                price=price,
            )
        return fee
    async def _format_trading_rules(
            self, exchange_info_list: List[Dict[str,
                                                Any]]) -> List[TradingRule]:
        """
        Queries the necessary API endpoint and initialize the TradingRule object for each trading pair being traded.

        Parameters
        ----------
        exchange_info_dict:
            Trading rules dictionary response from the exchange
        """
        return_val: list = []
        for rule in exchange_info_list:
            try:
                trading_pair = combine_to_hb_trading_pair(
                    rule["rootSymbol"], rule["quoteCurrency"])
                if trading_pair in self._trading_pairs:
                    trading_pair_multipliers = await utils.get_trading_pair_multipliers(
                        rule['symbol'])
                    self._trading_pair_to_multipliers[
                        trading_pair] = trading_pair_multipliers
                    max_order_size = Decimal(str(rule.get("maxOrderQty")))

                    min_order_size = Decimal(str(rule.get(
                        "lotSize"))) / trading_pair_multipliers.base_multiplier

                    tick_size = Decimal(str(rule.get("tickSize")))
                    return_val.append(
                        TradingRule(
                            trading_pair,
                            min_order_size=min_order_size,
                            min_price_increment=Decimal(tick_size),
                            min_base_amount_increment=Decimal(min_order_size),
                            max_order_size=max_order_size,
                        ))

            except Exception as e:
                self.logger().error(
                    f"Error parsing the trading pair rule {rule}. Error: {e}. Skipping...",
                    exc_info=True)
        return return_val
Beispiel #20
0
    async def init_trading_pair_symbols(
        cls,
        domain: str = CONSTANTS.DOMAIN,
        throttler: Optional[AsyncThrottler] = None,
        api_factory: WebAssistantsFactory = None
    ):
        """Initialize _trading_pair_symbol_map class variable"""
        mapping = bidict()

        api_factory = api_factory or utils.build_api_factory()
        rest_assistant = await api_factory.get_rest_assistant()

        url = utils.rest_url(path_url=CONSTANTS.EXCHANGE_INFO_URL, domain=domain)
        throttler = throttler or cls._get_throttler_instance()

        try:
            async with throttler.execute_task(limit_id=CONSTANTS.EXCHANGE_INFO_URL):
                request = RESTRequest(
                    method=RESTMethod.GET,
                    url=url,
                )
                response = await rest_assistant.call(request=request, timeout=10)

                if response.status == 200:
                    data = await response.json()
                    # fetch d["pair"] for binance perpetual
                    for symbol_data in filter(utils.is_exchange_information_valid, data["symbols"]):
                        try:
                            mapping[symbol_data["pair"]] = combine_to_hb_trading_pair(
                                symbol_data["baseAsset"],
                                symbol_data["quoteAsset"])
                        except ValueDuplicationError:
                            continue
        except Exception as ex:
            cls.logger().error(f"There was an error requesting exchange info ({str(ex)})")

        cls._trading_pair_symbol_map[domain] = mapping