From d1fa5f307b8406ff6d78763d07478be833a980a0 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 1 Oct 2019 00:33:33 +0300 Subject: [PATCH 1/2] Add --all option to list-exchanges --- freqtrade/configuration/arguments.py | 2 +- freqtrade/configuration/check_exchange.py | 14 +++++++------- freqtrade/configuration/cli_options.py | 5 +++++ freqtrade/exchange/__init__.py | 3 ++- freqtrade/exchange/exchange.py | 19 +++++++++++++++---- freqtrade/utils.py | 12 +++++++----- 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 89c86bdca..da9bef0b0 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -29,7 +29,7 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] -ARGS_LIST_EXCHANGES = ["print_one_column"] +ARGS_LIST_EXCHANGES = ["print_one_column", "list_exchanges_all"] ARGS_CREATE_USERDIR = ["user_data_dir"] diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index 19c377732..5e811fb81 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -3,7 +3,7 @@ from typing import Any, Dict from freqtrade import OperationalException from freqtrade.exchange import (available_exchanges, get_exchange_bad_reason, - is_exchange_available, is_exchange_bad, + is_exchange_known_ccxt, is_exchange_bad, is_exchange_officially_supported) from freqtrade.state import RunMode @@ -31,15 +31,15 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: raise OperationalException( f'This command requires a configured exchange. You should either use ' f'`--exchange ` or specify a configuration file via `--config`.\n' - f'The following exchanges are supported by ccxt: ' + f'The following exchanges are available for Freqtrade: ' f'{", ".join(available_exchanges())}' ) - if not is_exchange_available(exchange): + if not is_exchange_known_ccxt(exchange): raise OperationalException( - f'Exchange "{exchange}" is not supported by ccxt ' + f'Exchange "{exchange}" is not known to the ccxt library ' f'and therefore not available for the bot.\n' - f'The following exchanges are supported by ccxt: ' + f'The following exchanges are available for Freqtrade: ' f'{", ".join(available_exchanges())}' ) @@ -51,8 +51,8 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: logger.info(f'Exchange "{exchange}" is officially supported ' f'by the Freqtrade development team.') else: - logger.warning(f'Exchange "{exchange}" is supported by ccxt ' - f'and therefore available for the bot but not officially supported ' + logger.warning(f'Exchange "{exchange}" is known to the the ccxt library, ' + f'available for the bot, but not officially supported ' f'by the Freqtrade development team. ' f'It may work flawlessly (please report back) or have serious issues. ' f'Use it at your own discretion.') diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index cb07dbdba..caf34b5e3 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -244,6 +244,11 @@ AVAILABLE_CLI_OPTIONS = { help='Print exchanges in one column.', action='store_true', ), + "list_exchanges_all": Arg( + '-a', '--all', + help='Print all exchanges known to the ccxt library.', + action='store_true', + ), # Script options "pairs": Arg( '-p', '--pairs', diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 4037ca704..29971c897 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -1,8 +1,9 @@ from freqtrade.exchange.exchange import Exchange # noqa: F401 from freqtrade.exchange.exchange import (get_exchange_bad_reason, # noqa: F401 is_exchange_bad, - is_exchange_available, + is_exchange_known_ccxt, is_exchange_officially_supported, + ccxt_exchanges, available_exchanges) from freqtrade.exchange.exchange import (timeframe_to_seconds, # noqa: F401 timeframe_to_minutes, diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 3c313ab54..0c307e7ed 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -235,7 +235,7 @@ class Exchange: # Find matching class for the given exchange name name = exchange_config['name'] - if not is_exchange_available(name, ccxt_module): + if not is_exchange_known_ccxt(name, ccxt_module): raise OperationalException(f'Exchange {name} is not supported by ccxt') ex_config = { @@ -838,18 +838,29 @@ def get_exchange_bad_reason(exchange_name: str) -> str: return BAD_EXCHANGES.get(exchange_name, "") -def is_exchange_available(exchange_name: str, ccxt_module=None) -> bool: - return exchange_name in available_exchanges(ccxt_module) +def is_exchange_known_ccxt(exchange_name: str, ccxt_module=None) -> bool: + return exchange_name in ccxt_exchanges(ccxt_module) def is_exchange_officially_supported(exchange_name: str) -> bool: return exchange_name in ['bittrex', 'binance'] -def available_exchanges(ccxt_module=None) -> List[str]: +def ccxt_exchanges(ccxt_module=None) -> List[str]: + """ + Return the list of all exchanges known to ccxt + """ return ccxt_module.exchanges if ccxt_module is not None else ccxt.exchanges +def available_exchanges(ccxt_module=None) -> List[str]: + """ + Return exchanges available to the bot, i.e. non-bad exchanges in the ccxt list + """ + exchanges = ccxt_exchanges(ccxt_module) + return [x for x in exchanges if not is_exchange_bad(x)] + + def timeframe_to_seconds(ticker_interval: str) -> int: """ Translates the timeframe interval value written in the human readable diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 6ce5e888c..3a11ca4fc 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -9,7 +9,7 @@ from freqtrade import OperationalException from freqtrade.configuration import Configuration, TimeRange from freqtrade.configuration.directory_operations import create_userdata_dir from freqtrade.data.history import refresh_backtest_ohlcv_data -from freqtrade.exchange import available_exchanges +from freqtrade.exchange import available_exchanges, ccxt_exchanges from freqtrade.resolvers import ExchangeResolver from freqtrade.state import RunMode @@ -39,12 +39,14 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: :param args: Cli args from Arguments() :return: None """ - + exchanges = ccxt_exchanges() if args['list_exchanges_all'] else available_exchanges() if args['print_one_column']: - print('\n'.join(available_exchanges())) + print('\n'.join(exchanges)) else: - print(f"Exchanges supported by ccxt and available for Freqtrade: " - f"{', '.join(available_exchanges())}") + if args['list_exchanges_all']: + print(f"All exchanges supported by the ccxt library: {', '.join(exchanges)}") + else: + print(f"Exchanges available for Freqtrade: {', '.join(exchanges)}") def start_create_userdir(args: Dict[str, Any]) -> None: From f6a88c6e9bee6e645adc68429fa85096d2e994a1 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 1 Oct 2019 00:33:54 +0300 Subject: [PATCH 2/2] Tests adjusted --- tests/test_configuration.py | 9 +++++---- tests/test_utils.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 330b82d39..0f2d6a50a 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -506,7 +506,8 @@ def test_check_exchange(default_conf, caplog) -> None: # Test an available exchange, supported by ccxt default_conf.get('exchange').update({'name': 'huobipro'}) assert check_exchange(default_conf) - assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported " + assert log_has_re(r"Exchange .* is known to the the ccxt library, available for the bot, " + r"but not officially supported " r"by the Freqtrade development team\. .*", caplog) caplog.clear() @@ -520,16 +521,16 @@ def test_check_exchange(default_conf, caplog) -> None: # Test a 'bad' exchange with check_for_bad=False default_conf.get('exchange').update({'name': 'bitmex'}) assert check_exchange(default_conf, False) - assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported " + assert log_has_re(r"Exchange .* is known to the the ccxt library, available for the bot, " + r"but not officially supported " r"by the Freqtrade development team\. .*", caplog) caplog.clear() # Test an invalid exchange default_conf.get('exchange').update({'name': 'unknown_exchange'}) - with pytest.raises( OperationalException, - match=r'.*Exchange "unknown_exchange" is not supported by ccxt ' + match=r'Exchange "unknown_exchange" is not known to the ccxt library ' r'and therefore not available for the bot.*' ): check_exchange(default_conf) diff --git a/tests/test_utils.py b/tests/test_utils.py index c99044610..9025e4906 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -31,7 +31,7 @@ def test_list_exchanges(capsys): start_list_exchanges(get_args(args)) captured = capsys.readouterr() - assert re.match(r"Exchanges supported by ccxt and available.*", captured.out) + assert re.match(r"Exchanges available for Freqtrade.*", captured.out) assert re.match(r".*binance,.*", captured.out) assert re.match(r".*bittrex,.*", captured.out) @@ -43,10 +43,35 @@ def test_list_exchanges(capsys): start_list_exchanges(get_args(args)) captured = capsys.readouterr() - assert not re.match(r"Exchanges supported by ccxt and available.*", captured.out) assert re.search(r"^binance$", captured.out, re.MULTILINE) assert re.search(r"^bittrex$", captured.out, re.MULTILINE) + # Test with --all + args = [ + "list-exchanges", + "--all", + ] + + start_list_exchanges(get_args(args)) + captured = capsys.readouterr() + assert re.match(r"All exchanges supported by the ccxt library.*", captured.out) + assert re.match(r".*binance,.*", captured.out) + assert re.match(r".*bittrex,.*", captured.out) + assert re.match(r".*bitmex,.*", captured.out) + + # Test with --one-column --all + args = [ + "list-exchanges", + "--one-column", + "--all", + ] + + start_list_exchanges(get_args(args)) + captured = capsys.readouterr() + assert re.search(r"^binance$", captured.out, re.MULTILINE) + assert re.search(r"^bittrex$", captured.out, re.MULTILINE) + assert re.search(r"^bitmex$", captured.out, re.MULTILINE) + def test_create_datadir_failed(caplog):