Merge pull request #2118 from freqtrade/config_standalone
Config standalone loading
This commit is contained in:
commit
615ce6aa69
@ -56,8 +56,8 @@ freqtrade -c path/far/far/away/config.json
|
|||||||
|
|
||||||
The bot allows you to use multiple configuration files by specifying multiple
|
The bot allows you to use multiple configuration files by specifying multiple
|
||||||
`-c/--config` configuration options in the command line. Configuration parameters
|
`-c/--config` configuration options in the command line. Configuration parameters
|
||||||
defined in the last configuration file override parameters with the same name
|
defined in latter configuration files override parameters with the same name
|
||||||
defined in the previous configuration file specified in the command line.
|
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 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
|
for the Exchange you use for trading, specify default configuration file with
|
||||||
|
@ -31,6 +31,16 @@ df = load_trades_from_db("sqlite:///tradesv3.sqlite")
|
|||||||
df.groupby("pair")["sell_reason"].value_counts()
|
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
|
## Strategy debugging example
|
||||||
|
|
||||||
Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data.
|
Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data.
|
||||||
|
@ -4,7 +4,7 @@ This module contains the configuration class
|
|||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
from argparse import Namespace
|
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 import OperationalException, constants
|
||||||
from freqtrade.configuration.check_exchange import check_exchange
|
from freqtrade.configuration.check_exchange import check_exchange
|
||||||
@ -39,43 +39,43 @@ class Configuration(object):
|
|||||||
|
|
||||||
return self.config
|
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,
|
Iterate through the config files passed in, loading all of them
|
||||||
loading all of them and merging their contents.
|
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] = {}
|
config: Dict[str, Any] = {}
|
||||||
|
|
||||||
# We expect here a list of config filenames
|
# We expect here a list of config filenames
|
||||||
for path in self.args.config:
|
for path in files:
|
||||||
logger.info('Using config: %s ...', path)
|
logger.info(f'Using config: {path} ...')
|
||||||
|
|
||||||
# Merge config options, overwriting old values
|
# Merge config options, overwriting old values
|
||||||
config = deep_merge_dicts(load_config_file(path), config)
|
config = deep_merge_dicts(load_config_file(path), config)
|
||||||
|
|
||||||
return config
|
# Normalize 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...
|
|
||||||
"""
|
|
||||||
if 'internals' not in config:
|
if 'internals' not in config:
|
||||||
config['internals'] = {}
|
config['internals'] = {}
|
||||||
|
|
||||||
|
# validate configuration before returning
|
||||||
|
logger.info('Validating configuration ...')
|
||||||
|
validate_config_schema(config)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
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
|
||||||
"""
|
"""
|
||||||
# Load all configs
|
# Load all configs
|
||||||
config: Dict[str, Any] = self._load_config_files()
|
config: Dict[str, Any] = Configuration.from_files(self.args.config)
|
||||||
|
|
||||||
# Make resulting config more canonical
|
|
||||||
self._normalize_config(config)
|
|
||||||
|
|
||||||
logger.info('Validating configuration ...')
|
|
||||||
validate_config_schema(config)
|
|
||||||
|
|
||||||
self._validate_config_consistency(config)
|
self._validate_config_consistency(config)
|
||||||
|
|
||||||
|
@ -133,6 +133,35 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
|
|||||||
assert log_has('Validating configuration ...', caplog.record_tuples)
|
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:
|
def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None:
|
||||||
default_conf['max_open_trades'] = -1
|
default_conf['max_open_trades'] = -1
|
||||||
patched_configuration_load_config_file(mocker, default_conf)
|
patched_configuration_load_config_file(mocker, default_conf)
|
||||||
|
Loading…
Reference in New Issue
Block a user