diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index 1f6cea4e6..10768db55 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -5,10 +5,11 @@ This module contains the configuration class import json from argparse import Namespace from typing import Dict, Any - from jsonschema import Draft4Validator, validate from jsonschema.exceptions import ValidationError, best_match +import ccxt +from freqtrade import OperationalException from freqtrade.constants import Constants from freqtrade.logger import Logger @@ -100,6 +101,9 @@ class Configuration(object): else: self.logger.info('Dry run is disabled. (--dry_run_db ignored)') + # Check if the exchange set by the user is supported + self.check_exchange() + return config def _load_backtesting_config(self, config: Dict[str, Any]) -> Dict[str, Any]: @@ -198,3 +202,23 @@ class Configuration(object): self.config = self.load_config() return self.config + + def check_exchange(self) -> bool: + """ + Check if the exchange name in the config file is supported by Freqtrade + :return: True or raised an exception if the exchange if not supported + """ + exchange = self.config.get('exchange', {}).get('name').lower() + if exchange not in ccxt.exchanges: + + exception_msg = 'Exchange "{}" not supported.\n' \ + 'The following exchanges are supported: {}'\ + .format(exchange, ', '.join(ccxt.exchanges)) + + self.logger.critical(exception_msg) + raise OperationalException( + exception_msg + ) + + self.logger.debug('Exchange "%s" supported', exchange) + return True diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 2c4136cab..20ea4f43e 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -74,16 +74,14 @@ def init(config: dict) -> None: # Find matching class for the given exchange name name = exchange_config['name'] - # TODO add check for a list of supported exchanges - + # Init the exchange if the exchange name passed is supported try: - # exchange_class = Exchanges[name.upper()].value _API = getattr(ccxt, name.lower())({ 'apiKey': exchange_config.get('key'), 'secret': exchange_config.get('secret'), }) logger.info('Using Exchange %s', name.capitalize()) - except KeyError: + except (KeyError, AttributeError): raise OperationalException('Exchange {} is not supported'.format(name)) # we need load api markets diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index 002eac722..f43c29b43 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -13,6 +13,7 @@ from jsonschema import ValidationError from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.tests.conftest import log_has +from freqtrade import OperationalException def test_configuration_object() -> None: @@ -28,7 +29,7 @@ def test_configuration_object() -> None: assert hasattr(Configuration, 'get_config') -def test_load_config_invalid_pair(default_conf, mocker) -> None: +def test_load_config_invalid_pair(default_conf) -> None: """ Test the configuration validator with an invalid PAIR format """ @@ -40,7 +41,7 @@ def test_load_config_invalid_pair(default_conf, mocker) -> None: configuration._validate_config(conf) -def test_load_config_missing_attributes(default_conf, mocker) -> None: +def test_load_config_missing_attributes(default_conf) -> None: """ Test the configuration validator with a missing attribute """ @@ -314,3 +315,31 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: assert 'spaces' in config assert config['spaces'] == ['all'] assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog.record_tuples) + + +def test_check_exchange(default_conf) -> None: + """ + Test the configuration validator with a missing attribute + """ + conf = deepcopy(default_conf) + configuration = Configuration([]) + + # Test a valid exchange + conf.get('exchange').update({'name': 'BITTREX'}) + configuration.config = conf + assert configuration.check_exchange() + + # Test a valid exchange + conf.get('exchange').update({'name': 'binance'}) + configuration.config = conf + assert configuration.check_exchange() + + # Test a invalid exchange + conf.get('exchange').update({'name': 'unknown_exchange'}) + configuration.config = conf + + with pytest.raises( + OperationalException, + match=r'.*Exchange "unknown_exchange" not supported.*' + ): + configuration.check_exchange()