diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index 6388a97ac..54862ff70 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -7,7 +7,7 @@ import os import sys from argparse import Namespace from logging.handlers import RotatingFileHandler -from typing import Any, Dict, List, Optional +from typing import Any, Callable, Dict, List, Optional from jsonschema import Draft4Validator, validators from jsonschema.exceptions import ValidationError, best_match @@ -17,7 +17,6 @@ from freqtrade.exchange import is_exchange_supported, supported_exchanges from freqtrade.misc import deep_merge_dicts from freqtrade.state import RunMode - logger = logging.getLogger(__name__) @@ -95,14 +94,8 @@ class Configuration(object): # Load Common configuration config = self._load_common_config(config) - # Load Backtesting - config = self._load_backtesting_config(config) - - # Load Edge - config = self._load_edge_config(config) - - # Load Hyperopt - config = self._load_hyperopt_config(config) + # Load Optimize configurations + config = self._load_optimize_config(config) # Set runmode if not self.runmode: @@ -216,25 +209,41 @@ class Configuration(object): logger.info(f'Created data directory: {datadir}') return datadir - def _load_backtesting_config(self, config: Dict[str, Any]) -> Dict[str, Any]: # noqa: C901 + def _args_to_config(self, config: Dict[str, Any], argname: str, + logstring: str, logfun: Optional[Callable] = None) -> None: """ - Extract information for sys.argv and load Backtesting configuration + :param config: Configuration dictionary + :param argname: Argumentname in self.args - will be copied to config dict. + :param logstring: Logging String + :param logfun: logfun is applied to the configuration entry before passing + that entry to the log string using .format(). + sample: logfun=len (prints the length of the found + configuration instead of the content) + """ + if argname in self.args and getattr(self.args, argname): + + config.update({argname: getattr(self.args, argname)}) + if logfun: + logger.info(logstring.format(logfun(config[argname]))) + else: + logger.info(logstring.format(config[argname])) + + def _load_optimize_config(self, config: Dict[str, Any]) -> Dict[str, Any]: + """ + Extract information for sys.argv and load Optimize configuration :return: configuration as dictionary """ # This will override the strategy configuration - if 'ticker_interval' in self.args and self.args.ticker_interval: - config.update({'ticker_interval': self.args.ticker_interval}) - logger.info('Parameter -i/--ticker-interval detected ...') - logger.info('Using ticker_interval: %s ...', config.get('ticker_interval')) + self._args_to_config(config, argname='ticker_interval', + logstring='Parameter -i/--ticker-interval detected ... ' + 'Using ticker_interval: {} ...') - if 'live' in self.args and self.args.live: - config.update({'live': True}) - logger.info('Parameter -l/--live detected ...') + self._args_to_config(config, argname='live', + logstring='Parameter -l/--live detected ...') - if 'position_stacking' in self.args and self.args.position_stacking: - config.update({'position_stacking': True}) - logger.info('Parameter --enable-position-stacking detected ...') + self._args_to_config(config, argname='position_stacking', + logstring='Parameter --enable-position-stacking detected ...') if 'use_max_market_positions' in self.args and not self.args.use_max_market_positions: config.update({'use_max_market_positions': False}) @@ -247,14 +256,12 @@ class Configuration(object): else: logger.info('Using max_open_trades: %s ...', config.get('max_open_trades')) - if 'stake_amount' in self.args and self.args.stake_amount: - config.update({'stake_amount': self.args.stake_amount}) - logger.info('Parameter --stake_amount detected, overriding stake_amount to: %s ...', - config.get('stake_amount')) + self._args_to_config(config, argname='stake_amount', + logstring='Parameter --stake_amount detected, ' + 'overriding stake_amount to: {} ...') - if 'timerange' in self.args and self.args.timerange: - config.update({'timerange': self.args.timerange}) - logger.info('Parameter --timerange detected: %s ...', self.args.timerange) + self._args_to_config(config, argname='timerange', + logstring='Parameter --timerange detected: {} ...') if 'datadir' in self.args and self.args.datadir: config.update({'datadir': self._create_datadir(config, self.args.datadir)}) @@ -262,38 +269,22 @@ class Configuration(object): config.update({'datadir': self._create_datadir(config, None)}) logger.info('Using data folder: %s ...', config.get('datadir')) - if 'refresh_pairs' in self.args and self.args.refresh_pairs: - config.update({'refresh_pairs': True}) - logger.info('Parameter -r/--refresh-pairs-cached detected ...') + self._args_to_config(config, argname='refresh_pairs', + logstring='Parameter -r/--refresh-pairs-cached detected ...') - if 'strategy_list' in self.args and self.args.strategy_list: - config.update({'strategy_list': self.args.strategy_list}) - logger.info('Using strategy list of %s Strategies', len(self.args.strategy_list)) + self._args_to_config(config, argname='strategy_list', + logstring='Using strategy list of {} Strategies', logfun=len) - if 'ticker_interval' in self.args and self.args.ticker_interval: - config.update({'ticker_interval': self.args.ticker_interval}) - logger.info('Overriding ticker interval with Command line argument') + self._args_to_config(config, argname='ticker_interval', + logstring='Overriding ticker interval with Command line argument') - if 'export' in self.args and self.args.export: - config.update({'export': self.args.export}) - logger.info('Parameter --export detected: %s ...', self.args.export) + self._args_to_config(config, argname='export', + logstring='Parameter --export detected: {} ...') - if 'export' in config and 'exportfilename' in self.args and self.args.exportfilename: - config.update({'exportfilename': self.args.exportfilename}) - logger.info('Storing backtest results to %s ...', self.args.exportfilename) - - return config - - def _load_edge_config(self, config: Dict[str, Any]) -> Dict[str, Any]: - """ - Extract information for sys.argv and load Edge configuration - :return: configuration as dictionary - """ - - if 'timerange' in self.args and self.args.timerange: - config.update({'timerange': self.args.timerange}) - logger.info('Parameter --timerange detected: %s ...', self.args.timerange) + self._args_to_config(config, argname='exportfilename', + logstring='Storing backtest results to {} ...') + # Edge section: if 'stoploss_range' in self.args and self.args.stoploss_range: txt_range = eval(self.args.stoploss_range) config['edge'].update({'stoploss_range_min': txt_range[0]}) @@ -301,48 +292,26 @@ class Configuration(object): config['edge'].update({'stoploss_range_step': txt_range[2]}) logger.info('Parameter --stoplosses detected: %s ...', self.args.stoploss_range) - if 'refresh_pairs' in self.args and self.args.refresh_pairs: - config.update({'refresh_pairs': True}) - logger.info('Parameter -r/--refresh-pairs-cached detected ...') + # Hyperopt section + self._args_to_config(config, argname='hyperopt', + logstring='Using Hyperopt file {}') - return config + self._args_to_config(config, argname='epochs', + logstring='Parameter --epochs detected ... ' + 'Will run Hyperopt with for {} epochs ...' + ) - def _load_hyperopt_config(self, config: Dict[str, Any]) -> Dict[str, Any]: - """ - Extract information for sys.argv and load Hyperopt configuration - :return: configuration as dictionary - """ + self._args_to_config(config, argname='spaces', + logstring='Parameter -s/--spaces detected: {}') - if "hyperopt" in self.args: - # Add the hyperopt file to use - config.update({'hyperopt': self.args.hyperopt}) + self._args_to_config(config, argname='print_all', + logstring='Parameter --print-all detected ...') - if 'epochs' in self.args and self.args.epochs: - config.update({'epochs': self.args.epochs}) - logger.info('Parameter --epochs detected ...') - logger.info('Will run Hyperopt with for %s epochs ...', config.get('epochs')) - - if 'spaces' in self.args and self.args.spaces: - config.update({'spaces': self.args.spaces}) - logger.info('Parameter -s/--spaces detected: %s', config.get('spaces')) - - if 'print_all' in self.args and self.args.print_all: - config.update({'print_all': self.args.print_all}) - logger.info('Parameter --print-all detected: %s', config.get('print_all')) - - if 'hyperopt_jobs' in self.args and self.args.hyperopt_jobs: - config.update({'hyperopt_jobs': self.args.hyperopt_jobs}) - logger.info('Parameter -j/--job-workers detected: %s', config.get('hyperopt_jobs')) - - if 'refresh_pairs' in self.args and self.args.refresh_pairs: - config.update({'refresh_pairs': True}) - logger.info('Parameter -r/--refresh-pairs-cached detected ...') - - if 'hyperopt_random_state' in self.args and self.args.hyperopt_random_state is not None: - config.update({'hyperopt_random_state': self.args.hyperopt_random_state}) - logger.info("Parameter --random-state detected: %s", - config.get('hyperopt_random_state')) + self._args_to_config(config, argname='hyperopt_jobs', + logstring='Parameter -j/--job-workers detected: {}') + self._args_to_config(config, argname='hyperopt_random_state', + logstring='Parameter --random-state detected: {}') return config def _validate_config_schema(self, conf: Dict[str, Any]) -> Dict[str, Any]: diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index af0b07d6f..281ce2bfe 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -23,7 +23,7 @@ from freqtrade.optimize.backtesting import (Backtesting, setup_configuration, from freqtrade.state import RunMode from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.interface import SellType -from freqtrade.tests.conftest import log_has, patch_exchange +from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange def get_args(args) -> List[str]: @@ -190,7 +190,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> caplog.record_tuples ) assert 'ticker_interval' in config - assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) + assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) assert 'live' not in config assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) @@ -242,11 +242,8 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> caplog.record_tuples ) assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) - assert log_has( - 'Using ticker_interval: 1m ...', - caplog.record_tuples - ) + assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + caplog.record_tuples) assert 'live' in config assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) @@ -853,8 +850,7 @@ def test_backtest_start_live(default_conf, mocker, caplog): start(args) # check the logs, that will contain the backtest result exists = [ - 'Parameter -i/--ticker-interval detected ...', - 'Using ticker_interval: 1m ...', + 'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', 'Parameter -l/--live detected ...', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Parameter --timerange detected: -100 ...', @@ -912,8 +908,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog): # check the logs, that will contain the backtest result exists = [ - 'Parameter -i/--ticker-interval detected ...', - 'Using ticker_interval: 1m ...', + 'Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', 'Parameter -l/--live detected ...', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Parameter --timerange detected: -100 ...', diff --git a/freqtrade/tests/optimize/test_edge_cli.py b/freqtrade/tests/optimize/test_edge_cli.py index a58620139..488d552c8 100644 --- a/freqtrade/tests/optimize/test_edge_cli.py +++ b/freqtrade/tests/optimize/test_edge_cli.py @@ -1,14 +1,15 @@ # pragma pylint: disable=missing-docstring, C0103, C0330 # pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments -from unittest.mock import MagicMock import json from typing import List -from freqtrade.edge import PairInfo +from unittest.mock import MagicMock + from freqtrade.arguments import Arguments -from freqtrade.optimize.edge_cli import (EdgeCli, setup_configuration, start) +from freqtrade.edge import PairInfo +from freqtrade.optimize.edge_cli import EdgeCli, setup_configuration, start from freqtrade.state import RunMode -from freqtrade.tests.conftest import log_has, patch_exchange +from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange def get_args(args) -> List[str]: @@ -40,7 +41,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> caplog.record_tuples ) assert 'ticker_interval' in config - assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) + assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) assert 'refresh_pairs' not in config assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) @@ -79,11 +80,8 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N caplog.record_tuples ) assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) - assert log_has( - 'Using ticker_interval: 1m ...', - caplog.record_tuples - ) + assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + caplog.record_tuples) assert 'refresh_pairs' in config assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 21db63636..eb2506f00 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -14,7 +14,7 @@ from freqtrade.optimize.default_hyperopt import DefaultHyperOpts from freqtrade.optimize.hyperopt import Hyperopt, setup_configuration, start from freqtrade.resolvers import HyperOptResolver from freqtrade.state import RunMode -from freqtrade.tests.conftest import log_has, patch_exchange +from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange from freqtrade.tests.optimize.test_backtesting import get_args @@ -64,7 +64,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca caplog.record_tuples ) assert 'ticker_interval' in config - assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) + assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) assert 'live' not in config assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) @@ -114,11 +114,8 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo caplog.record_tuples ) assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) - assert log_has( - 'Using ticker_interval: 1m ...', - caplog.record_tuples - ) + assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + caplog.record_tuples) assert 'position_stacking' in config assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) @@ -137,7 +134,8 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo ) assert 'epochs' in config - assert log_has('Parameter --epochs detected ...', caplog.record_tuples) + assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 1000 epochs ...', + caplog.record_tuples) assert 'spaces' in config assert log_has( @@ -145,7 +143,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo caplog.record_tuples ) assert 'print_all' in config - assert log_has('Parameter --print-all detected: True', caplog.record_tuples) + assert log_has('Parameter --print-all detected ...', caplog.record_tuples) def test_hyperoptresolver(mocker, default_conf, caplog) -> None: diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index 09041dd3d..aee0dfadd 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -360,11 +360,8 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non caplog.record_tuples ) assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) - assert log_has( - 'Using ticker_interval: 1m ...', - caplog.record_tuples - ) + assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + caplog.record_tuples) assert 'live' in config assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) @@ -425,11 +422,8 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non caplog.record_tuples ) assert 'ticker_interval' in config - assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) - assert log_has( - 'Using ticker_interval: 1m ...', - caplog.record_tuples - ) + assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', + caplog.record_tuples) assert 'strategy_list' in config assert log_has('Using strategy list of 2 Strategies', caplog.record_tuples) @@ -463,8 +457,8 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: assert 'epochs' in config assert int(config['epochs']) == 10 - assert log_has('Parameter --epochs detected ...', caplog.record_tuples) - assert log_has('Will run Hyperopt with for 10 epochs ...', caplog.record_tuples) + assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 10 epochs ...', + caplog.record_tuples) assert 'spaces' in config assert config['spaces'] == ['all']