diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 0948692f1..c107f7abc 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -1,4 +1,5 @@ -from freqtrade.exchange.exchange import Exchange, MAP_EXCHANGE_CHILDCLASS # noqa: F401 +from freqtrade.exchange.common import MAP_EXCHANGE_CHILDCLASS # noqa: F401 +from freqtrade.exchange.exchange import Exchange # noqa: F401 from freqtrade.exchange.exchange import (get_exchange_bad_reason, # noqa: F401 is_exchange_bad, is_exchange_known_ccxt, diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py new file mode 100644 index 000000000..ed30b95c7 --- /dev/null +++ b/freqtrade/exchange/common.py @@ -0,0 +1,124 @@ +import logging + +from freqtrade import DependencyException, TemporaryError + +logger = logging.getLogger(__name__) + + +API_RETRY_COUNT = 4 +BAD_EXCHANGES = { + "bitmex": "Various reasons.", + "bitstamp": "Does not provide history. " + "Details in https://github.com/freqtrade/freqtrade/issues/1983", + "hitbtc": "This API cannot be used with Freqtrade. " + "Use `hitbtc2` exchange id to access this exchange.", + **dict.fromkeys([ + 'adara', + 'anxpro', + 'bigone', + 'coinbase', + 'coinexchange', + 'coinmarketcap', + 'lykke', + 'xbtce', + ], "Does not provide timeframes. ccxt fetchOHLCV: False"), + **dict.fromkeys([ + 'bcex', + 'bit2c', + 'bitbay', + 'bitflyer', + 'bitforex', + 'bithumb', + 'bitso', + 'bitstamp1', + 'bl3p', + 'braziliex', + 'btcbox', + 'btcchina', + 'btctradeim', + 'btctradeua', + 'bxinth', + 'chilebit', + 'coincheck', + 'coinegg', + 'coinfalcon', + 'coinfloor', + 'coingi', + 'coinmate', + 'coinone', + 'coinspot', + 'coolcoin', + 'crypton', + 'deribit', + 'exmo', + 'exx', + 'flowbtc', + 'foxbit', + 'fybse', + # 'hitbtc', + 'ice3x', + 'independentreserve', + 'indodax', + 'itbit', + 'lakebtc', + 'latoken', + 'liquid', + 'livecoin', + 'luno', + 'mixcoins', + 'negociecoins', + 'nova', + 'paymium', + 'southxchange', + 'stronghold', + 'surbitcoin', + 'therock', + 'tidex', + 'vaultoro', + 'vbtc', + 'virwox', + 'yobit', + 'zaif', + ], "Does not provide timeframes. ccxt fetchOHLCV: emulated"), +} + +MAP_EXCHANGE_CHILDCLASS = { + 'binanceus': 'binance', + 'binanceje': 'binance', +} + + +def retrier_async(f): + async def wrapper(*args, **kwargs): + count = kwargs.pop('count', API_RETRY_COUNT) + try: + return await f(*args, **kwargs) + except (TemporaryError, DependencyException) as ex: + logger.warning('%s() returned exception: "%s"', f.__name__, ex) + if count > 0: + count -= 1 + kwargs.update({'count': count}) + logger.warning('retrying %s() still for %s times', f.__name__, count) + return await wrapper(*args, **kwargs) + else: + logger.warning('Giving up retrying: %s()', f.__name__) + raise ex + return wrapper + + +def retrier(f): + def wrapper(*args, **kwargs): + count = kwargs.pop('count', API_RETRY_COUNT) + try: + return f(*args, **kwargs) + except (TemporaryError, DependencyException) as ex: + logger.warning('%s() returned exception: "%s"', f.__name__, ex) + if count > 0: + count -= 1 + kwargs.update({'count': count}) + logger.warning('retrying %s() still for %s times', f.__name__, count) + return wrapper(*args, **kwargs) + else: + logger.warning('Giving up retrying: %s()', f.__name__) + raise ex + return wrapper diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 023e16cc5..430a2ff54 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -14,137 +14,18 @@ from typing import Any, Dict, List, Optional, Tuple import arrow import ccxt import ccxt.async_support as ccxt_async -from ccxt.base.decimal_to_precision import ROUND_UP, ROUND_DOWN +from ccxt.base.decimal_to_precision import ROUND_DOWN, ROUND_UP from pandas import DataFrame from freqtrade import (DependencyException, InvalidOrderException, OperationalException, TemporaryError, constants) from freqtrade.data.converter import parse_ticker_dataframe +from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async from freqtrade.misc import deep_merge_dicts - logger = logging.getLogger(__name__) -API_RETRY_COUNT = 4 -BAD_EXCHANGES = { - "bitmex": "Various reasons.", - "bitstamp": "Does not provide history. " - "Details in https://github.com/freqtrade/freqtrade/issues/1983", - "hitbtc": "This API cannot be used with Freqtrade. " - "Use `hitbtc2` exchange id to access this exchange.", - **dict.fromkeys([ - 'adara', - 'anxpro', - 'bigone', - 'coinbase', - 'coinexchange', - 'coinmarketcap', - 'lykke', - 'xbtce', - ], "Does not provide timeframes. ccxt fetchOHLCV: False"), - **dict.fromkeys([ - 'bcex', - 'bit2c', - 'bitbay', - 'bitflyer', - 'bitforex', - 'bithumb', - 'bitso', - 'bitstamp1', - 'bl3p', - 'braziliex', - 'btcbox', - 'btcchina', - 'btctradeim', - 'btctradeua', - 'bxinth', - 'chilebit', - 'coincheck', - 'coinegg', - 'coinfalcon', - 'coinfloor', - 'coingi', - 'coinmate', - 'coinone', - 'coinspot', - 'coolcoin', - 'crypton', - 'deribit', - 'exmo', - 'exx', - 'flowbtc', - 'foxbit', - 'fybse', - # 'hitbtc', - 'ice3x', - 'independentreserve', - 'indodax', - 'itbit', - 'lakebtc', - 'latoken', - 'liquid', - 'livecoin', - 'luno', - 'mixcoins', - 'negociecoins', - 'nova', - 'paymium', - 'southxchange', - 'stronghold', - 'surbitcoin', - 'therock', - 'tidex', - 'vaultoro', - 'vbtc', - 'virwox', - 'yobit', - 'zaif', - ], "Does not provide timeframes. ccxt fetchOHLCV: emulated"), - } - -MAP_EXCHANGE_CHILDCLASS = { - 'binanceus': 'binance', - 'binanceje': 'binance', -} - - -def retrier_async(f): - async def wrapper(*args, **kwargs): - count = kwargs.pop('count', API_RETRY_COUNT) - try: - return await f(*args, **kwargs) - except (TemporaryError, DependencyException) as ex: - logger.warning('%s() returned exception: "%s"', f.__name__, ex) - if count > 0: - count -= 1 - kwargs.update({'count': count}) - logger.warning('retrying %s() still for %s times', f.__name__, count) - return await wrapper(*args, **kwargs) - else: - logger.warning('Giving up retrying: %s()', f.__name__) - raise ex - return wrapper - - -def retrier(f): - def wrapper(*args, **kwargs): - count = kwargs.pop('count', API_RETRY_COUNT) - try: - return f(*args, **kwargs) - except (TemporaryError, DependencyException) as ex: - logger.warning('%s() returned exception: "%s"', f.__name__, ex) - if count > 0: - count -= 1 - kwargs.update({'count': count}) - logger.warning('retrying %s() still for %s times', f.__name__, count) - return wrapper(*args, **kwargs) - else: - logger.warning('Giving up retrying: %s()', f.__name__) - raise ex - return wrapper - - class Exchange: _config: Dict = {} diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 4eb0df1a3..8a4121d80 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -14,13 +14,13 @@ from pandas import DataFrame from freqtrade import (DependencyException, InvalidOrderException, OperationalException, TemporaryError) from freqtrade.exchange import Binance, Exchange, Kraken -from freqtrade.exchange.exchange import (API_RETRY_COUNT, timeframe_to_minutes, +from freqtrade.exchange.common import API_RETRY_COUNT +from freqtrade.exchange.exchange import (market_is_active, symbol_is_pair, + timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date, timeframe_to_prev_date, - timeframe_to_seconds, - symbol_is_pair, - market_is_active) + timeframe_to_seconds) from freqtrade.resolvers.exchange_resolver import ExchangeResolver from tests.conftest import get_patched_exchange, log_has, log_has_re