From 84759073d9a65ffd3ce6acb99e30c62afa4593c0 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Sat, 3 Mar 2018 13:39:39 -0800 Subject: [PATCH] Refactor Configuration() to apply common configurations all the time and to remove show_info --- freqtrade/configuration.py | 78 +++++++++++++++------------ freqtrade/misc.py | 1 - freqtrade/optimize/backtesting.py | 1 + freqtrade/optimize/hyperopt.py | 3 +- freqtrade/tests/test_configuration.py | 48 +++++++---------- freqtrade/tests/test_main.py | 10 ++-- 6 files changed, 71 insertions(+), 70 deletions(-) diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index 721def2a4..dfce90bf6 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -17,14 +17,13 @@ class Configuration(object): Class to read and init the bot configuration Reuse this class for the bot, backtesting, hyperopt and every script that required configuration """ - def __init__(self, args: List[str]) -> None: + def __init__(self, args: List[str], do_not_init=False) -> None: self.args = args self.logging = Logger(name=__name__) self.logger = self.logging.get_logger() - self.config = self._load_config() - self.show_info() + self.config = None - def _load_config(self) -> Dict[str, Any]: + def load_config(self) -> Dict[str, Any]: """ Extract information for sys.argv and load the bot configuration :return: Configuration dictionary @@ -35,18 +34,8 @@ class Configuration(object): # Add the strategy file to use config.update({'strategy': self.args.strategy}) - # Add dynamic_whitelist if found - if 'dynamic_whitelist' in self.args and self.args.dynamic_whitelist: - config.update({'dynamic_whitelist': self.args.dynamic_whitelist}) - - # Add dry_run_db if found and the bot in dry run - if self.args.dry_run_db and config.get('dry_run', False): - config.update({'dry_run_db': True}) - - # Log level - if 'loglevel' in self.args and self.args.loglevel: - config.update({'loglevel': self.args.loglevel}) - self.logging.set_level(self.args.loglevel) + # Load Common configuration + config = self._load_common_config(config) # Load Backtesting config = self._load_backtesting_config(config) @@ -71,11 +60,46 @@ class Configuration(object): return self._validate_config(conf) + def _load_common_config(self, config: Dict[str, Any]) -> Dict[str, Any]: + """ + Extract information for sys.argv and load common configuration + :return: configuration as dictionary + """ + + # Log level + if 'loglevel' in self.args and self.args.loglevel: + config.update({'loglevel': self.args.loglevel}) + self.logging.set_level(self.args.loglevel) + self.logger.info('Log level set at %s', config['loglevel']) + + # Add dynamic_whitelist if found + if 'dynamic_whitelist' in self.args and self.args.dynamic_whitelist: + config.update({'dynamic_whitelist': self.args.dynamic_whitelist}) + self.logger.info( + 'Parameter --dynamic-whitelist detected. ' + 'Using dynamically generated whitelist. ' + '(not applicable with Backtesting and Hyperopt)' + ) + + # Add dry_run_db if found and the bot in dry run + if self.args.dry_run_db and config.get('dry_run', False): + config.update({'dry_run_db': True}) + self.logger.info('Parameter --dry-run-db detected ...') + + if config.get('dry_run_db', False): + if config.get('dry_run', False): + self.logger.info('Dry_run will use the DB file: "tradesv3.dry_run.sqlite"') + else: + self.logger.info('Dry run is disabled. (--dry_run_db ignored)') + + return config + def _load_backtesting_config(self, config: Dict[str, Any]) -> Dict[str, Any]: """ Extract information for sys.argv and load Backtesting configuration :return: configuration as dictionary """ + # If -i/--ticker-interval is used we override the configuration parameter # (that will override the strategy configuration) if 'ticker_interval' in self.args and self.args.ticker_interval: @@ -152,28 +176,12 @@ class Configuration(object): best_match(Draft4Validator(Constants.CONF_SCHEMA).iter_errors(conf)).message ) - def show_info(self) -> None: - """ - Display info message to user depending of the configuration of the bot - :return: None - """ - if self.config.get('dynamic_whitelist', False): - self.logger.info( - 'Using dynamically generated whitelist. (--dynamic-whitelist detected)' - ) - - if self.config.get('dry_run_db', False): - if self.config.get('dry_run', False): - self.logger.info( - 'Dry_run will use the DB file: "tradesv3.dry_run.sqlite". ' - '(--dry_run_db detected)' - ) - else: - self.logger.info('Dry run is disabled. (--dry_run_db ignored)') - def get_config(self) -> Dict[str, Any]: """ Return the config. Use this method to get the bot config :return: Dict: Bot config """ + if self.config is None: + self.config = self.load_config() + return self.config diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 55bfed1f7..741a306a1 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -69,4 +69,3 @@ def file_dump_json(filename, data) -> None: """ with open(filename, 'w') as file: json.dump(data, file) - diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index dfe5a5f49..51cc7c053 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -293,6 +293,7 @@ def setup_configuration(args) -> Dict[str, Any]: :return: Configuration """ configuration = Configuration(args) + config = configuration.get_config() # Ensure we do not use Exchange credentials diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 3408a63c3..ad8ff32be 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -589,7 +589,8 @@ def start(args) -> None: # Monkey patch the configuration with hyperopt_conf.py configuration = Configuration(args) optimize_config = hyperopt_optimize_conf() - config = configuration._load_backtesting_config(optimize_config) + config = configuration._load_common_config(optimize_config) + config = configuration._load_backtesting_config(config) config = configuration._load_hyperopt_config(config) config['exchange']['key'] = '' config['exchange']['secret'] = '' diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index e6ca8bfd0..cb50ea72d 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -6,7 +6,6 @@ Unit test file for configuration.py import json from copy import deepcopy -from unittest.mock import MagicMock import pytest from jsonschema import ValidationError @@ -19,10 +18,12 @@ def test_configuration_object() -> None: """ Test the Constants object has the mandatory Constants """ - assert hasattr(Configuration, '_load_config') + assert hasattr(Configuration, 'load_config') assert hasattr(Configuration, '_load_config_file') assert hasattr(Configuration, '_validate_config') - assert hasattr(Configuration, 'show_info') + assert hasattr(Configuration, '_load_common_config') + assert hasattr(Configuration, '_load_backtesting_config') + assert hasattr(Configuration, '_load_hyperopt_config') assert hasattr(Configuration, 'get_config') @@ -30,11 +31,6 @@ def test_load_config_invalid_pair(default_conf, mocker) -> None: """ Test the configuration validator with an invalid PAIR format """ - mocker.patch.multiple( - 'freqtrade.configuration.Configuration', - _load_config=MagicMock(return_value=[]), - show_info=MagicMock - ) conf = deepcopy(default_conf) conf['exchange']['pair_whitelist'].append('BTC-ETH') @@ -47,11 +43,6 @@ def test_load_config_missing_attributes(default_conf, mocker) -> None: """ Test the configuration validator with a missing attribute """ - mocker.patch.multiple( - 'freqtrade.configuration.Configuration', - _load_config=MagicMock(return_value=[]), - show_info=MagicMock - ) conf = deepcopy(default_conf) conf.pop('exchange') @@ -64,11 +55,6 @@ def test_load_config_file(default_conf, mocker, caplog) -> None: """ Test Configuration._load_config_file() method """ - mocker.patch.multiple( - 'freqtrade.configuration.Configuration', - _load_config=MagicMock(return_value=[]), - show_info=MagicMock - ) file_mock = mocker.patch('freqtrade.configuration.open', mocker.mock_open( read_data=json.dumps(default_conf) )) @@ -83,7 +69,7 @@ def test_load_config_file(default_conf, mocker, caplog) -> None: def test_load_config(default_conf, mocker) -> None: """ - Test Configuration._load_config() without any cli params + Test Configuration.load_config() without any cli params """ mocker.patch('freqtrade.configuration.open', mocker.mock_open( read_data=json.dumps(default_conf) @@ -91,7 +77,7 @@ def test_load_config(default_conf, mocker) -> None: args = Arguments([], '').get_parsed_arg() configuration = Configuration(args) - validated_conf = configuration._load_config() + validated_conf = configuration.load_config() assert 'strategy' in validated_conf assert validated_conf['strategy'] == 'default_strategy' @@ -101,7 +87,7 @@ def test_load_config(default_conf, mocker) -> None: def test_load_config_with_params(default_conf, mocker) -> None: """ - Test Configuration._load_config() with cli params used + Test Configuration.load_config() with cli params used """ mocker.patch('freqtrade.configuration.open', mocker.mock_open( read_data=json.dumps(default_conf) @@ -115,7 +101,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: args = Arguments(args, '').get_parsed_arg() configuration = Configuration(args) - validated_conf = configuration._load_config() + validated_conf = configuration.load_config() assert 'dynamic_whitelist' in validated_conf assert validated_conf['dynamic_whitelist'] == 10 @@ -141,22 +127,28 @@ def test_show_info(default_conf, mocker, caplog) -> None: args = Arguments(args, '').get_parsed_arg() configuration = Configuration(args) - configuration.show_info() + configuration.get_config() assert tt.log_has( - 'Using dynamically generated whitelist. (--dynamic-whitelist detected)', + 'Parameter --dynamic-whitelist detected. ' + 'Using dynamically generated whitelist. ' + '(not applicable with Backtesting and Hyperopt)', caplog.record_tuples ) assert tt.log_has( - 'Dry_run will use the DB file: "tradesv3.dry_run.sqlite". ' - '(--dry_run_db detected)', + 'Parameter --dry-run-db detected ...', + caplog.record_tuples + ) + + assert tt.log_has( + 'Dry_run will use the DB file: "tradesv3.dry_run.sqlite"', caplog.record_tuples ) # Test the Dry run condition configuration.config.update({'dry_run': False}) - configuration.show_info() + configuration._load_common_config(configuration.config) assert tt.log_has( 'Dry run is disabled. (--dry_run_db ignored)', caplog.record_tuples @@ -167,7 +159,6 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> """ Test setup_configuration() function """ - mocker.patch('freqtrade.configuration.Configuration.show_info', MagicMock) mocker.patch('freqtrade.configuration.open', mocker.mock_open( read_data=json.dumps(default_conf) )) @@ -212,7 +203,6 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non """ Test setup_configuration() function """ - mocker.patch('freqtrade.configuration.Configuration.show_info', MagicMock) mocker.patch('freqtrade.configuration.open', mocker.mock_open( read_data=json.dumps(default_conf) )) diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index ae89912e3..ef6aabedd 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -29,7 +29,7 @@ def test_parse_args_backtesting(mocker) -> None: def test_main_start_hyperopt(mocker) -> None: """ - Test that main() can start hyperopt. + Test that main() can start hyperopt """ hyperopt_mock = mocker.patch('freqtrade.optimize.hyperopt.start', MagicMock()) main(['hyperopt']) @@ -61,7 +61,7 @@ def test_set_loggers() -> None: def test_main(mocker, caplog) -> None: """ - Test main() function. + Test main() function In this test we are skipping the while True loop by throwing an exception. """ mocker.patch.multiple( @@ -73,9 +73,11 @@ def test_main(mocker, caplog) -> None: clean=MagicMock(), ) + args = ['-c', 'config.json.example'] + # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit) as pytest_wrapped_e: - main([]) + main(args) tt.log_has('Starting freqtrade', caplog.record_tuples) tt.log_has('Got SIGINT, aborting ...', caplog.record_tuples) assert pytest_wrapped_e.type == SystemExit @@ -87,5 +89,5 @@ def test_main(mocker, caplog) -> None: MagicMock(side_effect=BaseException) ) with pytest.raises(SystemExit): - main([]) + main(args) tt.log_has('Got fatal exception!', caplog.record_tuples)