Merge pull request #2327 from hroff-1902/enhance-list-exchanges2

Add --all option to list-exchanges
This commit is contained in:
Matthias 2019-10-01 06:52:27 +02:00 committed by GitHub
commit 628c4c996a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 69 additions and 24 deletions

View File

@ -29,7 +29,7 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path",
ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] 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"] ARGS_CREATE_USERDIR = ["user_data_dir"]

View File

@ -3,7 +3,7 @@ from typing import Any, Dict
from freqtrade import OperationalException from freqtrade import OperationalException
from freqtrade.exchange import (available_exchanges, get_exchange_bad_reason, 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) is_exchange_officially_supported)
from freqtrade.state import RunMode from freqtrade.state import RunMode
@ -31,15 +31,15 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool:
raise OperationalException( raise OperationalException(
f'This command requires a configured exchange. You should either use ' f'This command requires a configured exchange. You should either use '
f'`--exchange <exchange_name>` or specify a configuration file via `--config`.\n' f'`--exchange <exchange_name>` 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())}' f'{", ".join(available_exchanges())}'
) )
if not is_exchange_available(exchange): if not is_exchange_known_ccxt(exchange):
raise OperationalException( 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'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())}' 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 ' logger.info(f'Exchange "{exchange}" is officially supported '
f'by the Freqtrade development team.') f'by the Freqtrade development team.')
else: else:
logger.warning(f'Exchange "{exchange}" is supported by ccxt ' logger.warning(f'Exchange "{exchange}" is known to the the ccxt library, '
f'and therefore available for the bot but not officially supported ' f'available for the bot, but not officially supported '
f'by the Freqtrade development team. ' f'by the Freqtrade development team. '
f'It may work flawlessly (please report back) or have serious issues. ' f'It may work flawlessly (please report back) or have serious issues. '
f'Use it at your own discretion.') f'Use it at your own discretion.')

View File

@ -244,6 +244,11 @@ AVAILABLE_CLI_OPTIONS = {
help='Print exchanges in one column.', help='Print exchanges in one column.',
action='store_true', action='store_true',
), ),
"list_exchanges_all": Arg(
'-a', '--all',
help='Print all exchanges known to the ccxt library.',
action='store_true',
),
# Script options # Script options
"pairs": Arg( "pairs": Arg(
'-p', '--pairs', '-p', '--pairs',

View File

@ -1,8 +1,9 @@
from freqtrade.exchange.exchange import Exchange # noqa: F401 from freqtrade.exchange.exchange import Exchange # noqa: F401
from freqtrade.exchange.exchange import (get_exchange_bad_reason, # noqa: F401 from freqtrade.exchange.exchange import (get_exchange_bad_reason, # noqa: F401
is_exchange_bad, is_exchange_bad,
is_exchange_available, is_exchange_known_ccxt,
is_exchange_officially_supported, is_exchange_officially_supported,
ccxt_exchanges,
available_exchanges) available_exchanges)
from freqtrade.exchange.exchange import (timeframe_to_seconds, # noqa: F401 from freqtrade.exchange.exchange import (timeframe_to_seconds, # noqa: F401
timeframe_to_minutes, timeframe_to_minutes,

View File

@ -235,7 +235,7 @@ class Exchange:
# Find matching class for the given exchange name # Find matching class for the given exchange name
name = exchange_config['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') raise OperationalException(f'Exchange {name} is not supported by ccxt')
ex_config = { ex_config = {
@ -838,18 +838,29 @@ def get_exchange_bad_reason(exchange_name: str) -> str:
return BAD_EXCHANGES.get(exchange_name, "") return BAD_EXCHANGES.get(exchange_name, "")
def is_exchange_available(exchange_name: str, ccxt_module=None) -> bool: def is_exchange_known_ccxt(exchange_name: str, ccxt_module=None) -> bool:
return exchange_name in available_exchanges(ccxt_module) return exchange_name in ccxt_exchanges(ccxt_module)
def is_exchange_officially_supported(exchange_name: str) -> bool: def is_exchange_officially_supported(exchange_name: str) -> bool:
return exchange_name in ['bittrex', 'binance'] 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 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: def timeframe_to_seconds(ticker_interval: str) -> int:
""" """
Translates the timeframe interval value written in the human readable Translates the timeframe interval value written in the human readable

View File

@ -9,7 +9,7 @@ from freqtrade import OperationalException
from freqtrade.configuration import Configuration, TimeRange from freqtrade.configuration import Configuration, TimeRange
from freqtrade.configuration.directory_operations import create_userdata_dir from freqtrade.configuration.directory_operations import create_userdata_dir
from freqtrade.data.history import refresh_backtest_ohlcv_data 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.resolvers import ExchangeResolver
from freqtrade.state import RunMode from freqtrade.state import RunMode
@ -39,12 +39,14 @@ def start_list_exchanges(args: Dict[str, Any]) -> None:
:param args: Cli args from Arguments() :param args: Cli args from Arguments()
:return: None :return: None
""" """
exchanges = ccxt_exchanges() if args['list_exchanges_all'] else available_exchanges()
if args['print_one_column']: if args['print_one_column']:
print('\n'.join(available_exchanges())) print('\n'.join(exchanges))
else: else:
print(f"Exchanges supported by ccxt and available for Freqtrade: " if args['list_exchanges_all']:
f"{', '.join(available_exchanges())}") 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: def start_create_userdir(args: Dict[str, Any]) -> None:

View File

@ -506,7 +506,8 @@ def test_check_exchange(default_conf, caplog) -> None:
# Test an available exchange, supported by ccxt # Test an available exchange, supported by ccxt
default_conf.get('exchange').update({'name': 'huobipro'}) default_conf.get('exchange').update({'name': 'huobipro'})
assert check_exchange(default_conf) 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) r"by the Freqtrade development team\. .*", caplog)
caplog.clear() caplog.clear()
@ -520,16 +521,16 @@ def test_check_exchange(default_conf, caplog) -> None:
# Test a 'bad' exchange with check_for_bad=False # Test a 'bad' exchange with check_for_bad=False
default_conf.get('exchange').update({'name': 'bitmex'}) default_conf.get('exchange').update({'name': 'bitmex'})
assert check_exchange(default_conf, False) 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) r"by the Freqtrade development team\. .*", caplog)
caplog.clear() caplog.clear()
# Test an invalid exchange # Test an invalid exchange
default_conf.get('exchange').update({'name': 'unknown_exchange'}) default_conf.get('exchange').update({'name': 'unknown_exchange'})
with pytest.raises( with pytest.raises(
OperationalException, 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.*' r'and therefore not available for the bot.*'
): ):
check_exchange(default_conf) check_exchange(default_conf)

View File

@ -31,7 +31,7 @@ def test_list_exchanges(capsys):
start_list_exchanges(get_args(args)) start_list_exchanges(get_args(args))
captured = capsys.readouterr() 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".*binance,.*", captured.out)
assert re.match(r".*bittrex,.*", captured.out) assert re.match(r".*bittrex,.*", captured.out)
@ -43,10 +43,35 @@ def test_list_exchanges(capsys):
start_list_exchanges(get_args(args)) start_list_exchanges(get_args(args))
captured = capsys.readouterr() 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"^binance$", captured.out, re.MULTILINE)
assert re.search(r"^bittrex$", 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): def test_create_datadir_failed(caplog):