From 32dee81195f928d331f722a9a206db454f6dfb6f Mon Sep 17 00:00:00 2001 From: Florian Merz Date: Sun, 14 Feb 2021 14:37:48 +0100 Subject: [PATCH] config - add argument 'strategy_params' --- config_full.json.example | 1 + docs/backtesting.md | 6 ++-- docs/bot-usage.md | 17 ++++++++--- docs/configuration.md | 1 + docs/edge.md | 3 ++ docs/hyperopt.md | 3 ++ docs/plotting.md | 10 +++++-- freqtrade/commands/arguments.py | 2 +- freqtrade/commands/cli_options.py | 5 ++++ freqtrade/configuration/configuration.py | 38 +++++++++++++++++++++--- tests/test_arguments.py | 12 ++++++++ tests/test_configuration.py | 32 ++++++++++++++++++++ 12 files changed, 117 insertions(+), 13 deletions(-) diff --git a/config_full.json.example b/config_full.json.example index 6593750b4..17d1abbe3 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -188,6 +188,7 @@ "disable_dataframe_checks": false, "strategy": "DefaultStrategy", "strategy_path": "user_data/strategies/", + "strategy_param": "{'param1': 5, 'param2': 'something'}", "dataformat_ohlcv": "json", "dataformat_trades": "jsongz" } diff --git a/docs/backtesting.md b/docs/backtesting.md index a14c8f2e4..d6e3fc49a 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -10,8 +10,8 @@ To learn how to get data for the pairs and exchange you're interested in, head o ``` usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] [-i TIMEFRAME] - [--timerange TIMERANGE] + [--strategy-path PATH] [--strategy-params JSON] + [-i TIMEFRAME] [--timerange TIMERANGE] [--data-format-ohlcv {json,jsongz,hdf5}] [--max-open-trades INT] [--stake-amount STAKE_AMOUNT] [--fee FLOAT] @@ -85,6 +85,8 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. + --strategy-params JSON + Specify additional strategy parameters. ``` diff --git a/docs/bot-usage.md b/docs/bot-usage.md index c7fe8634d..fdff76e9e 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -54,7 +54,7 @@ optional arguments: ``` usage: freqtrade trade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] [-s NAME] [--strategy-path PATH] + [--userdir PATH] [-s NAME] [--strategy-path PATH] [--strategy-params JSON] [--db-url PATH] [--sd-notify] [--dry-run] optional arguments: @@ -85,9 +85,10 @@ Common arguments: Strategy arguments: -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --strategy-params JSON Specify additional strategy params. ``` @@ -189,6 +190,14 @@ checked before the default locations (The passed path must be a directory!): freqtrade trade --strategy AwesomeStrategy --strategy-path /some/directory ``` +### How to use **--strategy-params**? + +This parameter allows you to add parameters of your strategy: + +```bash +freqtrade trade --strategy AwesomeStrategy --strategy-params "{'param1': 5, 'param2': 'something'}" +``` + #### How to install a strategy? This is very simple. Copy paste your strategy file into the directory diff --git a/docs/configuration.md b/docs/configuration.md index 00d2830e4..97bbe9c55 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -117,6 +117,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `disable_dataframe_checks` | Disable checking the OHLCV dataframe returned from the strategy methods for correctness. Only use when intentionally changing the dataframe and understand what you are doing. [Strategy Override](#parameters-in-the-strategy).
*Defaults to `False`*.
**Datatype:** Boolean | `strategy` | **Required** Defines Strategy class to use. Recommended to be set via `--strategy NAME`.
**Datatype:** ClassName | `strategy_path` | Adds an additional strategy lookup path (must be a directory).
**Datatype:** String +| `strategy_params` | Adds an additional strategy parameters.
**Datatype:** JSON | `internals.process_throttle_secs` | Set the process throttle, or minimum loop duration for one bot iteration loop. Value in second.
*Defaults to `5` seconds.*
**Datatype:** Positive Integer | `internals.heartbeat_interval` | Print heartbeat message every N seconds. Set to 0 to disable heartbeat messages.
*Defaults to `60` seconds.*
**Datatype:** Positive Integer or 0 | `internals.sd_notify` | Enables use of the sd_notify protocol to tell systemd service manager about changes in the bot state and issue keep-alive pings. See [here](installation.md#7-optional-configure-freqtrade-as-a-systemd-service) for more details.
**Datatype:** Boolean diff --git a/docs/edge.md b/docs/edge.md index 5565ca2f9..56c65e55f 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -214,6 +214,7 @@ Let's say the stake currency is **ETH** and there is $10$ **ETH** on the wallet. ``` usage: freqtrade edge [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] [--strategy-path PATH] + [--strategy-params JSON] [-i TIMEFRAME] [--timerange TIMERANGE] [--max-open-trades INT] [--stake-amount STAKE_AMOUNT] [--fee FLOAT] [--stoplosses STOPLOSS_RANGE] @@ -260,6 +261,8 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. + --strategy-params JSON + Specify additional strategy parameters. ``` diff --git a/docs/hyperopt.md b/docs/hyperopt.md index ec155062f..edc3b3ff1 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -38,6 +38,7 @@ pip install -r requirements-hyperopt.txt ``` usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] [--strategy-path PATH] + [--strategy-params JSON] [-i TIMEFRAME] [--timerange TIMERANGE] [--data-format-ohlcv {json,jsongz,hdf5}] [--max-open-trades INT] @@ -130,6 +131,8 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. + --strategy-params JSON + Specify additional strategy parameters. ``` diff --git a/docs/plotting.md b/docs/plotting.md index 19ddb4f57..870baa894 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -25,7 +25,8 @@ Possible arguments: ``` usage: freqtrade plot-dataframe [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] [-p PAIRS [PAIRS ...]] + [--strategy-path PATH] [--strategy-params JSON] + [-p PAIRS [PAIRS ...]] [--indicators1 INDICATORS1 [INDICATORS1 ...]] [--indicators2 INDICATORS2 [INDICATORS2 ...]] [--plot-limit INT] [--db-url PATH] @@ -91,6 +92,8 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. + --strategy-params JSON + Specify additional strategy parameters. ``` Example: @@ -237,7 +240,8 @@ Possible options for the `freqtrade plot-profit` subcommand: ``` usage: freqtrade plot-profit [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] [-p PAIRS [PAIRS ...]] + [--strategy-path PATH] [--strategy-params JSON] + [-p PAIRS [PAIRS ...]] [--timerange TIMERANGE] [--export EXPORT] [--export-filename PATH] [--db-url PATH] [--trade-source {DB,file}] [-i TIMEFRAME] @@ -288,6 +292,8 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. + --strategy-params JSON + Specify additional strategy parameters. ``` The `-p/--pairs` argument, can be used to limit the pairs that are considered for this calculation. diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index c64c11a18..d9ce72140 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -12,7 +12,7 @@ from freqtrade.constants import DEFAULT_CONFIG ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"] -ARGS_STRATEGY = ["strategy", "strategy_path"] +ARGS_STRATEGY = ["strategy", "strategy_path", "strategy_params"] ARGS_TRADE = ["db_url", "sd_notify", "dry_run"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 7dc85377d..89ddc5d23 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -93,6 +93,11 @@ AVAILABLE_CLI_OPTIONS = { help='Specify additional strategy lookup path.', metavar='PATH', ), + "strategy_params": Arg( + '--strategy-params', + help='Specify additional strategy parameters.', + metavar='JSON', + ), "db_url": Arg( '--db-url', help=f'Override trades database URL, this is useful in custom deployments ' diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 7bf3e6bf2..e4b65e772 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -1,6 +1,7 @@ """ This module contains the configuration class """ +import json import logging import warnings from copy import deepcopy @@ -155,6 +156,9 @@ class Configuration: self._args_to_config(config, argname='strategy_path', logstring='Using additional Strategy lookup path: {}') + self._args_to_config_from_json(config, argname='strategy_params', + logstring='Using additional Strategy params: {}') + if ('db_url' in self.args and self.args['db_url'] and self.args['db_url'] != constants.DEFAULT_DB_PROD_URL): config.update({'db_url': self.args['db_url']}) @@ -403,9 +407,9 @@ class Configuration: config.update({'runmode': self.runmode}) - def _args_to_config(self, config: Dict[str, Any], argname: str, - logstring: str, logfun: Optional[Callable] = None, - deprecated_msg: Optional[str] = None) -> None: + def _args_to_config_from_json(self, config: Dict[str, Any], argname: str, + logstring: str, logfun: Optional[Callable] = None, + deprecated_msg: Optional[str] = None) -> None: """ :param config: Configuration dictionary :param argname: Argumentname in self.args - will be copied to config dict. @@ -415,10 +419,36 @@ class Configuration: sample: logfun=len (prints the length of the found configuration instead of the content) """ + def parsejson(argvalue): + return json.loads(argvalue.replace('\\\'', '\"')) + + self._args_to_config( + config=config, + argname=argname, + logstring=logstring, + logfun=logfun, + deprecated_msg=deprecated_msg, + parsefun=parsejson + ) + + def _args_to_config(self, config: Dict[str, Any], argname: str, + logstring: str, logfun: Optional[Callable] = None, + deprecated_msg: Optional[str] = None, + parsefun: Callable = lambda x: x) -> None: + """ + :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) + :param parsefun: parsefun is applied to the argvalue before updating the configuration + """ if (argname in self.args and self.args[argname] is not None and self.args[argname] is not False): - config.update({argname: self.args[argname]}) + config.update({argname: parsefun(self.args[argname])}) if logfun: logger.info(logstring.format(logfun(config[argname]))) else: diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 60c2cfbac..2aea7baa4 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -109,6 +109,18 @@ def test_parse_args_strategy_path_invalid() -> None: Arguments(['--strategy-path']).get_parsed_arg() +def test_parse_args_strategy_params() -> None: + args = Arguments([ + 'trade', '--strategy-params', '{"param1": 5, "param2": "something"}' + ]).get_parsed_arg() + assert args['strategy_params'] == '{"param1": 5, "param2": "something"}' + + +def test_parse_args_strategy_params_invalid() -> None: + with pytest.raises(SystemExit, match=r'2'): + Arguments(['--strategy-params']).get_parsed_arg() + + def test_parse_args_backtesting_invalid() -> None: with pytest.raises(SystemExit, match=r'2'): Arguments(['backtesting --ticker-interval']).get_parsed_arg() diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 94c3e24f6..015ce4443 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -117,6 +117,38 @@ def test__args_to_config(caplog): assert config['strategy_path'] == "TestTest" +def test__args_to_config_from_json(caplog): + + arg_list = ['trade', '--strategy-params', '{"param1": 5, "param2": "something"}'] + args = Arguments(arg_list).get_parsed_arg() + configuration = Configuration(args) + config = {} + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + # No warnings ... + configuration._args_to_config_from_json(config, argname="strategy_params", + logstring="DeadBeef") + assert len(w) == 0 + assert log_has("DeadBeef", caplog) + assert config['strategy_params']['param1'] == 5 + assert config['strategy_params']['param2'] == 'something' + + configuration = Configuration(args) + config = {} + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + # Deprecation warnings! + configuration._args_to_config_from_json(config, argname="strategy_params", + logstring="DeadBeef", + deprecated_msg="Going away soon!") + assert len(w) == 1 + assert issubclass(w[-1].category, DeprecationWarning) + assert "DEPRECATED: Going away soon!" in str(w[-1].message) + assert log_has("DeadBeef", caplog) + assert config['strategy_params']['param1'] == 5 + assert config['strategy_params']['param2'] == 'something' + + def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None: default_conf['max_open_trades'] = 0 patched_configuration_load_config_file(mocker, default_conf)