add "cfg" argument for overriding any config from command line
This commit is contained in:
parent
5f6eae52a2
commit
79e70016e0
@ -10,7 +10,7 @@ from freqtrade.commands.cli_options import AVAILABLE_CLI_OPTIONS
|
|||||||
from freqtrade.constants import DEFAULT_CONFIG
|
from freqtrade.constants import DEFAULT_CONFIG
|
||||||
|
|
||||||
|
|
||||||
ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"]
|
ARGS_COMMON = ["verbosity", "logfile", "version", "config", "cfg", "datadir", "user_data_dir"]
|
||||||
|
|
||||||
ARGS_STRATEGY = ["strategy", "strategy_path"]
|
ARGS_STRATEGY = ["strategy", "strategy_path"]
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Definition of cli arguments used in arguments.py
|
Definition of cli arguments used in arguments.py
|
||||||
"""
|
"""
|
||||||
|
import argparse
|
||||||
from argparse import ArgumentTypeError
|
from argparse import ArgumentTypeError
|
||||||
|
|
||||||
from freqtrade import __version__, constants
|
from freqtrade import __version__, constants
|
||||||
@ -38,6 +39,50 @@ class Arg:
|
|||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class SetDictFromArgAction(argparse.Action):
|
||||||
|
"""
|
||||||
|
argparse action to split an argument into KEY=VALUE form
|
||||||
|
on the first = and append to a dictionary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __call__(self, parser, args, values, option_string=None):
|
||||||
|
for key_value in values:
|
||||||
|
try:
|
||||||
|
(k, v) = key_value.split("=", 2)
|
||||||
|
except ValueError as ex:
|
||||||
|
raise argparse.ArgumentError(self, f"could not parse argument \"{values[0]}\" as k=v format")
|
||||||
|
d = self.set_dict_path_value(getattr(args, self.dest) or {}, k, v)
|
||||||
|
setattr(args, self.dest, d)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_dict_path_value(obj, key, value):
|
||||||
|
root_obj = obj
|
||||||
|
keys = key.split('.')
|
||||||
|
latest = keys.pop()
|
||||||
|
for k in keys:
|
||||||
|
obj[k] = obj.get(k, {})
|
||||||
|
obj = obj[k]
|
||||||
|
latest_key, *value_type = latest.split(':')
|
||||||
|
value_type = value_type[0] if len(value_type) > 0 else None
|
||||||
|
if value_type is None or value_type == "str":
|
||||||
|
value = value
|
||||||
|
elif value_type == "int":
|
||||||
|
value = int(value)
|
||||||
|
elif value_type == "float":
|
||||||
|
value = float(value)
|
||||||
|
elif value_type == "bool":
|
||||||
|
if value == "True" or value == "true" or value == "1":
|
||||||
|
value = True
|
||||||
|
elif value == "False" or value == "false" or value == "0":
|
||||||
|
value = False
|
||||||
|
else:
|
||||||
|
raise ArgumentTypeError(f"Argument '{key}' has unknown value '{value}'. Must be (true|false|True|False|1|0)")
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unsupported arg type '{value_type}'")
|
||||||
|
obj[latest_key] = value
|
||||||
|
return root_obj
|
||||||
|
|
||||||
|
|
||||||
# List of available command line options
|
# List of available command line options
|
||||||
AVAILABLE_CLI_OPTIONS = {
|
AVAILABLE_CLI_OPTIONS = {
|
||||||
# Common options
|
# Common options
|
||||||
@ -530,4 +575,12 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
help='Do not print epoch details header.',
|
help='Do not print epoch details header.',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
),
|
),
|
||||||
}
|
"cfg": Arg(
|
||||||
|
'--cfg',
|
||||||
|
nargs='+',
|
||||||
|
metavar="KEY=VALUE",
|
||||||
|
action=SetDictFromArgAction,
|
||||||
|
help='Set any config value.',
|
||||||
|
# action='store_const'
|
||||||
|
),
|
||||||
|
}
|
@ -93,6 +93,9 @@ class Configuration:
|
|||||||
# Load all configs
|
# Load all configs
|
||||||
config: Dict[str, Any] = self.load_from_files(self.args.get("config", []))
|
config: Dict[str, Any] = self.load_from_files(self.args.get("config", []))
|
||||||
|
|
||||||
|
if 'cfg' in self.args:
|
||||||
|
config = deep_merge_dicts(self.args['cfg'] or {}, config)
|
||||||
|
|
||||||
# Keep a copy of the original configuration file
|
# Keep a copy of the original configuration file
|
||||||
config['original_config'] = deepcopy(config)
|
config['original_config'] = deepcopy(config)
|
||||||
|
|
||||||
|
@ -35,6 +35,30 @@ def test_setup_utils_configuration():
|
|||||||
assert config['exchange']['secret'] == ''
|
assert config['exchange']['secret'] == ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_cfg_argument_replaces_configuration():
|
||||||
|
args = [
|
||||||
|
'list-exchanges', '--config', 'config_bittrex.json.example',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Check original config
|
||||||
|
config = setup_utils_configuration(get_args(args), RunMode.OTHER)
|
||||||
|
assert config['dry_run'] is True
|
||||||
|
assert config['exchange']['key'] == ''
|
||||||
|
assert config['stake_amount'] == 0.05
|
||||||
|
assert config['timeframe'] == '5m'
|
||||||
|
|
||||||
|
args = [
|
||||||
|
'list-exchanges', '--config', 'config_bittrex.json.example',
|
||||||
|
'--cfg', 'stake_amount:float=0.1', 'timeframe=15m', 'exchange.sandbox:bool=true',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Check original config
|
||||||
|
config = setup_utils_configuration(get_args(args), RunMode.OTHER)
|
||||||
|
assert config['stake_amount'] == 0.1
|
||||||
|
assert config['timeframe'] == '15m'
|
||||||
|
assert config['exchange']['sandbox'] is True
|
||||||
|
|
||||||
|
|
||||||
def test_start_trading_fail(mocker, caplog):
|
def test_start_trading_fail(mocker, caplog):
|
||||||
|
|
||||||
mocker.patch("freqtrade.worker.Worker.run", MagicMock(side_effect=OperationalException))
|
mocker.patch("freqtrade.worker.Worker.run", MagicMock(side_effect=OperationalException))
|
||||||
|
Loading…
Reference in New Issue
Block a user