Merge branch 'freqtrade:feat/short' into feat/short
This commit is contained in:
		| @@ -206,7 +206,9 @@ class HDF5DataHandler(IDataHandler): | |||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _pair_ohlcv_key(cls, pair: str, timeframe: str) -> str: |     def _pair_ohlcv_key(cls, pair: str, timeframe: str) -> str: | ||||||
|         return f"{pair}/ohlcv/tf_{timeframe}" |         # Escape futures pairs to avoid warnings | ||||||
|  |         pair_esc = pair.replace(':', '_') | ||||||
|  |         return f"{pair_esc}/ohlcv/tf_{timeframe}" | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _pair_trades_key(cls, pair: str) -> str: |     def _pair_trades_key(cls, pair: str) -> str: | ||||||
|   | |||||||
| @@ -75,6 +75,7 @@ class Exchange: | |||||||
|         "mark_ohlcv_price": "mark", |         "mark_ohlcv_price": "mark", | ||||||
|         "mark_ohlcv_timeframe": "8h", |         "mark_ohlcv_timeframe": "8h", | ||||||
|         "ccxt_futures_name": "swap", |         "ccxt_futures_name": "swap", | ||||||
|  |         "needs_trading_fees": False,  # use fetch_trading_fees to cache fees | ||||||
|     } |     } | ||||||
|     _ft_has: Dict = {} |     _ft_has: Dict = {} | ||||||
|     _ft_has_futures: Dict = {} |     _ft_has_futures: Dict = {} | ||||||
| @@ -92,6 +93,7 @@ class Exchange: | |||||||
|         self._api: ccxt.Exchange = None |         self._api: ccxt.Exchange = None | ||||||
|         self._api_async: ccxt_async.Exchange = None |         self._api_async: ccxt_async.Exchange = None | ||||||
|         self._markets: Dict = {} |         self._markets: Dict = {} | ||||||
|  |         self._trading_fees: Dict[str, Any] = {} | ||||||
|         self._leverage_tiers: Dict[str, List[Dict]] = {} |         self._leverage_tiers: Dict[str, List[Dict]] = {} | ||||||
|         self.loop = asyncio.new_event_loop() |         self.loop = asyncio.new_event_loop() | ||||||
|         asyncio.set_event_loop(self.loop) |         asyncio.set_event_loop(self.loop) | ||||||
| @@ -451,6 +453,9 @@ class Exchange: | |||||||
|             self._markets = self._api.load_markets() |             self._markets = self._api.load_markets() | ||||||
|             self._load_async_markets() |             self._load_async_markets() | ||||||
|             self._last_markets_refresh = arrow.utcnow().int_timestamp |             self._last_markets_refresh = arrow.utcnow().int_timestamp | ||||||
|  |             if self._ft_has['needs_trading_fees']: | ||||||
|  |                 self._trading_fees = self.fetch_trading_fees() | ||||||
|  |  | ||||||
|         except ccxt.BaseError: |         except ccxt.BaseError: | ||||||
|             logger.exception('Unable to initialize markets.') |             logger.exception('Unable to initialize markets.') | ||||||
|  |  | ||||||
| @@ -1299,6 +1304,27 @@ class Exchange: | |||||||
|         except ccxt.BaseError as e: |         except ccxt.BaseError as e: | ||||||
|             raise OperationalException(e) from e |             raise OperationalException(e) from e | ||||||
|  |  | ||||||
|  |     @retrier | ||||||
|  |     def fetch_trading_fees(self) -> Dict[str, Any]: | ||||||
|  |         """ | ||||||
|  |         Fetch user account trading fees | ||||||
|  |         Can be cached, should not update often. | ||||||
|  |         """ | ||||||
|  |         if (self._config['dry_run'] or self.trading_mode != TradingMode.FUTURES | ||||||
|  |                 or not self.exchange_has('fetchTradingFees')): | ||||||
|  |             return {} | ||||||
|  |         try: | ||||||
|  |             trading_fees: Dict[str, Any] = self._api.fetch_trading_fees() | ||||||
|  |             self._log_exchange_response('fetch_trading_fees', trading_fees) | ||||||
|  |             return trading_fees | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|  |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|  |             raise TemporaryError( | ||||||
|  |                 f'Could not fetch trading fees due to {e.__class__.__name__}. Message: {e}') from e | ||||||
|  |         except ccxt.BaseError as e: | ||||||
|  |             raise OperationalException(e) from e | ||||||
|  |  | ||||||
|     @retrier |     @retrier | ||||||
|     def fetch_bids_asks(self, symbols: List[str] = None, cached: bool = False) -> Dict: |     def fetch_bids_asks(self, symbols: List[str] = None, cached: bool = False) -> Dict: | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| """ Gate.io exchange subclass """ | """ Gate.io exchange subclass """ | ||||||
| import logging | import logging | ||||||
| from typing import Dict, List, Tuple | from datetime import datetime | ||||||
|  | from typing import Dict, List, Optional, Tuple | ||||||
|  |  | ||||||
| from freqtrade.enums import MarginMode, TradingMode | from freqtrade.enums import MarginMode, TradingMode | ||||||
| from freqtrade.exceptions import OperationalException | from freqtrade.exceptions import OperationalException | ||||||
| @@ -27,6 +28,10 @@ class Gateio(Exchange): | |||||||
|         "stoploss_on_exchange": True, |         "stoploss_on_exchange": True, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     _ft_has_futures: Dict = { | ||||||
|  |         "needs_trading_fees": True | ||||||
|  |     } | ||||||
|  |  | ||||||
|     _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ |     _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ | ||||||
|         # TradingMode.SPOT always supported and not required in this list |         # TradingMode.SPOT always supported and not required in this list | ||||||
|         # (TradingMode.MARGIN, MarginMode.CROSS), |         # (TradingMode.MARGIN, MarginMode.CROSS), | ||||||
| @@ -42,6 +47,30 @@ class Gateio(Exchange): | |||||||
|                 raise OperationalException( |                 raise OperationalException( | ||||||
|                     f'Exchange {self.name} does not support market orders.') |                     f'Exchange {self.name} does not support market orders.') | ||||||
|  |  | ||||||
|  |     def get_trades_for_order(self, order_id: str, pair: str, since: datetime, | ||||||
|  |                              params: Optional[Dict] = None) -> List: | ||||||
|  |         trades = super().get_trades_for_order(order_id, pair, since, params) | ||||||
|  |  | ||||||
|  |         if self.trading_mode == TradingMode.FUTURES: | ||||||
|  |             # Futures usually don't contain fees in the response. | ||||||
|  |             # As such, futures orders on gateio will not contain a fee, which causes | ||||||
|  |             # a repeated "update fee" cycle and wrong calculations. | ||||||
|  |             # Therefore we patch the response with fees if it's not available. | ||||||
|  |             # An alternative also contianing fees would be | ||||||
|  |             # privateFuturesGetSettleAccountBook({"settle": "usdt"}) | ||||||
|  |             pair_fees = self._trading_fees.get(pair, {}) | ||||||
|  |             if pair_fees: | ||||||
|  |                 for idx, trade in enumerate(trades): | ||||||
|  |                     if trade.get('fee', {}).get('cost') is None: | ||||||
|  |                         takerOrMaker = trade.get('takerOrMaker', 'taker') | ||||||
|  |                         if pair_fees.get(takerOrMaker) is not None: | ||||||
|  |                             trades[idx]['fee'] = { | ||||||
|  |                                 'currency': self.get_pair_quote_currency(pair), | ||||||
|  |                                 'cost': trade['cost'] * pair_fees[takerOrMaker], | ||||||
|  |                                 'rate': pair_fees[takerOrMaker], | ||||||
|  |                             } | ||||||
|  |         return trades | ||||||
|  |  | ||||||
|     def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: |     def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: | ||||||
|         return self.fetch_order( |         return self.fetch_order( | ||||||
|             order_id=order_id, |             order_id=order_id, | ||||||
|   | |||||||
| @@ -511,7 +511,7 @@ class FreqtradeBot(LoggingMixin): | |||||||
|                 return |                 return | ||||||
|         else: |         else: | ||||||
|             logger.debug("Max adjustment entries is set to unlimited.") |             logger.debug("Max adjustment entries is set to unlimited.") | ||||||
|         current_rate = self.exchange.get_rate(trade.pair, refresh=True, side="buy") |         current_rate = self.exchange.get_rate(trade.pair, refresh=True, side=trade.enter_side) | ||||||
|         current_profit = trade.calc_profit_ratio(current_rate) |         current_profit = trade.calc_profit_ratio(current_rate) | ||||||
|  |  | ||||||
|         min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair, |         min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair, | ||||||
| @@ -536,12 +536,7 @@ class FreqtradeBot(LoggingMixin): | |||||||
|             logger.error(f"Unable to decrease trade position / sell partially" |             logger.error(f"Unable to decrease trade position / sell partially" | ||||||
|                          f" for pair {trade.pair}, feature not implemented.") |                          f" for pair {trade.pair}, feature not implemented.") | ||||||
|  |  | ||||||
|     def _check_depth_of_market( |     def _check_depth_of_market(self, pair: str, conf: Dict, side: SignalDirection) -> bool: | ||||||
|         self, |  | ||||||
|         pair: str, |  | ||||||
|         conf: Dict, |  | ||||||
|         side: SignalDirection |  | ||||||
|     ) -> bool: |  | ||||||
|         """ |         """ | ||||||
|         Checks depth of market before executing a buy |         Checks depth of market before executing a buy | ||||||
|         """ |         """ | ||||||
| @@ -1564,6 +1559,7 @@ class FreqtradeBot(LoggingMixin): | |||||||
|         if not order_obj: |         if not order_obj: | ||||||
|             raise DependencyException( |             raise DependencyException( | ||||||
|                 f"Order_obj not found for {order_id}. This should not have happened.") |                 f"Order_obj not found for {order_id}. This should not have happened.") | ||||||
|  |  | ||||||
|         self.handle_order_fee(trade, order_obj, order) |         self.handle_order_fee(trade, order_obj, order) | ||||||
|  |  | ||||||
|         trade.update_trade(order_obj) |         trade.update_trade(order_obj) | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ numpy==1.22.3 | |||||||
| pandas==1.4.1 | pandas==1.4.1 | ||||||
| pandas-ta==0.3.14b | pandas-ta==0.3.14b | ||||||
|  |  | ||||||
| ccxt==1.76.65 | ccxt==1.77.29 | ||||||
| # Pin cryptography for now due to rust build errors with piwheels | # Pin cryptography for now due to rust build errors with piwheels | ||||||
| cryptography==36.0.2 | cryptography==36.0.2 | ||||||
| aiohttp==3.8.1 | aiohttp==3.8.1 | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							| @@ -42,7 +42,7 @@ setup( | |||||||
|     ], |     ], | ||||||
|     install_requires=[ |     install_requires=[ | ||||||
|         # from requirements.txt |         # from requirements.txt | ||||||
|         'ccxt>=1.76.5', |         'ccxt>=1.77.29', | ||||||
|         'SQLAlchemy', |         'SQLAlchemy', | ||||||
|         'python-telegram-bot>=13.4', |         'python-telegram-bot>=13.4', | ||||||
|         'arrow>=0.17.0', |         'arrow>=0.17.0', | ||||||
|   | |||||||
| @@ -683,7 +683,7 @@ def test_datahandler_ohlcv_get_pairs(testdatadir): | |||||||
|     assert set(pairs) == {'XRP/USDT'} |     assert set(pairs) == {'XRP/USDT'} | ||||||
|  |  | ||||||
|     pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.MARK) |     pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.MARK) | ||||||
|     assert set(pairs) == {'UNITTEST/USDT'} |     assert set(pairs) == {'UNITTEST/USDT:USDT'} | ||||||
|  |  | ||||||
|  |  | ||||||
| @pytest.mark.parametrize('filename,pair,timeframe,candletype', [ | @pytest.mark.parametrize('filename,pair,timeframe,candletype', [ | ||||||
| @@ -914,7 +914,7 @@ def test_hdf5datahandler_trades_purge(mocker, testdatadir): | |||||||
|     # Data goes from 2018-01-10 - 2018-01-30 |     # Data goes from 2018-01-10 - 2018-01-30 | ||||||
|     ('UNITTEST/BTC', '5m', 'spot',  '', '2018-01-15', '2018-01-19'), |     ('UNITTEST/BTC', '5m', 'spot',  '', '2018-01-15', '2018-01-19'), | ||||||
|     # Mark data goes from to 2021-11-15 2021-11-19 |     # Mark data goes from to 2021-11-15 2021-11-19 | ||||||
|     ('UNITTEST/USDT', '1h', 'mark', '-mark', '2021-11-16', '2021-11-18'), |     ('UNITTEST/USDT:USDT', '1h', 'mark', '-mark', '2021-11-16', '2021-11-18'), | ||||||
| ]) | ]) | ||||||
| def test_hdf5datahandler_ohlcv_load_and_resave( | def test_hdf5datahandler_ohlcv_load_and_resave( | ||||||
|     testdatadir, |     testdatadir, | ||||||
|   | |||||||
| @@ -134,7 +134,7 @@ def exchange_futures(request, exchange_conf, class_mocker): | |||||||
|  |  | ||||||
|         class_mocker.patch( |         class_mocker.patch( | ||||||
|             'freqtrade.exchange.binance.Binance.fill_leverage_tiers') |             'freqtrade.exchange.binance.Binance.fill_leverage_tiers') | ||||||
|  |         class_mocker.patch('freqtrade.exchange.exchange.Exchange.fetch_trading_fees') | ||||||
|         exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True) |         exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True) | ||||||
|  |  | ||||||
|         yield exchange, request.param |         yield exchange, request.param | ||||||
|   | |||||||
| @@ -1624,6 +1624,62 @@ def test_fetch_positions(default_conf, mocker, exchange_name): | |||||||
|                            "fetch_positions", "fetch_positions") |                            "fetch_positions", "fetch_positions") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_fetch_trading_fees(default_conf, mocker): | ||||||
|  |     api_mock = MagicMock() | ||||||
|  |     tick = { | ||||||
|  |         '1INCH/USDT:USDT': { | ||||||
|  |             'info': {'user_id': '', | ||||||
|  |                      'taker_fee': '0.0018', | ||||||
|  |                      'maker_fee': '0.0018', | ||||||
|  |                      'gt_discount': False, | ||||||
|  |                      'gt_taker_fee': '0', | ||||||
|  |                      'gt_maker_fee': '0', | ||||||
|  |                      'loan_fee': '0.18', | ||||||
|  |                      'point_type': '1', | ||||||
|  |                      'futures_taker_fee': '0.0005', | ||||||
|  |                      'futures_maker_fee': '0'}, | ||||||
|  |             'symbol': '1INCH/USDT:USDT', | ||||||
|  |             'maker': 0.0, | ||||||
|  |             'taker': 0.0005}, | ||||||
|  |         'ETH/USDT:USDT': { | ||||||
|  |             'info': {'user_id': '', | ||||||
|  |                      'taker_fee': '0.0018', | ||||||
|  |                      'maker_fee': '0.0018', | ||||||
|  |                      'gt_discount': False, | ||||||
|  |                      'gt_taker_fee': '0', | ||||||
|  |                      'gt_maker_fee': '0', | ||||||
|  |                      'loan_fee': '0.18', | ||||||
|  |                      'point_type': '1', | ||||||
|  |                      'futures_taker_fee': '0.0005', | ||||||
|  |                      'futures_maker_fee': '0'}, | ||||||
|  |             'symbol': 'ETH/USDT:USDT', | ||||||
|  |             'maker': 0.0, | ||||||
|  |             'taker': 0.0005} | ||||||
|  |     } | ||||||
|  |     exchange_name = 'gateio' | ||||||
|  |     default_conf['dry_run'] = False | ||||||
|  |     default_conf['trading_mode'] = TradingMode.FUTURES | ||||||
|  |     default_conf['margin_mode'] = MarginMode.ISOLATED | ||||||
|  |     api_mock.fetch_trading_fees = MagicMock(return_value=tick) | ||||||
|  |     mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) | ||||||
|  |     exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) | ||||||
|  |  | ||||||
|  |     assert '1INCH/USDT:USDT' in exchange._trading_fees | ||||||
|  |     assert 'ETH/USDT:USDT' in exchange._trading_fees | ||||||
|  |     assert api_mock.fetch_trading_fees.call_count == 1 | ||||||
|  |  | ||||||
|  |     api_mock.fetch_trading_fees.reset_mock() | ||||||
|  |  | ||||||
|  |     ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, | ||||||
|  |                            "fetch_trading_fees", "fetch_trading_fees") | ||||||
|  |  | ||||||
|  |     api_mock.fetch_trading_fees = MagicMock(return_value={}) | ||||||
|  |     exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) | ||||||
|  |     exchange.fetch_trading_fees() | ||||||
|  |     mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) | ||||||
|  |     assert exchange.fetch_trading_fees() == {} | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_fetch_bids_asks(default_conf, mocker): | def test_fetch_bids_asks(default_conf, mocker): | ||||||
|     api_mock = MagicMock() |     api_mock = MagicMock() | ||||||
|     tick = {'ETH/BTC': { |     tick = {'ETH/BTC': { | ||||||
|   | |||||||
| @@ -1,7 +1,9 @@ | |||||||
|  | from datetime import datetime, timezone | ||||||
| from unittest.mock import MagicMock | from unittest.mock import MagicMock | ||||||
|  |  | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
|  | from freqtrade.enums import MarginMode, TradingMode | ||||||
| from freqtrade.exceptions import OperationalException | from freqtrade.exceptions import OperationalException | ||||||
| from freqtrade.exchange import Gateio | from freqtrade.exchange import Gateio | ||||||
| from freqtrade.resolvers.exchange_resolver import ExchangeResolver | from freqtrade.resolvers.exchange_resolver import ExchangeResolver | ||||||
| @@ -70,3 +72,47 @@ def test_stoploss_adjust_gateio(mocker, default_conf, sl1, sl2, sl3, side): | |||||||
|     } |     } | ||||||
|     assert exchange.stoploss_adjust(sl1, order, side) |     assert exchange.stoploss_adjust(sl1, order, side) | ||||||
|     assert not exchange.stoploss_adjust(sl2, order, side) |     assert not exchange.stoploss_adjust(sl2, order, side) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pytest.mark.parametrize('takerormaker,rate,cost', [ | ||||||
|  |     ('taker', 0.0005, 0.0001554325), | ||||||
|  |     ('maker', 0.0, 0.0), | ||||||
|  | ]) | ||||||
|  | def test_fetch_my_trades_gateio(mocker, default_conf, takerormaker, rate, cost): | ||||||
|  |     mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) | ||||||
|  |     tick = {'ETH/USDT:USDT': { | ||||||
|  |         'info': {'user_id': '', | ||||||
|  |                  'taker_fee': '0.0018', | ||||||
|  |                  'maker_fee': '0.0018', | ||||||
|  |                  'gt_discount': False, | ||||||
|  |                  'gt_taker_fee': '0', | ||||||
|  |                  'gt_maker_fee': '0', | ||||||
|  |                  'loan_fee': '0.18', | ||||||
|  |                  'point_type': '1', | ||||||
|  |                  'futures_taker_fee': '0.0005', | ||||||
|  |                  'futures_maker_fee': '0'}, | ||||||
|  |             'symbol': 'ETH/USDT:USDT', | ||||||
|  |             'maker': 0.0, | ||||||
|  |             'taker': 0.0005} | ||||||
|  |             } | ||||||
|  |     default_conf['dry_run'] = False | ||||||
|  |     default_conf['trading_mode'] = TradingMode.FUTURES | ||||||
|  |     default_conf['margin_mode'] = MarginMode.ISOLATED | ||||||
|  |  | ||||||
|  |     api_mock = MagicMock() | ||||||
|  |     api_mock.fetch_my_trades = MagicMock(return_value=[{ | ||||||
|  |         'fee': {'cost': None}, | ||||||
|  |         'price': 3108.65, | ||||||
|  |         'cost': 0.310865, | ||||||
|  |         'order': '22255', | ||||||
|  |         'takerOrMaker': takerormaker, | ||||||
|  |         'amount': 1,  # 1 contract | ||||||
|  |     }]) | ||||||
|  |     exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, id='gateio') | ||||||
|  |     exchange._trading_fees = tick | ||||||
|  |     trades = exchange.get_trades_for_order('22255', 'ETH/USDT:USDT', datetime.now(timezone.utc)) | ||||||
|  |     trade = trades[0] | ||||||
|  |     assert trade['fee'] | ||||||
|  |     assert trade['fee']['rate'] == rate | ||||||
|  |     assert trade['fee']['currency'] == 'USDT' | ||||||
|  |     assert trade['fee']['cost'] == cost | ||||||
|   | |||||||
| @@ -311,8 +311,7 @@ def test_dca_short(default_conf_usdt, ticker_usdt, fee, mocker) -> None: | |||||||
|  |  | ||||||
|     # Reduce bid amount |     # Reduce bid amount | ||||||
|     ticker_usdt_modif = ticker_usdt.return_value |     ticker_usdt_modif = ticker_usdt.return_value | ||||||
|     ticker_usdt_modif['ask'] = ticker_usdt_modif['ask'] * 1.015 |     ticker_usdt_modif['ask'] = ticker_usdt_modif['ask'] * 1.004 | ||||||
|     ticker_usdt_modif['bid'] = ticker_usdt_modif['bid'] * 1.0125 |  | ||||||
|     mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value=ticker_usdt_modif) |     mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value=ticker_usdt_modif) | ||||||
|  |  | ||||||
|     # additional buy order |     # additional buy order | ||||||
|   | |||||||
| @@ -73,6 +73,8 @@ def test_file_load_json(mocker, testdatadir) -> None: | |||||||
|     ("ETH/BTC", 'ETH_BTC'), |     ("ETH/BTC", 'ETH_BTC'), | ||||||
|     ("ETH/USDT", 'ETH_USDT'), |     ("ETH/USDT", 'ETH_USDT'), | ||||||
|     ("ETH/USDT:USDT", 'ETH_USDT_USDT'),  # swap with USDT as settlement currency |     ("ETH/USDT:USDT", 'ETH_USDT_USDT'),  # swap with USDT as settlement currency | ||||||
|  |     ("ETH/USD:USD", 'ETH_USD_USD'),  # swap with USD as settlement currency | ||||||
|  |     ("AAVE/USD:USD", 'AAVE_USD_USD'),  # swap with USDT as settlement currency | ||||||
|     ("ETH/USDT:USDT-210625", 'ETH_USDT_USDT-210625'),  # expiring futures |     ("ETH/USDT:USDT-210625", 'ETH_USDT_USDT-210625'),  # expiring futures | ||||||
|     ("Fabric Token/ETH", 'Fabric_Token_ETH'), |     ("Fabric Token/ETH", 'Fabric_Token_ETH'), | ||||||
|     ("ETHH20", 'ETHH20'), |     ("ETHH20", 'ETHH20'), | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user