Refactor Configuration() to apply common configurations all the time and to remove show_info

This commit is contained in:
Gerald Lonlas 2018-03-03 13:39:39 -08:00
parent 0632cf0f44
commit 84759073d9
6 changed files with 71 additions and 70 deletions

View File

@ -17,14 +17,13 @@ class Configuration(object):
Class to read and init the bot configuration Class to read and init the bot configuration
Reuse this class for the bot, backtesting, hyperopt and every script that required 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.args = args
self.logging = Logger(name=__name__) self.logging = Logger(name=__name__)
self.logger = self.logging.get_logger() self.logger = self.logging.get_logger()
self.config = self._load_config() self.config = None
self.show_info()
def _load_config(self) -> Dict[str, Any]: def load_config(self) -> Dict[str, Any]:
""" """
Extract information for sys.argv and load the bot configuration Extract information for sys.argv and load the bot configuration
:return: Configuration dictionary :return: Configuration dictionary
@ -35,18 +34,8 @@ class Configuration(object):
# Add the strategy file to use # Add the strategy file to use
config.update({'strategy': self.args.strategy}) config.update({'strategy': self.args.strategy})
# Add dynamic_whitelist if found # Load Common configuration
if 'dynamic_whitelist' in self.args and self.args.dynamic_whitelist: config = self._load_common_config(config)
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 Backtesting # Load Backtesting
config = self._load_backtesting_config(config) config = self._load_backtesting_config(config)
@ -71,11 +60,46 @@ class Configuration(object):
return self._validate_config(conf) 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]: def _load_backtesting_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
""" """
Extract information for sys.argv and load Backtesting configuration Extract information for sys.argv and load Backtesting configuration
:return: configuration as dictionary :return: configuration as dictionary
""" """
# If -i/--ticker-interval is used we override the configuration parameter # If -i/--ticker-interval is used we override the configuration parameter
# (that will override the strategy configuration) # (that will override the strategy configuration)
if 'ticker_interval' in self.args and self.args.ticker_interval: 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 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]: def get_config(self) -> Dict[str, Any]:
""" """
Return the config. Use this method to get the bot config Return the config. Use this method to get the bot config
:return: Dict: Bot config :return: Dict: Bot config
""" """
if self.config is None:
self.config = self.load_config()
return self.config return self.config

View File

@ -69,4 +69,3 @@ def file_dump_json(filename, data) -> None:
""" """
with open(filename, 'w') as file: with open(filename, 'w') as file:
json.dump(data, file) json.dump(data, file)

View File

@ -293,6 +293,7 @@ def setup_configuration(args) -> Dict[str, Any]:
:return: Configuration :return: Configuration
""" """
configuration = Configuration(args) configuration = Configuration(args)
config = configuration.get_config() config = configuration.get_config()
# Ensure we do not use Exchange credentials # Ensure we do not use Exchange credentials

View File

@ -589,7 +589,8 @@ def start(args) -> None:
# Monkey patch the configuration with hyperopt_conf.py # Monkey patch the configuration with hyperopt_conf.py
configuration = Configuration(args) configuration = Configuration(args)
optimize_config = hyperopt_optimize_conf() 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 = configuration._load_hyperopt_config(config)
config['exchange']['key'] = '' config['exchange']['key'] = ''
config['exchange']['secret'] = '' config['exchange']['secret'] = ''

View File

@ -6,7 +6,6 @@ Unit test file for configuration.py
import json import json
from copy import deepcopy from copy import deepcopy
from unittest.mock import MagicMock
import pytest import pytest
from jsonschema import ValidationError from jsonschema import ValidationError
@ -19,10 +18,12 @@ def test_configuration_object() -> None:
""" """
Test the Constants object has the mandatory Constants 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, '_load_config_file')
assert hasattr(Configuration, '_validate_config') 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') 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 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 = deepcopy(default_conf)
conf['exchange']['pair_whitelist'].append('BTC-ETH') 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 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 = deepcopy(default_conf)
conf.pop('exchange') conf.pop('exchange')
@ -64,11 +55,6 @@ def test_load_config_file(default_conf, mocker, caplog) -> None:
""" """
Test Configuration._load_config_file() method 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( file_mock = mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf) 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: 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( mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf) read_data=json.dumps(default_conf)
@ -91,7 +77,7 @@ def test_load_config(default_conf, mocker) -> None:
args = Arguments([], '').get_parsed_arg() args = Arguments([], '').get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
validated_conf = configuration._load_config() validated_conf = configuration.load_config()
assert 'strategy' in validated_conf assert 'strategy' in validated_conf
assert validated_conf['strategy'] == 'default_strategy' 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: 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( mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf) 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() args = Arguments(args, '').get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
validated_conf = configuration._load_config() validated_conf = configuration.load_config()
assert 'dynamic_whitelist' in validated_conf assert 'dynamic_whitelist' in validated_conf
assert validated_conf['dynamic_whitelist'] == 10 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() args = Arguments(args, '').get_parsed_arg()
configuration = Configuration(args) configuration = Configuration(args)
configuration.show_info() configuration.get_config()
assert tt.log_has( 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 caplog.record_tuples
) )
assert tt.log_has( assert tt.log_has(
'Dry_run will use the DB file: "tradesv3.dry_run.sqlite". ' 'Parameter --dry-run-db detected ...',
'(--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 caplog.record_tuples
) )
# Test the Dry run condition # Test the Dry run condition
configuration.config.update({'dry_run': False}) configuration.config.update({'dry_run': False})
configuration.show_info() configuration._load_common_config(configuration.config)
assert tt.log_has( assert tt.log_has(
'Dry run is disabled. (--dry_run_db ignored)', 'Dry run is disabled. (--dry_run_db ignored)',
caplog.record_tuples caplog.record_tuples
@ -167,7 +159,6 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
""" """
Test setup_configuration() function Test setup_configuration() function
""" """
mocker.patch('freqtrade.configuration.Configuration.show_info', MagicMock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open( mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf) 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 Test setup_configuration() function
""" """
mocker.patch('freqtrade.configuration.Configuration.show_info', MagicMock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open( mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf) read_data=json.dumps(default_conf)
)) ))

View File

@ -29,7 +29,7 @@ def test_parse_args_backtesting(mocker) -> None:
def test_main_start_hyperopt(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()) hyperopt_mock = mocker.patch('freqtrade.optimize.hyperopt.start', MagicMock())
main(['hyperopt']) main(['hyperopt'])
@ -61,7 +61,7 @@ def test_set_loggers() -> None:
def test_main(mocker, caplog) -> 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. In this test we are skipping the while True loop by throwing an exception.
""" """
mocker.patch.multiple( mocker.patch.multiple(
@ -73,9 +73,11 @@ def test_main(mocker, caplog) -> None:
clean=MagicMock(), clean=MagicMock(),
) )
args = ['-c', 'config.json.example']
# Test Main + the KeyboardInterrupt exception # Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit) as pytest_wrapped_e: with pytest.raises(SystemExit) as pytest_wrapped_e:
main([]) main(args)
tt.log_has('Starting freqtrade', caplog.record_tuples) tt.log_has('Starting freqtrade', caplog.record_tuples)
tt.log_has('Got SIGINT, aborting ...', caplog.record_tuples) tt.log_has('Got SIGINT, aborting ...', caplog.record_tuples)
assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.type == SystemExit
@ -87,5 +89,5 @@ def test_main(mocker, caplog) -> None:
MagicMock(side_effect=BaseException) MagicMock(side_effect=BaseException)
) )
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
main([]) main(args)
tt.log_has('Got fatal exception!', caplog.record_tuples) tt.log_has('Got fatal exception!', caplog.record_tuples)