Merge pull request #2327 from hroff-1902/enhance-list-exchanges2
Add --all option to list-exchanges
This commit is contained in:
commit
628c4c996a
@ -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"]
|
||||||
|
|
||||||
|
@ -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.')
|
||||||
|
@ -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',
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user