diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 0ca2f3cc5..988e08029 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -56,8 +56,8 @@ freqtrade -c path/far/far/away/config.json The bot allows you to use multiple configuration files by specifying multiple `-c/--config` configuration options in the command line. Configuration parameters -defined in the last configuration file override parameters with the same name -defined in the previous configuration file specified in the command line. +defined in latter configuration files override parameters with the same name +defined in the previous configuration files specified in the command line earlier. For example, you can make a separate configuration file with your key and secrete for the Exchange you use for trading, specify default configuration file with diff --git a/docs/data-analysis.md b/docs/data-analysis.md index ecd94445b..c89353cc8 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -31,6 +31,16 @@ df = load_trades_from_db("sqlite:///tradesv3.sqlite") df.groupby("pair")["sell_reason"].value_counts() ``` +### Load multiple configuration files + +This option can be usefull to inspect the results of passing in multiple configs in case of problems + +``` python +from freqtrade.configuration import Configuration +config = Configuration.from_files(["config1.json", "config2.json"]) +print(config) +``` + ## Strategy debugging example Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data. diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index e564c79ce..237346e37 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -4,7 +4,7 @@ This module contains the configuration class import logging import warnings from argparse import Namespace -from typing import Any, Callable, Dict, Optional +from typing import Any, Callable, Dict, List, Optional from freqtrade import OperationalException, constants from freqtrade.configuration.check_exchange import check_exchange @@ -39,43 +39,43 @@ class Configuration(object): return self.config - def _load_config_files(self) -> Dict[str, Any]: + @staticmethod + def from_files(files: List[str]) -> Dict[str, Any]: """ - Iterate through the config files passed in the args, - loading all of them and merging their contents. + Iterate through the config files passed in, loading all of them + and merging their contents. + Files are loaded in sequence, parameters in later configuration files + override the same parameter from an earlier file (last definition wins). + :param files: List of file paths + :return: configuration dictionary """ + # Keep this method as staticmethod, so it can be used from interactive environments config: Dict[str, Any] = {} # We expect here a list of config filenames - for path in self.args.config: - logger.info('Using config: %s ...', path) + for path in files: + logger.info(f'Using config: {path} ...') # Merge config options, overwriting old values config = deep_merge_dicts(load_config_file(path), config) - return config - - def _normalize_config(self, config: Dict[str, Any]) -> None: - """ - Make config more canonical -- i.e. for example add missing parts that we expect - to be normally in it... - """ + # Normalize config if 'internals' not in config: config['internals'] = {} + # validate configuration before returning + logger.info('Validating configuration ...') + validate_config_schema(config) + + return config + def load_config(self) -> Dict[str, Any]: """ Extract information for sys.argv and load the bot configuration :return: Configuration dictionary """ # Load all configs - config: Dict[str, Any] = self._load_config_files() - - # Make resulting config more canonical - self._normalize_config(config) - - logger.info('Validating configuration ...') - validate_config_schema(config) + config: Dict[str, Any] = Configuration.from_files(self.args.config) self._validate_config_consistency(config) diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index e325a0de2..667bd042e 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -133,6 +133,35 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None: assert log_has('Validating configuration ...', caplog.record_tuples) +def test_from_config(default_conf, mocker, caplog) -> None: + conf1 = deepcopy(default_conf) + conf2 = deepcopy(default_conf) + del conf1['exchange']['key'] + del conf1['exchange']['secret'] + del conf2['exchange']['name'] + conf2['exchange']['pair_whitelist'] += ['NANO/BTC'] + conf2['fiat_display_currency'] = "EUR" + config_files = [conf1, conf2] + + configsmock = MagicMock(side_effect=config_files) + mocker.patch( + 'freqtrade.configuration.configuration.load_config_file', + configsmock + ) + + validated_conf = Configuration.from_files(['test_conf.json', 'test2_conf.json']) + + exchange_conf = default_conf['exchange'] + assert validated_conf['exchange']['name'] == exchange_conf['name'] + assert validated_conf['exchange']['key'] == exchange_conf['key'] + assert validated_conf['exchange']['secret'] == exchange_conf['secret'] + assert validated_conf['exchange']['pair_whitelist'] != conf1['exchange']['pair_whitelist'] + assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist'] + assert validated_conf['fiat_display_currency'] == "EUR" + assert 'internals' in validated_conf + assert log_has('Validating configuration ...', caplog.record_tuples) + + def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None: default_conf['max_open_trades'] = -1 patched_configuration_load_config_file(mocker, default_conf)