From cd2336887cc74b6a518d84f32d7aa6f1691b4824 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Sep 2019 17:14:04 +0200 Subject: [PATCH 01/30] Add first version with shared parent parsers --- freqtrade/configuration/arguments.py | 65 ++++++++++++++++------------ 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 6e2ecea2e..124bf3b75 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -12,7 +12,7 @@ ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_dat ARGS_STRATEGY = ["strategy", "strategy_path"] -ARGS_MAIN = ARGS_COMMON + ARGS_STRATEGY + ["db_url", "sd_notify"] +ARGS_MAIN = ARGS_STRATEGY + ["db_url", "sd_notify"] ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange", "max_open_trades", "stake_amount"] @@ -51,11 +51,6 @@ class Arguments: def __init__(self, args: Optional[List[str]]) -> None: self.args = args self._parsed_arg: Optional[argparse.Namespace] = None - self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') - - def _load_args(self) -> None: - self._build_args(optionlist=ARGS_MAIN) - self._build_subcommands() def get_parsed_arg(self) -> Dict[str, Any]: """ @@ -63,7 +58,7 @@ class Arguments: :return: List[str] List of arguments """ if self._parsed_arg is None: - self._load_args() + self._build_subcommands() self._parsed_arg = self._parse_args() return vars(self._parsed_arg) @@ -86,7 +81,6 @@ class Arguments: return parsed_arg def _build_args(self, optionlist, parser=None): - parser = parser or self.parser for val in optionlist: opt = AVAILABLE_CLI_OPTIONS[val] @@ -97,61 +91,76 @@ class Arguments: Builds and attaches all subcommands. :return: None """ + # Build shared arguments (as group Common Options) + _common_parser = argparse.ArgumentParser(add_help=False) + group = _common_parser.add_argument_group("Common Options") + self._build_args(optionlist=ARGS_COMMON, parser=group) + + # Build main command + self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot', + parents=[_common_parser]) + self._build_args(optionlist=ARGS_MAIN, parser=self.parser) + from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge from freqtrade.utils import start_create_userdir, start_download_data, start_list_exchanges subparsers = self.parser.add_subparsers(dest='subparser') # Add backtesting subcommand - backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.') + backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.', + parents=[_common_parser]) backtesting_cmd.set_defaults(func=start_backtesting) self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd) # Add edge subcommand - edge_cmd = subparsers.add_parser('edge', help='Edge module.') + edge_cmd = subparsers.add_parser('edge', help='Edge module.', parents=[_common_parser]) edge_cmd.set_defaults(func=start_edge) self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd) # Add hyperopt subcommand - hyperopt_cmd = subparsers.add_parser('hyperopt', help='Hyperopt module.') + hyperopt_cmd = subparsers.add_parser('hyperopt', help='Hyperopt module.', + parents=[_common_parser], + ) hyperopt_cmd.set_defaults(func=start_hyperopt) self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd) # add create-userdir subcommand create_userdir_cmd = subparsers.add_parser('create-userdir', - help="Create user-data directory.") + help="Create user-data directory.", + + ) create_userdir_cmd.set_defaults(func=start_create_userdir) self._build_args(optionlist=ARGS_CREATE_USERDIR, parser=create_userdir_cmd) # Add list-exchanges subcommand - list_exchanges_cmd = subparsers.add_parser( - 'list-exchanges', - help='Print available exchanges.' - ) + list_exchanges_cmd = subparsers.add_parser('list-exchanges', + help='Print available exchanges.', + parents=[_common_parser], + ) list_exchanges_cmd.set_defaults(func=start_list_exchanges) self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd) # Add download-data subcommand - download_data_cmd = subparsers.add_parser( - 'download-data', - help='Download backtesting data.' - ) + download_data_cmd = subparsers.add_parser('download-data', + help='Download backtesting data.', + parents=[_common_parser], + ) download_data_cmd.set_defaults(func=start_download_data) self._build_args(optionlist=ARGS_DOWNLOAD_DATA, parser=download_data_cmd) # Add Plotting subcommand from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit - plot_dataframe_cmd = subparsers.add_parser( - 'plot-dataframe', - help='Plot candles with indicators.' - ) + plot_dataframe_cmd = subparsers.add_parser('plot-dataframe', + help='Plot candles with indicators.', + parents=[_common_parser], + ) plot_dataframe_cmd.set_defaults(func=start_plot_dataframe) self._build_args(optionlist=ARGS_PLOT_DATAFRAME, parser=plot_dataframe_cmd) # Plot profit - plot_profit_cmd = subparsers.add_parser( - 'plot-profit', - help='Generate plot showing profits.' - ) + plot_profit_cmd = subparsers.add_parser('plot-profit', + help='Generate plot showing profits.', + parents=[_common_parser], + ) plot_profit_cmd.set_defaults(func=start_plot_profit) self._build_args(optionlist=ARGS_PLOT_PROFIT, parser=plot_profit_cmd) From 2a535b72ff6e79b9bc5c218ce97d4b7ec40aede1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 11:15:51 +0200 Subject: [PATCH 02/30] Parser should not have default --- freqtrade/configuration/arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 124bf3b75..d26d64c40 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -80,7 +80,7 @@ class Arguments: return parsed_arg - def _build_args(self, optionlist, parser=None): + def _build_args(self, optionlist, parser): for val in optionlist: opt = AVAILABLE_CLI_OPTIONS[val] From cb37f43277bb4d3f81d867888ba7bd63550c2df5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 11:16:14 +0200 Subject: [PATCH 03/30] Add trade subparser (and make subparser a requirement) --- freqtrade/configuration/arguments.py | 14 ++++++++++---- freqtrade/utils.py | 15 +++++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index d26d64c40..ff28a6406 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -99,12 +99,19 @@ class Arguments: # Build main command self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot', parents=[_common_parser]) - self._build_args(optionlist=ARGS_MAIN, parser=self.parser) from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge - from freqtrade.utils import start_create_userdir, start_download_data, start_list_exchanges + from freqtrade.utils import (start_create_userdir, start_download_data, + start_list_exchanges, start_trading) + from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit - subparsers = self.parser.add_subparsers(dest='subparser') + subparsers = self.parser.add_subparsers(dest='subparser', required=True) + + # Add trade subcommand + trade_cmd = subparsers.add_parser('trade', help='Trade module.', + parents=[_common_parser]) + trade_cmd.set_defaults(func=start_trading) + self._build_args(optionlist=ARGS_MAIN, parser=trade_cmd) # Add backtesting subcommand backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.', @@ -149,7 +156,6 @@ class Arguments: self._build_args(optionlist=ARGS_DOWNLOAD_DATA, parser=download_data_cmd) # Add Plotting subcommand - from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit plot_dataframe_cmd = subparsers.add_parser('plot-dataframe', help='Plot candles with indicators.', parents=[_common_parser], diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 6ce5e888c..8e57606da 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -33,6 +33,17 @@ def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str return config +def start_trading(args: Dict[str, Any]) -> int: + """ + Main entry point for trading mode + """ + from freqtrade.worker import Worker + # Load and run worker + worker = Worker(args) + worker.run() + return 0 + + def start_list_exchanges(args: Dict[str, Any]) -> None: """ Print available exchanges @@ -47,7 +58,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: f"{', '.join(available_exchanges())}") -def start_create_userdir(args: Dict[str, Any]) -> None: +def start_create_userdir(args: Dict[str, Any]) -> int: """ Create "user_data" directory to contain user data strategies, hyperopts, ...) :param args: Cli args from Arguments() @@ -57,7 +68,7 @@ def start_create_userdir(args: Dict[str, Any]) -> None: create_userdata_dir(args["user_data_dir"], create_dir=True) else: logger.warning("`create-userdir` requires --userdir to be set.") - sys.exit(1) + return 1 def start_download_data(args: Dict[str, Any]) -> None: From 8664e7f7d35d85dae15862d6d42bf292446bd198 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 11:19:08 +0200 Subject: [PATCH 04/30] Have main.py support only subcommand mode --- freqtrade/main.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index 4d6f0dce7..543aab169 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -15,7 +15,6 @@ from typing import Any, List from freqtrade import OperationalException from freqtrade.configuration import Arguments -from freqtrade.worker import Worker logger = logging.getLogger('freqtrade') @@ -33,16 +32,9 @@ def main(sysargv: List[str] = None) -> None: arguments = Arguments(sysargv) args = arguments.get_parsed_arg() - # A subcommand has been issued. - # Means if Backtesting or Hyperopt have been called we exit the bot + # Call subcommand. if 'func' in args: - args['func'](args) - # TODO: fetch return_code as returned by the command function here - return_code = 0 - else: - # Load and run worker - worker = Worker(args) - worker.run() + return_code = args['func'](args) except SystemExit as e: return_code = e From 0f2e277f80b68a82272e62348f59a17418894c9c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 11:19:42 +0200 Subject: [PATCH 05/30] Rename subparser variable to command --- freqtrade/configuration/arguments.py | 2 +- tests/test_arguments.py | 4 ++-- tests/test_main.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index ff28a6406..8f6924032 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -105,7 +105,7 @@ class Arguments: start_list_exchanges, start_trading) from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit - subparsers = self.parser.add_subparsers(dest='subparser', required=True) + subparsers = self.parser.add_subparsers(dest='command', required=True) # Add trade subcommand trade_cmd = subparsers.add_parser('trade', help='Trade module.', diff --git a/tests/test_arguments.py b/tests/test_arguments.py index cf0104c01..1b04d9419 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -106,7 +106,7 @@ def test_parse_args_backtesting_custom() -> None: call_args = Arguments(args).get_parsed_arg() assert call_args["config"] == ['test_conf.json'] assert call_args["verbosity"] == 0 - assert call_args["subparser"] == 'backtesting' + assert call_args["command"] == 'backtesting' assert call_args["func"] is not None assert call_args["ticker_interval"] == '1m' assert type(call_args["strategy_list"]) is list @@ -124,7 +124,7 @@ def test_parse_args_hyperopt_custom() -> None: assert call_args["config"] == ['test_conf.json'] assert call_args["epochs"] == 20 assert call_args["verbosity"] == 0 - assert call_args["subparser"] == 'hyperopt' + assert call_args["command"] == 'hyperopt' assert call_args["spaces"] == ['buy'] assert call_args["func"] is not None assert callable(call_args["func"]) diff --git a/tests/test_main.py b/tests/test_main.py index d73edc0da..10d7d3216 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -29,7 +29,7 @@ def test_parse_args_backtesting(mocker) -> None: call_args = backtesting_mock.call_args[0][0] assert call_args["config"] == ['config.json'] assert call_args["verbosity"] == 0 - assert call_args["subparser"] == 'backtesting' + assert call_args["command"] == 'backtesting' assert call_args["func"] is not None assert callable(call_args["func"]) assert call_args["ticker_interval"] is None @@ -45,7 +45,7 @@ def test_main_start_hyperopt(mocker) -> None: call_args = hyperopt_mock.call_args[0][0] assert call_args["config"] == ['config.json'] assert call_args["verbosity"] == 0 - assert call_args["subparser"] == 'hyperopt' + assert call_args["command"] == 'hyperopt' assert call_args["func"] is not None assert callable(call_args["func"]) From 03add90c9494e0a848b73b31e73ecaa05763cfcd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 13:18:52 +0200 Subject: [PATCH 06/30] Adjust some tests to new call-method --- tests/optimize/test_backtesting.py | 12 ++++++------ tests/test_main.py | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index fa40809d8..1a52f851e 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -169,9 +169,9 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'backtesting' ] config = setup_configuration(get_args(args), RunMode.BACKTEST) @@ -202,10 +202,10 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> ) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', '/foo/bar', - 'backtesting', '--ticker-interval', '1m', '--enable-position-stacking', '--disable-max-market-positions', @@ -250,9 +250,9 @@ def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'backtesting' ] with pytest.raises(DependencyException, match=r'.*stake amount.*'): @@ -267,9 +267,9 @@ def test_start(mocker, fee, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'backtesting' ] args = get_args(args) start_backtesting(args) @@ -812,10 +812,10 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir): patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', str(testdatadir), - 'backtesting', '--ticker-interval', '1m', '--timerange', '-100', '--enable-position-stacking', @@ -859,9 +859,9 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): patched_configuration_load_config_file(mocker, default_conf) args = [ + 'backtesting', '--config', 'config.json', '--datadir', str(testdatadir), - 'backtesting', '--ticker-interval', '1m', '--timerange', '-100', '--enable-position-stacking', diff --git a/tests/test_main.py b/tests/test_main.py index 10d7d3216..c5e1c8901 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -58,7 +58,7 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = ['-c', 'config.json.example'] + args = ['trade', '-c', 'config.json.example'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): @@ -75,7 +75,7 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = ['-c', 'config.json.example'] + args = ['trade', '-c', 'config.json.example'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): @@ -95,7 +95,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = ['-c', 'config.json.example'] + args = ['trade', '-c', 'config.json.example'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): @@ -114,15 +114,15 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None: OperationalException("Oh snap!")]) mocker.patch('freqtrade.worker.Worker._worker', worker_mock) patched_configuration_load_config_file(mocker, default_conf) - reconfigure_mock = mocker.patch('freqtrade.main.Worker._reconfigure', MagicMock()) + reconfigure_mock = mocker.patch('freqtrade.worker.Worker._reconfigure', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = Arguments(['-c', 'config.json.example']).get_parsed_arg() + args = Arguments(['trade', '-c', 'config.json.example']).get_parsed_arg() worker = Worker(args=args, config=default_conf) with pytest.raises(SystemExit): - main(['-c', 'config.json.example']) + main(['trade', '-c', 'config.json.example']) assert log_has('Using config: config.json.example ...', caplog) assert worker_mock.call_count == 4 @@ -141,7 +141,7 @@ def test_reconfigure(mocker, default_conf) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - args = Arguments(['-c', 'config.json.example']).get_parsed_arg() + args = Arguments(['trade', '-c', 'config.json.example']).get_parsed_arg() worker = Worker(args=args, config=default_conf) freqtrade = worker.freqtrade From 1b25b5f590e3d50678b853003c9ca06e77ef2a82 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 13:19:05 +0200 Subject: [PATCH 07/30] Remove duplicate short-form `-s` --- freqtrade/configuration/cli_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index cb07dbdba..24230f1b3 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -171,7 +171,7 @@ AVAILABLE_CLI_OPTIONS = { default=constants.HYPEROPT_EPOCH, ), "spaces": Arg( - '-s', '--spaces', + '--spaces', help='Specify which parameters to hyperopt. Space-separated list. ' 'Default: `%(default)s`.', choices=['all', 'buy', 'sell', 'roi', 'stoploss'], From d62a4d3566eee173e923f51d76cf1d2e5c0e914b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 13:19:21 +0200 Subject: [PATCH 08/30] Fix some minor problems --- freqtrade/configuration/arguments.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 8f6924032..d0db162b9 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -14,8 +14,8 @@ ARGS_STRATEGY = ["strategy", "strategy_path"] ARGS_MAIN = ARGS_STRATEGY + ["db_url", "sd_notify"] -ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange", - "max_open_trades", "stake_amount"] +ARGS_COMMON_OPTIMIZE = ARGS_STRATEGY + ["ticker_interval", "timerange", + "max_open_trades", "stake_amount"] ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", "strategy_list", "export", "exportfilename"] @@ -75,7 +75,7 @@ class Arguments: # (see https://bugs.python.org/issue16399) # Allow no-config for certain commands (like downloading / plotting) if (parsed_arg.config is None and ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() or - not ('subparser' in parsed_arg and parsed_arg.subparser in NO_CONF_REQURIED))): + not ('command' in parsed_arg and parsed_arg.command in NO_CONF_REQURIED))): parsed_arg.config = [constants.DEFAULT_CONFIG] return parsed_arg @@ -120,7 +120,8 @@ class Arguments: self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd) # Add edge subcommand - edge_cmd = subparsers.add_parser('edge', help='Edge module.', parents=[_common_parser]) + edge_cmd = subparsers.add_parser('edge', help='Edge module.', + parents=[_common_parser]) edge_cmd.set_defaults(func=start_edge) self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd) From db3b974479976273ba54ce919ed07521d3c1311c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 13:19:40 +0200 Subject: [PATCH 09/30] Fix calling sequence --- tests/optimize/test_edge_cli.py | 6 +++--- tests/test_configuration.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py index 97103da55..1d2faead0 100644 --- a/tests/optimize/test_edge_cli.py +++ b/tests/optimize/test_edge_cli.py @@ -15,9 +15,9 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> patched_configuration_load_config_file(mocker, default_conf) args = [ + 'edge', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'edge' ] config = setup_configuration(get_args(args), RunMode.EDGE) @@ -45,10 +45,10 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N ) args = [ + 'edge', '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', '/foo/bar', - 'edge', '--ticker-interval', '1m', '--timerange', ':100', '--stoplosses=-0.01,-0.10,-0.001' @@ -79,9 +79,9 @@ def test_start(mocker, fee, edge_conf, caplog) -> None: patched_configuration_load_config_file(mocker, edge_conf) args = [ + 'edge', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'edge' ] args = get_args(args) start_edge(args) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 330b82d39..522b1c1ee 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -806,8 +806,8 @@ def test_pairlist_resolving(): def test_pairlist_resolving_with_config(mocker, default_conf): patched_configuration_load_config_file(mocker, default_conf) arglist = [ - '--config', 'config.json', 'download-data', + '--config', 'config.json', ] args = Arguments(arglist).get_parsed_arg() @@ -820,8 +820,8 @@ def test_pairlist_resolving_with_config(mocker, default_conf): # Override pairs arglist = [ - '--config', 'config.json', 'download-data', + '--config', 'config.json', '--pairs', 'ETH/BTC', 'XRP/BTC', ] @@ -842,8 +842,8 @@ def test_pairlist_resolving_with_config_pl(mocker, default_conf): mocker.patch.object(Path, "open", MagicMock(return_value=MagicMock())) arglist = [ - '--config', 'config.json', 'download-data', + '--config', 'config.json', '--pairs-file', 'pairs.json', ] @@ -864,8 +864,8 @@ def test_pairlist_resolving_with_config_pl_not_exists(mocker, default_conf): mocker.patch.object(Path, "exists", MagicMock(return_value=False)) arglist = [ - '--config', 'config.json', 'download-data', + '--config', 'config.json', '--pairs-file', 'pairs.json', ] From e8106f379222e9353bdb93dcc1504cc8d5f2b809 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 13:38:26 +0200 Subject: [PATCH 10/30] Fix most tests to have trade as default argument --- tests/test_arguments.py | 22 +++++++++++----------- tests/test_configuration.py | 30 ++++++++++++++++++------------ tests/test_plotting.py | 4 ++-- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 1b04d9419..53602574e 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -25,27 +25,27 @@ def test_parse_args_defaults() -> None: def test_parse_args_config() -> None: - args = Arguments(['-c', '/dev/null']).get_parsed_arg() + args = Arguments(['trade', '-c', '/dev/null']).get_parsed_arg() assert args["config"] == ['/dev/null'] - args = Arguments(['--config', '/dev/null']).get_parsed_arg() + args = Arguments(['trade', '--config', '/dev/null']).get_parsed_arg() assert args["config"] == ['/dev/null'] - args = Arguments(['--config', '/dev/null', + args = Arguments(['trade', '--config', '/dev/null', '--config', '/dev/zero'],).get_parsed_arg() assert args["config"] == ['/dev/null', '/dev/zero'] def test_parse_args_db_url() -> None: - args = Arguments(['--db-url', 'sqlite:///test.sqlite']).get_parsed_arg() + args = Arguments(['trade', '--db-url', 'sqlite:///test.sqlite']).get_parsed_arg() assert args["db_url"] == 'sqlite:///test.sqlite' def test_parse_args_verbose() -> None: - args = Arguments(['-v']).get_parsed_arg() + args = Arguments(['trade', '-v']).get_parsed_arg() assert args["verbosity"] == 1 - args = Arguments(['--verbose']).get_parsed_arg() + args = Arguments(['trade', '--verbose']).get_parsed_arg() assert args["verbosity"] == 1 @@ -67,7 +67,7 @@ def test_parse_args_invalid() -> None: def test_parse_args_strategy() -> None: - args = Arguments(['--strategy', 'SomeStrategy']).get_parsed_arg() + args = Arguments(['trade', '--strategy', 'SomeStrategy']).get_parsed_arg() assert args["strategy"] == 'SomeStrategy' @@ -77,7 +77,7 @@ def test_parse_args_strategy_invalid() -> None: def test_parse_args_strategy_path() -> None: - args = Arguments(['--strategy-path', '/some/path']).get_parsed_arg() + args = Arguments(['trade', '--strategy-path', '/some/path']).get_parsed_arg() assert args["strategy_path"] == '/some/path' @@ -96,8 +96,8 @@ def test_parse_args_backtesting_invalid() -> None: def test_parse_args_backtesting_custom() -> None: args = [ - '-c', 'test_conf.json', 'backtesting', + '-c', 'test_conf.json', '--ticker-interval', '1m', '--strategy-list', 'DefaultStrategy', @@ -115,8 +115,8 @@ def test_parse_args_backtesting_custom() -> None: def test_parse_args_hyperopt_custom() -> None: args = [ - '-c', 'test_conf.json', 'hyperopt', + '-c', 'test_conf.json', '--epochs', '20', '--spaces', 'buy' ] @@ -132,8 +132,8 @@ def test_parse_args_hyperopt_custom() -> None: def test_download_data_options() -> None: args = [ - '--datadir', 'datadir/directory', 'download-data', + '--datadir', 'datadir/directory', '--pairs-file', 'file_with_pairs', '--days', '30', '--exchange', 'binance' diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 522b1c1ee..3c3ad3026 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -65,7 +65,7 @@ def test_load_config_file(default_conf, mocker, caplog) -> None: def test__args_to_config(caplog): - arg_list = ['--strategy-path', 'TestTest'] + arg_list = ['trade', '--strategy-path', 'TestTest'] args = Arguments(arg_list).get_parsed_arg() configuration = Configuration(args) config = {} @@ -93,7 +93,7 @@ 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) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -118,7 +118,7 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None: configsmock ) - arg_list = ['-c', 'test_conf.json', '--config', 'test2_conf.json', ] + arg_list = ['trade', '-c', 'test_conf.json', '--config', 'test2_conf.json', ] args = Arguments(arg_list).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -184,7 +184,7 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> default_conf['max_open_trades'] = -1 patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -208,7 +208,7 @@ def test_load_config_file_exception(mocker) -> None: def test_load_config(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -221,6 +221,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, default_conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path', '--db-url', 'sqlite:///someurl', @@ -240,6 +241,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path' ] @@ -256,6 +258,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path' ] @@ -272,6 +275,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path' ] @@ -290,6 +294,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: patched_configuration_load_config_file(mocker, conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--strategy-path', '/some/path' ] @@ -307,7 +312,7 @@ def test_load_custom_strategy(default_conf, mocker) -> None: }) patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() @@ -319,6 +324,7 @@ def test_show_info(default_conf, mocker, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) arglist = [ + 'trade', '--strategy', 'TestStrategy', '--db-url', 'sqlite:///tmp/testdb', ] @@ -335,9 +341,9 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> patched_configuration_load_config_file(mocker, default_conf) arglist = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', - 'backtesting' ] args = Arguments(arglist).get_parsed_arg() @@ -373,11 +379,11 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non lambda x, *args, **kwargs: Path(x) ) arglist = [ + 'backtesting', '--config', 'config.json', '--strategy', 'DefaultStrategy', '--datadir', '/foo/bar', '--userdir', "/tmp/freqtrade", - 'backtesting', '--ticker-interval', '1m', '--enable-position-stacking', '--disable-max-market-positions', @@ -424,8 +430,8 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non patched_configuration_load_config_file(mocker, default_conf) arglist = [ - '--config', 'config.json', 'backtesting', + '--config', 'config.json', '--ticker-interval', '1m', '--export', '/bar/foo', '--strategy-list', @@ -552,7 +558,7 @@ def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: # Prevent setting loggers mocker.patch('freqtrade.loggers._set_loggers', MagicMock) - arglist = ['-vvv'] + arglist = ['trade', '-vvv'] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) @@ -604,7 +610,7 @@ def test_set_logfile(default_conf, mocker): patched_configuration_load_config_file(mocker, default_conf) arglist = [ - '--logfile', 'test_file.log', + 'trade', '--logfile', 'test_file.log', ] args = Arguments(arglist).get_parsed_arg() configuration = Configuration(args) @@ -620,7 +626,7 @@ def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None: default_conf['forcebuy_enable'] = True patched_configuration_load_config_file(mocker, default_conf) - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 9028ab961..cf5a22973 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -281,8 +281,8 @@ def test_generate_profit_graph(testdatadir): def test_start_plot_dataframe(mocker): aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock()) args = [ - "--config", "config.json.example", "plot-dataframe", + "--config", "config.json.example", "--pairs", "ETH/BTC" ] start_plot_dataframe(get_args(args)) @@ -323,8 +323,8 @@ def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir): def test_start_plot_profit(mocker): aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock()) args = [ - "--config", "config.json.example", "plot-profit", + "--config", "config.json.example", "--pairs", "ETH/BTC" ] start_plot_profit(get_args(args)) From ad2fa61765f80e79d8014255887c79343e642117 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 13:40:28 +0200 Subject: [PATCH 11/30] Fix utils test --- freqtrade/utils.py | 2 +- tests/test_utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 8e57606da..de167671f 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -68,7 +68,7 @@ def start_create_userdir(args: Dict[str, Any]) -> int: create_userdata_dir(args["user_data_dir"], create_dir=True) else: logger.warning("`create-userdir` requires --userdir to be set.") - return 1 + sys.exit(1) def start_download_data(args: Dict[str, Any]) -> None: diff --git a/tests/test_utils.py b/tests/test_utils.py index c99044610..7922bb624 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -13,7 +13,7 @@ from tests.conftest import get_args, log_has, patch_exchange def test_setup_utils_configuration(): args = [ - '--config', 'config.json.example', + 'list-exchanges', '--config', 'config.json.example', ] config = setup_utils_configuration(get_args(args), RunMode.OTHER) From 0aa73d5b35b09e088e1ab06dfe148702513de964 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2019 13:47:33 +0200 Subject: [PATCH 12/30] Add test for failing case --- tests/test_arguments.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 53602574e..8ea55dd6a 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -1,5 +1,6 @@ # pragma pylint: disable=missing-docstring, C0103 import argparse +import re import pytest @@ -8,8 +9,16 @@ from freqtrade.configuration.cli_options import check_int_positive # Parse common command-line-arguments. Used for all tools -def test_parse_args_none() -> None: +def test_parse_args_error(capsys) -> None: arguments = Arguments([]) + with pytest.raises(SystemExit): + arguments.get_parsed_arg() + captured = capsys.readouterr() + assert re.search(r".*the following arguments are required.*", captured.err) + + +def test_parse_args_none() -> None: + arguments = Arguments(['trade']) assert isinstance(arguments, Arguments) x = arguments.get_parsed_arg() assert isinstance(x, dict) @@ -17,7 +26,7 @@ def test_parse_args_none() -> None: def test_parse_args_defaults() -> None: - args = Arguments([]).get_parsed_arg() + args = Arguments(['trade']).get_parsed_arg() assert args["config"] == ['config.json'] assert args["strategy_path"] is None assert args["datadir"] is None From 9ef874e979c064d3423f6dd1b75dc971dd96be34 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Sep 2019 06:35:37 +0200 Subject: [PATCH 13/30] Add Custom message during transition period --- freqtrade/configuration/arguments.py | 14 +++++++++----- freqtrade/main.py | 7 +++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index d0db162b9..4fa493eea 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -74,8 +74,9 @@ class Arguments: # Workaround issue in argparse with action='append' and default value # (see https://bugs.python.org/issue16399) # Allow no-config for certain commands (like downloading / plotting) - if (parsed_arg.config is None and ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() or - not ('command' in parsed_arg and parsed_arg.command in NO_CONF_REQURIED))): + if ('config' in parsed_arg and parsed_arg.config is None and + ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() or + not ('command' in parsed_arg and parsed_arg.command in NO_CONF_REQURIED))): parsed_arg.config = [constants.DEFAULT_CONFIG] return parsed_arg @@ -97,15 +98,18 @@ class Arguments: self._build_args(optionlist=ARGS_COMMON, parser=group) # Build main command - self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot', - parents=[_common_parser]) + self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge from freqtrade.utils import (start_create_userdir, start_download_data, start_list_exchanges, start_trading) from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit - subparsers = self.parser.add_subparsers(dest='command', required=True) + subparsers = self.parser.add_subparsers(dest='command', + # Use custom message when no subhandler is added + # shown from `main.py` + # required=True + ) # Add trade subcommand trade_cmd = subparsers.add_parser('trade', help='Trade module.', diff --git a/freqtrade/main.py b/freqtrade/main.py index 543aab169..d984ff487 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -35,6 +35,13 @@ def main(sysargv: List[str] = None) -> None: # Call subcommand. if 'func' in args: return_code = args['func'](args) + else: + # No subcommand was issued. + raise OperationalException( + "Usage of freqtrade requires a subcommand.\n" + "To use the previous behaviour, run freqtrade with `freqtrade trade [...]`.\n" + "To see a full list of options, please use `freqtrade --help`" + ) except SystemExit as e: return_code = e From 09f18d07b06ac4e7d08a902ea2e84e9dc91f31fe Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Sep 2019 06:44:07 +0200 Subject: [PATCH 14/30] Adjust some hyperopt tests --- tests/optimize/test_hyperopt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index c9a112422..8a8e31df3 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -68,8 +68,8 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca patched_configuration_load_config_file(mocker, default_conf) args = [ + 'hyperopt', '--config', 'config.json', - 'hyperopt' ] config = setup_configuration(get_args(args), RunMode.HYPEROPT) @@ -99,9 +99,9 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo ) args = [ + 'hyperopt', '--config', 'config.json', '--datadir', '/foo/bar', - 'hyperopt', '--ticker-interval', '1m', '--timerange', ':100', '--enable-position-stacking', @@ -215,8 +215,8 @@ def test_start(mocker, default_conf, caplog) -> None: patch_exchange(mocker) args = [ - '--config', 'config.json', 'hyperopt', + '--config', 'config.json', '--epochs', '5' ] args = get_args(args) @@ -237,8 +237,8 @@ def test_start_no_data(mocker, default_conf, caplog) -> None: patch_exchange(mocker) args = [ - '--config', 'config.json', 'hyperopt', + '--config', 'config.json', '--epochs', '5' ] args = get_args(args) @@ -254,8 +254,8 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: patch_exchange(mocker) args = [ - '--config', 'config.json', 'hyperopt', + '--config', 'config.json', '--epochs', '5' ] args = get_args(args) From 67b82638dbda9d879987f2012c2d1847c7364c61 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Sep 2019 06:44:20 +0200 Subject: [PATCH 15/30] Move test without command to test_main --- tests/test_arguments.py | 11 ++--------- tests/test_main.py | 8 +++++++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 8ea55dd6a..8cd24fe55 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -4,19 +4,12 @@ import re import pytest +from freqtrade import OperationalException from freqtrade.configuration import Arguments from freqtrade.configuration.cli_options import check_int_positive # Parse common command-line-arguments. Used for all tools -def test_parse_args_error(capsys) -> None: - arguments = Arguments([]) - with pytest.raises(SystemExit): - arguments.get_parsed_arg() - captured = capsys.readouterr() - assert re.search(r".*the following arguments are required.*", captured.err) - - def test_parse_args_none() -> None: arguments = Arguments(['trade']) assert isinstance(arguments, Arguments) @@ -157,8 +150,8 @@ def test_download_data_options() -> None: def test_plot_dataframe_options() -> None: args = [ - '-c', 'config.json.example', 'plot-dataframe', + '-c', 'config.json.example', '--indicators1', 'sma10', 'sma100', '--indicators2', 'macd', 'fastd', 'fastk', '--plot-limit', '30', diff --git a/tests/test_main.py b/tests/test_main.py index c5e1c8901..dac960886 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -11,10 +11,16 @@ from freqtrade.freqtradebot import FreqtradeBot from freqtrade.main import main from freqtrade.state import State from freqtrade.worker import Worker -from tests.conftest import (log_has, patch_exchange, +from tests.conftest import (log_has, log_has_re, patch_exchange, patched_configuration_load_config_file) +def test_parse_args_None(caplog) -> None: + with pytest.raises(SystemExit): + main([]) + assert log_has_re(r"Usage of freqtrade requires a subcommand\.", caplog) + + def test_parse_args_backtesting(mocker) -> None: """ Test that main() can start backtesting and also ensure we can pass some specific arguments From 014881e5504ad7b7ca02aa262e007ed23affc22c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Sep 2019 06:44:39 +0200 Subject: [PATCH 16/30] Allow query version without subcommand --- freqtrade/configuration/arguments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 4fa493eea..fecf1da07 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -99,6 +99,7 @@ class Arguments: # Build main command self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') + self._build_args(optionlist=['version'], parser=self.parser) from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge from freqtrade.utils import (start_create_userdir, start_download_data, From 0d13e2cb2eabac2011b156d7a6c454c7defde9f7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Sep 2019 11:34:44 +0200 Subject: [PATCH 17/30] Update travis to run new methods --- .travis.yml | 4 ++-- freqtrade/utils.py | 2 +- tests/test_arguments.py | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 405228ab8..81c3de2fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,11 +28,11 @@ jobs: name: pytest - script: - cp config.json.example config.json - - freqtrade --datadir tests/testdata backtesting + - freqtrade backtesting --datadir tests/testdata name: backtest - script: - cp config.json.example config.json - - freqtrade --datadir tests/testdata hyperopt -e 5 + - freqtrade hyperopt --datadir tests/testdata -e 5 name: hyperopt - script: flake8 name: flake8 diff --git a/freqtrade/utils.py b/freqtrade/utils.py index de167671f..6b2b5314b 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -58,7 +58,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: f"{', '.join(available_exchanges())}") -def start_create_userdir(args: Dict[str, Any]) -> int: +def start_create_userdir(args: Dict[str, Any]) -> None: """ Create "user_data" directory to contain user data strategies, hyperopts, ...) :param args: Cli args from Arguments() diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 8cd24fe55..4e01732b3 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -1,10 +1,8 @@ # pragma pylint: disable=missing-docstring, C0103 import argparse -import re import pytest -from freqtrade import OperationalException from freqtrade.configuration import Arguments from freqtrade.configuration.cli_options import check_int_positive From 52523bcd8bd9cd6c5b4a02730bb6d8ca50904793 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Sep 2019 16:25:25 +0200 Subject: [PATCH 18/30] Use strategy child parser --- freqtrade/configuration/arguments.py | 29 +++++++++++++++------------- tests/optimize/test_hyperopt.py | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index fecf1da07..d2e5d1fc5 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -12,10 +12,9 @@ ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_dat ARGS_STRATEGY = ["strategy", "strategy_path"] -ARGS_MAIN = ARGS_STRATEGY + ["db_url", "sd_notify"] +ARGS_TRADE = ["db_url", "sd_notify"] -ARGS_COMMON_OPTIMIZE = ARGS_STRATEGY + ["ticker_interval", "timerange", - "max_open_trades", "stake_amount"] +ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange", "max_open_trades", "stake_amount"] ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", "strategy_list", "export", "exportfilename"] @@ -35,8 +34,9 @@ ARGS_CREATE_USERDIR = ["user_data_dir"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "exchange", "timeframes", "erase"] -ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", - "trade_source", "export", "exportfilename", "timerange", "ticker_interval"] +ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", + "db_url", "trade_source", "export", "exportfilename", + "timerange", "ticker_interval"] ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source", "ticker_interval"] @@ -94,9 +94,13 @@ class Arguments: """ # Build shared arguments (as group Common Options) _common_parser = argparse.ArgumentParser(add_help=False) - group = _common_parser.add_argument_group("Common Options") + group = _common_parser.add_argument_group("Common arguments") self._build_args(optionlist=ARGS_COMMON, parser=group) + _strategy_parser = argparse.ArgumentParser(add_help=False) + strategy_group = _common_parser.add_argument_group("Strategy arguments") + self._build_args(optionlist=ARGS_STRATEGY, parser=strategy_group) + # Build main command self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') self._build_args(optionlist=['version'], parser=self.parser) @@ -114,25 +118,25 @@ class Arguments: # Add trade subcommand trade_cmd = subparsers.add_parser('trade', help='Trade module.', - parents=[_common_parser]) + parents=[_common_parser, _strategy_parser]) trade_cmd.set_defaults(func=start_trading) - self._build_args(optionlist=ARGS_MAIN, parser=trade_cmd) + self._build_args(optionlist=ARGS_TRADE, parser=trade_cmd) # Add backtesting subcommand backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.', - parents=[_common_parser]) + parents=[_common_parser, _strategy_parser]) backtesting_cmd.set_defaults(func=start_backtesting) self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd) # Add edge subcommand edge_cmd = subparsers.add_parser('edge', help='Edge module.', - parents=[_common_parser]) + parents=[_common_parser, _strategy_parser]) edge_cmd.set_defaults(func=start_edge) self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd) # Add hyperopt subcommand hyperopt_cmd = subparsers.add_parser('hyperopt', help='Hyperopt module.', - parents=[_common_parser], + parents=[_common_parser, _strategy_parser], ) hyperopt_cmd.set_defaults(func=start_hyperopt) self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd) @@ -140,7 +144,6 @@ class Arguments: # add create-userdir subcommand create_userdir_cmd = subparsers.add_parser('create-userdir', help="Create user-data directory.", - ) create_userdir_cmd.set_defaults(func=start_create_userdir) self._build_args(optionlist=ARGS_CREATE_USERDIR, parser=create_userdir_cmd) @@ -164,7 +167,7 @@ class Arguments: # Add Plotting subcommand plot_dataframe_cmd = subparsers.add_parser('plot-dataframe', help='Plot candles with indicators.', - parents=[_common_parser], + parents=[_common_parser, _strategy_parser], ) plot_dataframe_cmd.set_defaults(func=start_plot_dataframe) self._build_args(optionlist=ARGS_PLOT_DATAFRAME, parser=plot_dataframe_cmd) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 8a8e31df3..5ff11d5ea 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -198,8 +198,8 @@ def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None patch_exchange(mocker) args = [ - '--config', 'config.json', 'hyperopt', + '--config', 'config.json', '--epochs', '5' ] args = get_args(args) From 381b0d3d07f127ac4f1a27b31ccf997947dba103 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Sep 2019 16:28:14 +0200 Subject: [PATCH 19/30] Fix typo with new parser --- freqtrade/configuration/arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index d2e5d1fc5..a8d4b48f1 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -98,7 +98,7 @@ class Arguments: self._build_args(optionlist=ARGS_COMMON, parser=group) _strategy_parser = argparse.ArgumentParser(add_help=False) - strategy_group = _common_parser.add_argument_group("Strategy arguments") + strategy_group = _strategy_parser.add_argument_group("Strategy arguments") self._build_args(optionlist=ARGS_STRATEGY, parser=strategy_group) # Build main command From 2710226326f71eaaaf07830f64b33b905c180224 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Sep 2019 19:18:02 +0200 Subject: [PATCH 20/30] Update documentation to use subcommands --- docs/backtesting.md | 2 +- docs/bot-usage.md | 188 ++++++++++++++++++++++++--------- docs/docker.md | 4 +- docs/edge.md | 2 +- docs/faq.md | 2 +- docs/plotting.md | 59 +++++++++-- docs/rest-api.md | 2 +- docs/strategy-customization.md | 6 +- freqtrade.service | 2 +- freqtrade.service.watchdog | 2 +- 10 files changed, 198 insertions(+), 71 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 75aba6c73..6383b1855 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -45,7 +45,7 @@ freqtrade backtesting --datadir user_data/data/bittrex-20180101 #### With a (custom) strategy file ```bash -freqtrade -s SampleStrategy backtesting +freqtrade backtesting -s SampleStrategy ``` Where `-s SampleStrategy` refers to the class name within the strategy file `sample_strategy.py` found in the `freqtrade/user_data/strategies` directory. diff --git a/docs/bot-usage.md b/docs/bot-usage.md index f44400e32..ee01780a0 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -5,27 +5,47 @@ This page explains the different parameters of the bot and how to run it. !!! Note If you've used `setup.sh`, don't forget to activate your virtual environment (`source .env/bin/activate`) before running freqtrade commands. - ## Bot commands ``` -usage: freqtrade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] [-s NAME] [--strategy-path PATH] - [--db-url PATH] [--sd-notify] - {backtesting,edge,hyperopt,create-userdir,list-exchanges} ... +usage: freqtrade [-h] [-V] + {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,download-data,plot-dataframe,plot-profit} + ... Free, open source crypto trading bot positional arguments: - {backtesting,edge,hyperopt,create-userdir,list-exchanges} + {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,download-data,plot-dataframe,plot-profit} + trade Trade module. backtesting Backtesting module. edge Edge module. hyperopt Hyperopt module. create-userdir Create user-data directory. list-exchanges Print available exchanges. + download-data Download backtesting data. + plot-dataframe Plot candles with indicators. + plot-profit Generate plot showing profits. optional arguments: -h, --help show this help message and exit + -V, --version show program's version number and exit +``` + +### Bot trading commands + +``` +usage: freqtrade trade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] + [--userdir PATH] [-s NAME] [--strategy-path PATH] + [--db-url PATH] [--sd-notify] + +optional arguments: + -h, --help show this help message and exit + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite://` for Dry Run). + --sd-notify Notify systemd service manager. + +Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). --logfile FILE Log to the file specified. -V, --version show program's version number and exit @@ -37,14 +57,12 @@ optional arguments: Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH Path to userdata directory. + +Strategy arguments: -s NAME, --strategy NAME Specify strategy class name (default: `DefaultStrategy`). --strategy-path PATH Specify additional strategy lookup path. - --db-url PATH Override trades database URL, this is useful in custom - deployments (default: `sqlite:///tradesv3.sqlite` for - Live Run mode, `sqlite://` for Dry Run). - --sd-notify Notify systemd service manager. ``` @@ -128,7 +146,7 @@ In `user_data/strategies` you have a file `my_awesome_strategy.py` which has a strategy class called `AwesomeStrategy` to load it: ```bash -freqtrade --strategy AwesomeStrategy +freqtrade trade --strategy AwesomeStrategy ``` If the bot does not find your strategy file, it will display in an error @@ -143,7 +161,7 @@ This parameter allows you to add an additional strategy lookup path, which gets checked before the default locations (The passed path must be a directory!): ```bash -freqtrade --strategy AwesomeStrategy --strategy-path /some/directory +freqtrade trade --strategy AwesomeStrategy --strategy-path /some/directory ``` #### How to install a strategy? @@ -167,20 +185,22 @@ freqtrade -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite Backtesting also uses the config specified via `-c/--config`. ``` -usage: freqtrade backtesting [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] - [--max_open_trades MAX_OPEN_TRADES] - [--stake_amount STAKE_AMOUNT] [-r] [--eps] [--dmmp] - [-l] - [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] - [--export EXPORT] [--export-filename PATH] +usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] [-i TICKER_INTERVAL] + [--timerange TIMERANGE] [--max_open_trades INT] + [--stake_amount STAKE_AMOUNT] [--eps] [--dmmp] + [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] + [--export EXPORT] [--export-filename PATH] optional arguments: -h, --help show this help message and exit -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL - Specify ticker interval (1m, 5m, 30m, 1h, 1d). + Specify ticker interval (`1m`, `5m`, `30m`, `1h`, + `1d`). --timerange TIMERANGE Specify what timerange of data to use. - --max_open_trades MAX_OPEN_TRADES + --max_open_trades INT Specify max_open_trades to use. --stake_amount STAKE_AMOUNT Specify stake_amount. @@ -193,26 +213,47 @@ optional arguments: number). --strategy-list STRATEGY_LIST [STRATEGY_LIST ...] Provide a space-separated list of strategies to - backtest Please note that ticker-interval needs to be + backtest. Please note that ticker-interval needs to be set either in config or via command line. When using - this together with --export trades, the strategy-name - is injected into the filename (so backtest-data.json - becomes backtest-data-DefaultStrategy.json - --export EXPORT Export backtest results, argument are: trades. Example - --export=trades + this together with `--export trades`, the strategy- + name is injected into the filename (so `backtest- + data.json` becomes `backtest-data- + DefaultStrategy.json` + --export EXPORT Export backtest results, argument are: trades. + Example: `--export=trades` --export-filename PATH - Save backtest results to this filename requires - --export to be set as well Example --export- - filename=user_data/backtest_results/backtest_today.json - (default: user_data/backtest_results/backtest- - result.json) + Save backtest results to the file with this filename + (default: `user_data/backtest_results/backtest- + result.json`). Requires `--export` to be set as well. + Example: `--export-filename=user_data/backtest_results + /backtest_today.json` + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name (default: + `DefaultStrategy`). + --strategy-path PATH Specify additional strategy lookup path. + ``` ### Getting historic data for backtesting The first time your run Backtesting, you will need to download some historic data first. This can be accomplished by using `freqtrade download-data`. -Check the corresponding [help page section](backtesting.md#Getting-data-for-backtesting-and-hyperopt) for more details +Check the corresponding [Data Downloading](data-download.md) section for more details ## Hyperopt commands @@ -220,15 +261,17 @@ To optimize your strategy, you can use hyperopt parameter hyperoptimization to find optimal parameter values for your stategy. ``` -usage: freqtrade hyperopt [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] +usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] + [--userdir PATH] [-s NAME] [--strategy-path PATH] + [-i TICKER_INTERVAL] [--timerange TIMERANGE] [--max_open_trades INT] - [--stake_amount STAKE_AMOUNT] [-r] + [--stake_amount STAKE_AMOUNT] [--customhyperopt NAME] [--hyperopt-path PATH] [--eps] [-e INT] - [-s {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]] - [--dmmp] [--print-all] [--no-color] [-j JOBS] - [--random-state INT] [--min-trades INT] [--continue] - [--hyperopt-loss NAME] + [--spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]] + [--dmmp] [--print-all] [--no-color] [--print-json] + [-j JOBS] [--random-state INT] [--min-trades INT] + [--continue] [--hyperopt-loss NAME] optional arguments: -h, --help show this help message and exit @@ -250,7 +293,7 @@ optional arguments: Allow buying the same pair multiple times (position stacking). -e INT, --epochs INT Specify number of epochs (default: 100). - -s {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...], --spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...] + --spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...] Specify which parameters to hyperopt. Space-separated list. Default: `all`. --dmmp, --disable-max-market-positions @@ -260,6 +303,7 @@ optional arguments: --print-all Print all results, not only the best ones. --no-color Disable colorization of hyperopt results. May be useful if you are redirecting output to a file. + --print-json Print best result detailization in JSON format. -j JOBS, --job-workers JOBS The number of concurrently running jobs for hyperoptimization (hyperopt worker processes). If -1 @@ -278,8 +322,28 @@ optional arguments: generate completely different results, since the target for optimization is different. Built-in Hyperopt-loss-functions are: DefaultHyperOptLoss, - OnlyProfitHyperOptLoss, SharpeHyperOptLoss. - (default: `DefaultHyperOptLoss`). + OnlyProfitHyperOptLoss, SharpeHyperOptLoss.(default: + `DefaultHyperOptLoss`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name (default: + `DefaultStrategy`). + --strategy-path PATH Specify additional strategy lookup path. + ``` ## Edge commands @@ -287,26 +351,48 @@ optional arguments: To know your trade expectancy and winrate against historical data, you can use Edge. ``` -usage: freqtrade edge [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] - [--max_open_trades MAX_OPEN_TRADES] - [--stake_amount STAKE_AMOUNT] [-r] - [--stoplosses STOPLOSS_RANGE] +usage: freqtrade edge [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] + [--userdir PATH] [-s NAME] [--strategy-path PATH] + [-i TICKER_INTERVAL] [--timerange TIMERANGE] + [--max_open_trades INT] [--stake_amount STAKE_AMOUNT] + [--stoplosses STOPLOSS_RANGE] optional arguments: -h, --help show this help message and exit -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL - Specify ticker interval (1m, 5m, 30m, 1h, 1d). + Specify ticker interval (`1m`, `5m`, `30m`, `1h`, + `1d`). --timerange TIMERANGE Specify what timerange of data to use. - --max_open_trades MAX_OPEN_TRADES + --max_open_trades INT Specify max_open_trades to use. --stake_amount STAKE_AMOUNT Specify stake_amount. --stoplosses STOPLOSS_RANGE - Defines a range of stoploss against which edge will - assess the strategy the format is "min,max,step" - (without any space).example: - --stoplosses=-0.01,-0.1,-0.001 + Defines a range of stoploss values against which edge + will assess the strategy. The format is "min,max,step" + (without any space). Example: + `--stoplosses=-0.01,-0.1,-0.001` + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name (default: + `DefaultStrategy`). + --strategy-path PATH Specify additional strategy lookup path. + ``` To understand edge and how to read the results, please read the [edge documentation](edge.md). diff --git a/docs/docker.md b/docs/docker.md index 923dec1e2..7fc8d2ba4 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -160,7 +160,7 @@ docker run -d \ -v ~/.freqtrade/config.json:/freqtrade/config.json \ -v ~/.freqtrade/user_data/:/freqtrade/user_data \ -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ - freqtrade --db-url sqlite:///tradesv3.sqlite --strategy MyAwesomeStrategy + freqtrade trade --db-url sqlite:///tradesv3.sqlite --strategy MyAwesomeStrategy ``` !!! Note @@ -199,7 +199,7 @@ docker run -d \ -v ~/.freqtrade/config.json:/freqtrade/config.json \ -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ -v ~/.freqtrade/user_data/:/freqtrade/user_data/ \ - freqtrade --strategy AwsomelyProfitableStrategy backtesting + freqtrade backtesting --strategy AwsomelyProfitableStrategy ``` Head over to the [Backtesting Documentation](backtesting.md) for more details. diff --git a/docs/edge.md b/docs/edge.md index d91522770..80db7b91e 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -235,7 +235,7 @@ An example of its output: ### Update cached pairs with the latest data Edge requires historic data the same way as backtesting does. -Please refer to the [download section](backtesting.md#Getting-data-for-backtesting-and-hyperopt) of the documentation for details. +Please refer to the [Data Downloading](data-download.md) section of the documentation for details. ### Precising stoploss range diff --git a/docs/faq.md b/docs/faq.md index a441ffacd..c519f8cc3 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -4,7 +4,7 @@ ### The bot does not start -Running the bot with `freqtrade --config config.json` does show the output `freqtrade: command not found`. +Running the bot with `freqtrade trade --config config.json` does show the output `freqtrade: command not found`. This could have the following reasons: diff --git a/docs/plotting.md b/docs/plotting.md index 4deb6db12..25278c99d 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -23,13 +23,15 @@ The `freqtrade plot-dataframe` subcommand shows an interactive graph with three Possible arguments: ``` -usage: freqtrade plot-dataframe [-h] [-p PAIRS [PAIRS ...]] +usage: freqtrade plot-dataframe [-h] [-v] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] [-p PAIRS [PAIRS ...]] [--indicators1 INDICATORS1 [INDICATORS1 ...]] [--indicators2 INDICATORS2 [INDICATORS2 ...]] [--plot-limit INT] [--db-url PATH] [--trade-source {DB,file}] [--export EXPORT] [--export-filename PATH] - [--timerange TIMERANGE] + [--timerange TIMERANGE] [-i TICKER_INTERVAL] optional arguments: -h, --help show this help message and exit @@ -62,6 +64,28 @@ optional arguments: /backtest_today.json` --timerange TIMERANGE Specify what timerange of data to use. + -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + Specify ticker interval (`1m`, `5m`, `30m`, `1h`, + `1d`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name (default: + `DefaultStrategy`). + --strategy-path PATH Specify additional strategy lookup path. ``` @@ -83,7 +107,7 @@ Use `--indicators1` for the main plot and `--indicators2` for the subplot below You will almost certainly want to specify a custom strategy! This can be done by adding `-s Classname` / `--strategy ClassName` to the command. ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --indicators1 sma ema --indicators2 macd +freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH --indicators1 sma ema --indicators2 macd ``` ### Further usage examples @@ -91,25 +115,25 @@ freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --indicators1 sma To plot multiple pairs, separate them with a space: ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH XRP/ETH +freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH XRP/ETH ``` To plot a timerange (to zoom in) ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe -p BTC/ETH --timerange=20180801-20180805 +freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH --timerange=20180801-20180805 ``` To plot trades stored in a database use `--db-url` in combination with `--trade-source DB`: ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB +freqtrade plot-dataframe --strategy AwesomeStrategy --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB ``` To plot trades from a backtesting result, use `--export-filename ` ``` bash -freqtrade --strategy AwesomeStrategy plot-dataframe --export-filename user_data/backtest_results/backtest-result.json -p BTC/ETH +freqtrade plot-dataframe --strategy AwesomeStrategy --export-filename user_data/backtest_results/backtest-result.json -p BTC/ETH ``` ## Plot profit @@ -133,10 +157,11 @@ The third graph can be useful to spot outliers, events in pairs that cause profi Possible options for the `freqtrade plot-profit` subcommand: ``` -usage: freqtrade plot-profit [-h] [-p PAIRS [PAIRS ...]] +usage: freqtrade plot-profit [-h] [-v] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] [-p PAIRS [PAIRS ...]] [--timerange TIMERANGE] [--export EXPORT] [--export-filename PATH] [--db-url PATH] - [--trade-source {DB,file}] + [--trade-source {DB,file}] [-i TICKER_INTERVAL] optional arguments: -h, --help show this help message and exit @@ -159,6 +184,22 @@ optional arguments: --trade-source {DB,file} Specify the source for trades (Can be DB or file (backtest file)) Default: file + -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL + Specify ticker interval (`1m`, `5m`, `30m`, `1h`, + `1d`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: `config.json`). + Multiple --config options may be used. Can be set to + `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. ``` diff --git a/docs/rest-api.md b/docs/rest-api.md index afecc1d80..5295ebab4 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -58,7 +58,7 @@ docker run -d \ -v ~/.freqtrade/user_data/:/freqtrade/user_data \ -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ -p 127.0.0.1:8080:8080 \ - freqtrade --db-url sqlite:///tradesv3.sqlite --strategy MyAwesomeStrategy + freqtrade trade --db-url sqlite:///tradesv3.sqlite --strategy MyAwesomeStrategy ``` !!! Danger "Security warning" diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index b927e5aad..bb7138759 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -13,7 +13,7 @@ Let assume you have a class called `AwesomeStrategy` in the file `awesome-strate 2. Start the bot with the param `--strategy AwesomeStrategy` (the parameter is the class name) ```bash -freqtrade --strategy AwesomeStrategy +freqtrade trade --strategy AwesomeStrategy ``` ## Change your strategy @@ -45,7 +45,7 @@ The current version is 2 - which is also the default when it's not set explicitl Future versions will require this to be set. ```bash -freqtrade --strategy AwesomeStrategy +freqtrade trade --strategy AwesomeStrategy ``` **For the following section we will use the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py) @@ -402,7 +402,7 @@ The default buy strategy is located in the file If you want to use a strategy from a different directory you can pass `--strategy-path` ```bash -freqtrade --strategy AwesomeStrategy --strategy-path /some/directory +freqtrade trade --strategy AwesomeStrategy --strategy-path /some/directory ``` ### Further strategy ideas diff --git a/freqtrade.service b/freqtrade.service index 9de9f13c7..df220ed39 100644 --- a/freqtrade.service +++ b/freqtrade.service @@ -6,7 +6,7 @@ After=network.target # Set WorkingDirectory and ExecStart to your file paths accordingly # NOTE: %h will be resolved to /home/ WorkingDirectory=%h/freqtrade -ExecStart=/usr/bin/freqtrade +ExecStart=/usr/bin/freqtrade trade Restart=on-failure [Install] diff --git a/freqtrade.service.watchdog b/freqtrade.service.watchdog index ba491fa53..66ea00d76 100644 --- a/freqtrade.service.watchdog +++ b/freqtrade.service.watchdog @@ -6,7 +6,7 @@ After=network.target # Set WorkingDirectory and ExecStart to your file paths accordingly # NOTE: %h will be resolved to /home/ WorkingDirectory=%h/freqtrade -ExecStart=/usr/bin/freqtrade --sd-notify +ExecStart=/usr/bin/freqtrade trade --sd-notify Restart=always #Restart=on-failure From 344a0a094fcaef785d4d58bee58b654561d468dd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Sep 2019 19:21:18 +0200 Subject: [PATCH 21/30] Update remaining documentations --- docs/bot-usage.md | 8 ++++---- docs/hyperopt.md | 2 +- docs/installation.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index ee01780a0..112fc78a1 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -72,7 +72,7 @@ The bot allows you to select which configuration file you want to use by means o the `-c/--config` command line option: ```bash -freqtrade -c path/far/far/away/config.json +freqtrade trade -c path/far/far/away/config.json ``` Per default, the bot loads the `config.json` configuration file from the current @@ -91,13 +91,13 @@ empty key and secrete values while running in the Dry Mode (which does not actua require them): ```bash -freqtrade -c ./config.json +freqtrade trade -c ./config.json ``` and specify both configuration files when running in the normal Live Trade Mode: ```bash -freqtrade -c ./config.json -c path/to/secrets/keys.config.json +freqtrade trade -c ./config.json -c path/to/secrets/keys.config.json ``` This could help you hide your private Exchange key and Exchange secrete on you local machine @@ -177,7 +177,7 @@ using `--db-url`. This can also be used to specify a custom database in production mode. Example command: ```bash -freqtrade -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite +freqtrade trade -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite ``` ## Backtesting commands diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 1ca371e3d..e6f753072 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -239,7 +239,7 @@ Because hyperopt tries a lot of combinations to find the best parameters it will We strongly recommend to use `screen` or `tmux` to prevent any connection loss. ```bash -freqtrade -c config.json hyperopt --customhyperopt -e 5000 --spaces all +freqtrade hyperopt --config config.json --customhyperopt -e 5000 --spaces all ``` Use `` as the name of the custom hyperopt used. diff --git a/docs/installation.md b/docs/installation.md index 68348d4b0..3d0f27f2a 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -187,7 +187,7 @@ python3 -m pip install -e . If this is the first time you run the bot, ensure you are running it in Dry-run `"dry_run": true,` otherwise it will start to buy and sell coins. ```bash -freqtrade -c config.json +freqtrade trade -c config.json ``` *Note*: If you run the bot on a server, you should consider using [Docker](docker.md) or a terminal multiplexer like `screen` or [`tmux`](https://en.wikipedia.org/wiki/Tmux) to avoid that the bot is stopped on logout. From 52ff391c8a8eab054287683f82f2beae18703ae6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Sep 2019 19:48:37 +0200 Subject: [PATCH 22/30] Default dockerfile to "freqtrade trade" --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 8677b54de..5b69f55a2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,3 +24,5 @@ RUN pip install numpy --no-cache-dir \ COPY . /freqtrade/ RUN pip install -e . --no-cache-dir ENTRYPOINT ["freqtrade"] +# Default to trade mode +CMD [ "trade" ] From b73426b91f5925431a4631c0618fa917d845fe81 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Sep 2019 19:54:44 +0200 Subject: [PATCH 23/30] Disable Defaulting to DefaultStrategy --- .travis.yml | 4 ++-- build_helpers/publish_docker.sh | 2 +- docs/bot-usage.md | 4 ++-- docs/configuration.md | 2 +- freqtrade/configuration/cli_options.py | 3 +-- freqtrade/configuration/configuration.py | 2 +- freqtrade/constants.py | 1 - freqtrade/resolvers/strategy_resolver.py | 7 +++++-- tests/conftest.py | 1 + tests/strategy/test_strategy.py | 12 +++++++++++- tests/test_configuration.py | 1 - 11 files changed, 25 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 81c3de2fb..b066e7044 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,11 +28,11 @@ jobs: name: pytest - script: - cp config.json.example config.json - - freqtrade backtesting --datadir tests/testdata + - freqtrade backtesting --datadir tests/testdata --strategy DefaultStrategy name: backtest - script: - cp config.json.example config.json - - freqtrade hyperopt --datadir tests/testdata -e 5 + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy name: hyperopt - script: flake8 name: flake8 diff --git a/build_helpers/publish_docker.sh b/build_helpers/publish_docker.sh index 839ca0876..b8318c196 100755 --- a/build_helpers/publish_docker.sh +++ b/build_helpers/publish_docker.sh @@ -23,7 +23,7 @@ if [ $? -ne 0 ]; then fi # Run backtest -docker run --rm -it -v $(pwd)/config.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} --datadir /tests/testdata backtesting +docker run --rm -it -v $(pwd)/config.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy DefaultStrategy if [ $? -ne 0 ]; then echo "failed running backtest" diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 112fc78a1..2b66d3c25 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -60,8 +60,8 @@ Common arguments: Strategy arguments: -s NAME, --strategy NAME - Specify strategy class name (default: - `DefaultStrategy`). + Specify strategy class name which will be used by the + bot. --strategy-path PATH Specify additional strategy lookup path. ``` diff --git a/docs/configuration.md b/docs/configuration.md index 0d902766a..9c1b9d4f7 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -95,7 +95,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `db_url` | `sqlite:///tradesv3.sqlite`| Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. | `initial_state` | running | Defines the initial application state. More information below. | `forcebuy_enable` | false | Enables the RPC Commands to force a buy. More information below. -| `strategy` | DefaultStrategy | Defines Strategy class to use. +| `strategy` | None | **Required** Defines Strategy class to use. Recommended to set via `--strategy NAME`. | `strategy_path` | null | Adds an additional strategy lookup path (must be a directory). | `internals.process_throttle_secs` | 5 | **Required.** Set the process throttle. Value in second. | `internals.sd_notify` | false | 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. diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index 24230f1b3..2ecd4cfc5 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -66,9 +66,8 @@ AVAILABLE_CLI_OPTIONS = { # Main options "strategy": Arg( '-s', '--strategy', - help='Specify strategy class name (default: `%(default)s`).', + help='Specify strategy class name which will be used by the bot.', metavar='NAME', - default='DefaultStrategy', ), "strategy_path": Arg( '--strategy-path', diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 764593d0f..ac27a5c99 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -128,7 +128,7 @@ class Configuration: self._process_logging_options(config) # Set strategy if not specified in config and or if it's non default - if self.args.get("strategy") != constants.DEFAULT_STRATEGY or not config.get('strategy'): + if self.args.get("strategy") or not config.get('strategy'): config.update({'strategy': self.args.get("strategy")}) self._args_to_config(config, argname='strategy_path', diff --git a/freqtrade/constants.py b/freqtrade/constants.py index abf43b24d..749ae25b5 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -9,7 +9,6 @@ PROCESS_THROTTLE_SECS = 5 # sec DEFAULT_TICKER_INTERVAL = 5 # min HYPEROPT_EPOCH = 100 # epochs RETRY_TIMEOUT = 30 # sec -DEFAULT_STRATEGY = 'DefaultStrategy' DEFAULT_HYPEROPT = 'DefaultHyperOpts' DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss' DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index ca7e1165b..1b6d5c48a 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -32,8 +32,11 @@ class StrategyResolver(IResolver): """ config = config or {} - # Verify the strategy is in the configuration, otherwise fallback to the default strategy - strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY + if not config.get('strategy'): + raise OperationalException("No strategy set. Please use `--strategy` to specify " + "the strategy class to use.") + + strategy_name = config['strategy'] self.strategy: IStrategy = self._load_strategy(strategy_name, config=config, extra_dir=config.get('strategy_path')) diff --git a/tests/conftest.py b/tests/conftest.py index 6a0a74b5b..0ffb5a066 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -242,6 +242,7 @@ def default_conf(testdatadir): "db_url": "sqlite://", "user_data_dir": Path("user_data"), "verbosity": 3, + "strategy": "DefaultStrategy" } return configuration diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 6992d1aa5..82db30d47 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -55,6 +55,7 @@ def test_load_strategy_base64(result, caplog, default_conf): def test_load_strategy_invalid_directory(result, caplog, default_conf): + default_conf['strategy'] = 'SampleStrategy' resolver = StrategyResolver(default_conf) extra_dir = Path.cwd() / 'some/path' resolver._load_strategy('SampleStrategy', config=default_conf, extra_dir=extra_dir) @@ -65,13 +66,22 @@ def test_load_strategy_invalid_directory(result, caplog, default_conf): def test_load_not_found_strategy(default_conf): - strategy = StrategyResolver(default_conf) + default_conf['strategy'] = 'NotFoundStrategy' with pytest.raises(OperationalException, match=r"Impossible to load Strategy 'NotFoundStrategy'. " r"This class does not exist or contains Python code errors."): + strategy = StrategyResolver(default_conf) strategy._load_strategy(strategy_name='NotFoundStrategy', config=default_conf) +def test_load_strategy_noname(default_conf): + default_conf['strategy'] = '' + with pytest.raises(OperationalException, + match="No strategy set. Please use `--strategy` to specify " + "the strategy class to use."): + StrategyResolver(default_conf) + + def test_strategy(result, default_conf): default_conf.update({'strategy': 'DefaultStrategy'}) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 3c3ad3026..333a8992a 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -212,7 +212,6 @@ def test_load_config(default_conf, mocker) -> None: configuration = Configuration(args) validated_conf = configuration.load_config() - assert validated_conf.get('strategy') == 'DefaultStrategy' assert validated_conf.get('strategy_path') is None assert 'edge' not in validated_conf From 95299d94c4c824eed2c3aca6fc370a8b85633901 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Oct 2019 06:39:24 +0200 Subject: [PATCH 24/30] Remove unused test line --- tests/strategy/test_strategy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 82db30d47..7445e3ca7 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -70,8 +70,7 @@ def test_load_not_found_strategy(default_conf): with pytest.raises(OperationalException, match=r"Impossible to load Strategy 'NotFoundStrategy'. " r"This class does not exist or contains Python code errors."): - strategy = StrategyResolver(default_conf) - strategy._load_strategy(strategy_name='NotFoundStrategy', config=default_conf) + StrategyResolver(default_conf) def test_load_strategy_noname(default_conf): From c4105436ebbad89d37922aad57068bef5c5cca32 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 10 Oct 2019 04:37:32 +0300 Subject: [PATCH 25/30] Disable defaulting to DefaultHyperOpts and DefaultHyperOptLoss --- .travis.yml | 2 +- docs/bot-usage.md | 8 +-- freqtrade/configuration/cli_options.py | 7 +- freqtrade/constants.py | 2 - freqtrade/resolvers/hyperopt_resolver.py | 27 +++++--- tests/optimize/test_hyperopt.py | 81 +++++++++++++++++++++--- 6 files changed, 96 insertions(+), 31 deletions(-) diff --git a/.travis.yml b/.travis.yml index b066e7044..7a75a76c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ jobs: name: backtest - script: - cp config.json.example config.json - - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy --customhyperopt DefaultHyperOpts --hyperopt-loss DefaultHyperOptLoss name: hyperopt - script: flake8 name: flake8 diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 2b66d3c25..fcf82826a 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -285,8 +285,8 @@ optional arguments: --stake_amount STAKE_AMOUNT Specify stake_amount. --customhyperopt NAME - Specify hyperopt class name (default: - `DefaultHyperOpts`). + Specify hyperopt class name which will be used by the + bot. --hyperopt-path PATH Specify additional lookup path for Hyperopts and Hyperopt Loss functions. --eps, --enable-position-stacking @@ -322,8 +322,8 @@ optional arguments: generate completely different results, since the target for optimization is different. Built-in Hyperopt-loss-functions are: DefaultHyperOptLoss, - OnlyProfitHyperOptLoss, SharpeHyperOptLoss.(default: - `DefaultHyperOptLoss`). + OnlyProfitHyperOptLoss, SharpeHyperOptLoss. + Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index 2ecd4cfc5..6928ddfdb 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -153,9 +153,8 @@ AVAILABLE_CLI_OPTIONS = { # Hyperopt "hyperopt": Arg( '--customhyperopt', - help='Specify hyperopt class name (default: `%(default)s`).', + help='Specify hyperopt class name which will be used by the bot.', metavar='NAME', - default=constants.DEFAULT_HYPEROPT, ), "hyperopt_path": Arg( '--hyperopt-path', @@ -232,10 +231,8 @@ AVAILABLE_CLI_OPTIONS = { help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). ' 'Different functions can generate completely different results, ' 'since the target for optimization is different. Built-in Hyperopt-loss-functions are: ' - 'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss.' - '(default: `%(default)s`).', + 'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss.', metavar='NAME', - default=constants.DEFAULT_HYPEROPT_LOSS, ), # List exchanges "print_one_column": Arg( diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 749ae25b5..2f490c900 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -9,8 +9,6 @@ PROCESS_THROTTLE_SECS = 5 # sec DEFAULT_TICKER_INTERVAL = 5 # min HYPEROPT_EPOCH = 100 # epochs RETRY_TIMEOUT = 30 # sec -DEFAULT_HYPEROPT = 'DefaultHyperOpts' -DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss' DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' DEFAULT_DB_DRYRUN_URL = 'sqlite://' UNLIMITED_STAKE_AMOUNT = 'unlimited' diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index e96394d69..45fe2548e 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -8,7 +8,6 @@ from pathlib import Path from typing import Optional, Dict from freqtrade import OperationalException -from freqtrade.constants import DEFAULT_HYPEROPT, DEFAULT_HYPEROPT_LOSS from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.resolvers import IResolver @@ -20,17 +19,21 @@ class HyperOptResolver(IResolver): """ This class contains all the logic to load custom hyperopt class """ - __slots__ = ['hyperopt'] - def __init__(self, config: Dict) -> None: + def __init__(self, config: Dict = None) -> None: """ Load the custom class from config parameter :param config: configuration dictionary """ + config = config or {} + + if not config.get('hyperopt'): + raise OperationalException("No Hyperopt set. Please use `--customhyperopt` to specify " + "the Hyperopt class to use.") + + hyperopt_name = config['hyperopt'] - # Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt - hyperopt_name = config.get('hyperopt') or DEFAULT_HYPEROPT self.hyperopt = self._load_hyperopt(hyperopt_name, config, extra_dir=config.get('hyperopt_path')) @@ -75,7 +78,6 @@ class HyperOptLossResolver(IResolver): """ This class contains all the logic to load custom hyperopt loss class """ - __slots__ = ['hyperoptloss'] def __init__(self, config: Dict = None) -> None: @@ -85,17 +87,22 @@ class HyperOptLossResolver(IResolver): """ config = config or {} - # Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt - hyperopt_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS + if not config.get('hyperopt_loss'): + raise OperationalException("No Hyperopt Loss Function set. Please use " + "`--hyperopt-loss` to specify " + "the Hyperopt Loss Function class to use.") + + hyperoptloss_name = config['hyperopt_loss'] + self.hyperoptloss = self._load_hyperoptloss( - hyperopt_name, config, extra_dir=config.get('hyperopt_path')) + hyperoptloss_name, config, extra_dir=config.get('hyperopt_path')) # Assign ticker_interval to be used in hyperopt self.hyperoptloss.__class__.ticker_interval = str(config['ticker_interval']) if not hasattr(self.hyperoptloss, 'hyperopt_loss_function'): raise OperationalException( - f"Found hyperopt {hyperopt_name} does not implement `hyperopt_loss_function`.") + f"Found hyperopt {hyperoptloss_name} does not implement `hyperopt_loss_function`.") def _load_hyperoptloss( self, hyper_loss_name: str, config: Dict, diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 5ff11d5ea..cf211e35b 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -25,7 +25,11 @@ from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, @pytest.fixture(scope='function') def hyperopt(default_conf, mocker): - default_conf.update({'spaces': ['all']}) + default_conf.update({ + 'spaces': ['all'], + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', + }) patch_exchange(mocker) return Hyperopt(default_conf) @@ -70,6 +74,8 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', ] config = setup_configuration(get_args(args), RunMode.HYPEROPT) @@ -101,6 +107,8 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--datadir', '/foo/bar', '--ticker-interval', '1m', '--timerange', ':100', @@ -155,7 +163,9 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt', MagicMock(return_value=hyperopts(default_conf)) ) - x = HyperOptResolver(default_conf, ).hyperopt + default_conf.update({'hyperopt': 'DefaultHyperOpts'}) + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) + x = HyperOptResolver(default_conf).hyperopt assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_sell_trend') assert log_has("Hyperopt class does not provide populate_sell_trend() method. " @@ -169,7 +179,15 @@ def test_hyperoptresolver_wrongname(mocker, default_conf, caplog) -> None: default_conf.update({'hyperopt': "NonExistingHyperoptClass"}) with pytest.raises(OperationalException, match=r'Impossible to load Hyperopt.*'): - HyperOptResolver(default_conf, ).hyperopt + HyperOptResolver(default_conf).hyperopt + + +def test_hyperoptresolver_noname(default_conf): + default_conf['hyperopt'] = '' + with pytest.raises(OperationalException, + match="No Hyperopt set. Please use `--customhyperopt` to specify " + "the Hyperopt class to use."): + HyperOptResolver(default_conf) def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None: @@ -179,7 +197,8 @@ def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver._load_hyperoptloss', MagicMock(return_value=hl) ) - x = HyperOptLossResolver(default_conf, ).hyperoptloss + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) + x = HyperOptLossResolver(default_conf).hyperoptloss assert hasattr(x, "hyperopt_loss_function") @@ -187,7 +206,17 @@ def test_hyperoptlossresolver_wrongname(mocker, default_conf, caplog) -> None: default_conf.update({'hyperopt_loss': "NonExistingLossClass"}) with pytest.raises(OperationalException, match=r'Impossible to load HyperoptLoss.*'): - HyperOptLossResolver(default_conf, ).hyperopt + HyperOptLossResolver(default_conf).hyperopt + + +def test_hyperoptlossresolver_noname(default_conf): + default_conf.update({'hyperopt': 'DefaultHyperOpts'}) + default_conf['hyperopt_loss'] = '' + with pytest.raises(OperationalException, + match="No Hyperopt Loss Function set. Please use " + "`--hyperopt-loss` to specify " + "the Hyperopt Loss Function class to use."): + HyperOptLossResolver(default_conf) def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None: @@ -200,6 +229,8 @@ def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -217,6 +248,8 @@ def test_start(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -239,6 +272,8 @@ def test_start_no_data(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -256,6 +291,8 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -264,6 +301,7 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: def test_loss_calculation_prefer_correct_trade_count(default_conf, hyperopt_results) -> None: + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss correct = hl.hyperopt_loss_function(hyperopt_results, 600) over = hl.hyperopt_loss_function(hyperopt_results, 600 + 100) @@ -276,6 +314,7 @@ def test_loss_calculation_prefer_shorter_trades(default_conf, hyperopt_results) resultsb = hyperopt_results.copy() resultsb.loc[1, 'trade_duration'] = 20 + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss longer = hl.hyperopt_loss_function(hyperopt_results, 100) shorter = hl.hyperopt_loss_function(resultsb, 100) @@ -288,6 +327,7 @@ def test_loss_calculation_has_limited_profit(default_conf, hyperopt_results) -> results_under = hyperopt_results.copy() results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss correct = hl.hyperopt_loss_function(hyperopt_results, 600) over = hl.hyperopt_loss_function(results_over, 600) @@ -407,6 +447,8 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -510,10 +552,13 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None: def test_generate_optimizer(mocker, default_conf) -> None: - default_conf.update({'config': 'config.json.example'}) - default_conf.update({'timerange': None}) - default_conf.update({'spaces': 'all'}) - default_conf.update({'hyperopt_min_trades': 1}) + default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', + 'timerange': None, + 'spaces': 'all', + 'hyperopt_min_trades': 1, + }) trades = [ ('POWR/BTC', 0.023117, 0.000233, 100) @@ -576,6 +621,8 @@ def test_generate_optimizer(mocker, default_conf) -> None: def test_clean_hyperopt(mocker, default_conf, caplog): patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -592,6 +639,8 @@ def test_clean_hyperopt(mocker, default_conf, caplog): def test_continue_hyperopt(mocker, default_conf, caplog): patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -621,6 +670,8 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -658,6 +709,8 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) -> patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -696,6 +749,8 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys) patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -737,6 +792,8 @@ def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) - patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -770,6 +827,8 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'buy', @@ -815,6 +874,8 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'sell', @@ -862,6 +923,8 @@ def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, metho patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': space, From 08e6d8a7809222c217ba7050a9114dbab385d1f2 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 11 Oct 2019 23:33:22 +0300 Subject: [PATCH 26/30] Rollback defaulting to DefaultHyperOptLoss --- .travis.yml | 2 +- docs/bot-usage.md | 3 ++- freqtrade/configuration/cli_options.py | 4 ++- freqtrade/constants.py | 1 + freqtrade/resolvers/hyperopt_resolver.py | 10 +++---- tests/optimize/test_hyperopt.py | 33 ------------------------ 6 files changed, 11 insertions(+), 42 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a75a76c2..a45334dd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ jobs: name: backtest - script: - cp config.json.example config.json - - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy --customhyperopt DefaultHyperOpts --hyperopt-loss DefaultHyperOptLoss + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy --customhyperopt DefaultHyperOpts name: hyperopt - script: flake8 name: flake8 diff --git a/docs/bot-usage.md b/docs/bot-usage.md index fcf82826a..8f7e0bbcf 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -322,7 +322,8 @@ optional arguments: generate completely different results, since the target for optimization is different. Built-in Hyperopt-loss-functions are: DefaultHyperOptLoss, - OnlyProfitHyperOptLoss, SharpeHyperOptLoss. + OnlyProfitHyperOptLoss, SharpeHyperOptLoss (default: + `DefaultHyperOptLoss`). Common arguments: diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index 6928ddfdb..ee0d94023 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -231,8 +231,10 @@ AVAILABLE_CLI_OPTIONS = { help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). ' 'Different functions can generate completely different results, ' 'since the target for optimization is different. Built-in Hyperopt-loss-functions are: ' - 'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss.', + 'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss ' + '(default: `%(default)s`).', metavar='NAME', + default=constants.DEFAULT_HYPEROPT_LOSS, ), # List exchanges "print_one_column": Arg( diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 2f490c900..b053519b0 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -9,6 +9,7 @@ PROCESS_THROTTLE_SECS = 5 # sec DEFAULT_TICKER_INTERVAL = 5 # min HYPEROPT_EPOCH = 100 # epochs RETRY_TIMEOUT = 30 # sec +DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss' DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' DEFAULT_DB_DRYRUN_URL = 'sqlite://' UNLIMITED_STAKE_AMOUNT = 'unlimited' diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 45fe2548e..d1bc90e13 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -8,6 +8,7 @@ from pathlib import Path from typing import Optional, Dict from freqtrade import OperationalException +from freqtrade.constants import DEFAULT_HYPEROPT_LOSS from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.resolvers import IResolver @@ -87,12 +88,9 @@ class HyperOptLossResolver(IResolver): """ config = config or {} - if not config.get('hyperopt_loss'): - raise OperationalException("No Hyperopt Loss Function set. Please use " - "`--hyperopt-loss` to specify " - "the Hyperopt Loss Function class to use.") - - hyperoptloss_name = config['hyperopt_loss'] + # Verify the hyperopt_loss is in the configuration, otherwise fallback to the + # default hyperopt loss + hyperoptloss_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS self.hyperoptloss = self._load_hyperoptloss( hyperoptloss_name, config, extra_dir=config.get('hyperopt_path')) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index cf211e35b..e1ee649c8 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -28,7 +28,6 @@ def hyperopt(default_conf, mocker): default_conf.update({ 'spaces': ['all'], 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', }) patch_exchange(mocker) return Hyperopt(default_conf) @@ -75,7 +74,6 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca 'hyperopt', '--config', 'config.json', '--customhyperopt', 'DefaultHyperOpts', - '--hyperopt-loss', 'DefaultHyperOptLoss', ] config = setup_configuration(get_args(args), RunMode.HYPEROPT) @@ -108,7 +106,6 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo 'hyperopt', '--config', 'config.json', '--customhyperopt', 'DefaultHyperOpts', - '--hyperopt-loss', 'DefaultHyperOptLoss', '--datadir', '/foo/bar', '--ticker-interval', '1m', '--timerange', ':100', @@ -164,7 +161,6 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None: MagicMock(return_value=hyperopts(default_conf)) ) default_conf.update({'hyperopt': 'DefaultHyperOpts'}) - default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) x = HyperOptResolver(default_conf).hyperopt assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_sell_trend') @@ -197,7 +193,6 @@ def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver._load_hyperoptloss', MagicMock(return_value=hl) ) - default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) x = HyperOptLossResolver(default_conf).hyperoptloss assert hasattr(x, "hyperopt_loss_function") @@ -209,16 +204,6 @@ def test_hyperoptlossresolver_wrongname(mocker, default_conf, caplog) -> None: HyperOptLossResolver(default_conf).hyperopt -def test_hyperoptlossresolver_noname(default_conf): - default_conf.update({'hyperopt': 'DefaultHyperOpts'}) - default_conf['hyperopt_loss'] = '' - with pytest.raises(OperationalException, - match="No Hyperopt Loss Function set. Please use " - "`--hyperopt-loss` to specify " - "the Hyperopt Loss Function class to use."): - HyperOptLossResolver(default_conf) - - def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None: start_mock = MagicMock() patched_configuration_load_config_file(mocker, default_conf) @@ -230,7 +215,6 @@ def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None 'hyperopt', '--config', 'config.json', '--customhyperopt', 'DefaultHyperOpts', - '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -249,7 +233,6 @@ def test_start(mocker, default_conf, caplog) -> None: 'hyperopt', '--config', 'config.json', '--customhyperopt', 'DefaultHyperOpts', - '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -273,7 +256,6 @@ def test_start_no_data(mocker, default_conf, caplog) -> None: 'hyperopt', '--config', 'config.json', '--customhyperopt', 'DefaultHyperOpts', - '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -292,7 +274,6 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: 'hyperopt', '--config', 'config.json', '--customhyperopt', 'DefaultHyperOpts', - '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -301,7 +282,6 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: def test_loss_calculation_prefer_correct_trade_count(default_conf, hyperopt_results) -> None: - default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss correct = hl.hyperopt_loss_function(hyperopt_results, 600) over = hl.hyperopt_loss_function(hyperopt_results, 600 + 100) @@ -314,7 +294,6 @@ def test_loss_calculation_prefer_shorter_trades(default_conf, hyperopt_results) resultsb = hyperopt_results.copy() resultsb.loc[1, 'trade_duration'] = 20 - default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss longer = hl.hyperopt_loss_function(hyperopt_results, 100) shorter = hl.hyperopt_loss_function(resultsb, 100) @@ -327,7 +306,6 @@ def test_loss_calculation_has_limited_profit(default_conf, hyperopt_results) -> results_under = hyperopt_results.copy() results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 - default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss correct = hl.hyperopt_loss_function(hyperopt_results, 600) over = hl.hyperopt_loss_function(results_over, 600) @@ -448,7 +426,6 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None: default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -554,7 +531,6 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None: def test_generate_optimizer(mocker, default_conf) -> None: default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'timerange': None, 'spaces': 'all', 'hyperopt_min_trades': 1, @@ -622,7 +598,6 @@ def test_clean_hyperopt(mocker, default_conf, caplog): patch_exchange(mocker) default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -640,7 +615,6 @@ def test_continue_hyperopt(mocker, default_conf, caplog): patch_exchange(mocker) default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -671,7 +645,6 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None: default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -710,7 +683,6 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) -> default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -750,7 +722,6 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys) default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -793,7 +764,6 @@ def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) - default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -828,7 +798,6 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None: default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'buy', @@ -875,7 +844,6 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'sell', @@ -924,7 +892,6 @@ def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, metho default_conf.update({'config': 'config.json.example', 'hyperopt': 'DefaultHyperOpts', - 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': space, From ff1fa17dc3243cc3a9c1469ef92b542c49e86d47 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 13 Oct 2019 03:41:25 +0300 Subject: [PATCH 27/30] No default value for the config parameter --- freqtrade/resolvers/hyperopt_resolver.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index d1bc90e13..15080cda5 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -22,13 +22,11 @@ class HyperOptResolver(IResolver): """ __slots__ = ['hyperopt'] - def __init__(self, config: Dict = None) -> None: + def __init__(self, config: Dict) -> None: """ Load the custom class from config parameter :param config: configuration dictionary """ - config = config or {} - if not config.get('hyperopt'): raise OperationalException("No Hyperopt set. Please use `--customhyperopt` to specify " "the Hyperopt class to use.") @@ -81,12 +79,11 @@ class HyperOptLossResolver(IResolver): """ __slots__ = ['hyperoptloss'] - def __init__(self, config: Dict = None) -> None: + def __init__(self, config: Dict) -> None: """ Load the custom class from config parameter - :param config: configuration dictionary or None + :param config: configuration dictionary """ - config = config or {} # Verify the hyperopt_loss is in the configuration, otherwise fallback to the # default hyperopt loss @@ -100,7 +97,8 @@ class HyperOptLossResolver(IResolver): if not hasattr(self.hyperoptloss, 'hyperopt_loss_function'): raise OperationalException( - f"Found hyperopt {hyperoptloss_name} does not implement `hyperopt_loss_function`.") + f"Found HyperoptLoss class {hyperoptloss_name} does not " + "implement `hyperopt_loss_function`.") def _load_hyperoptloss( self, hyper_loss_name: str, config: Dict, From 89283ef486ad48a03f0ee694a1784be1b279702b Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Oct 2019 19:42:28 +0200 Subject: [PATCH 28/30] Rename --custom-hyperopt to --hyperopt --- .travis.yml | 2 +- docs/bot-usage.md | 4 ++-- docs/hyperopt.md | 2 +- freqtrade/configuration/cli_options.py | 2 +- freqtrade/resolvers/hyperopt_resolver.py | 2 +- tests/optimize/test_hyperopt.py | 14 +++++++------- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index a45334dd6..14466d2c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ jobs: name: backtest - script: - cp config.json.example config.json - - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy --customhyperopt DefaultHyperOpts + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy --hyperopt DefaultHyperOpts name: hyperopt - script: flake8 name: flake8 diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 8f7e0bbcf..cf59bc11f 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -266,7 +266,7 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [-i TICKER_INTERVAL] [--timerange TIMERANGE] [--max_open_trades INT] [--stake_amount STAKE_AMOUNT] - [--customhyperopt NAME] [--hyperopt-path PATH] + [--hyperopt NAME] [--hyperopt-path PATH] [--eps] [-e INT] [--spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]] [--dmmp] [--print-all] [--no-color] [--print-json] @@ -284,7 +284,7 @@ optional arguments: Specify max_open_trades to use. --stake_amount STAKE_AMOUNT Specify stake_amount. - --customhyperopt NAME + --hyperopt NAME Specify hyperopt class name which will be used by the bot. --hyperopt-path PATH Specify additional lookup path for Hyperopts and diff --git a/docs/hyperopt.md b/docs/hyperopt.md index e6f753072..66c250eb7 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -239,7 +239,7 @@ Because hyperopt tries a lot of combinations to find the best parameters it will We strongly recommend to use `screen` or `tmux` to prevent any connection loss. ```bash -freqtrade hyperopt --config config.json --customhyperopt -e 5000 --spaces all +freqtrade hyperopt --config config.json --hyperopt -e 5000 --spaces all ``` Use `` as the name of the custom hyperopt used. diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index ee0d94023..3a4629ada 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -152,7 +152,7 @@ AVAILABLE_CLI_OPTIONS = { ), # Hyperopt "hyperopt": Arg( - '--customhyperopt', + '--hyperopt', help='Specify hyperopt class name which will be used by the bot.', metavar='NAME', ), diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 15080cda5..a51935500 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -28,7 +28,7 @@ class HyperOptResolver(IResolver): :param config: configuration dictionary """ if not config.get('hyperopt'): - raise OperationalException("No Hyperopt set. Please use `--customhyperopt` to specify " + raise OperationalException("No Hyperopt set. Please use `--hyperopt` to specify " "the Hyperopt class to use.") hyperopt_name = config['hyperopt'] diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index e1ee649c8..1c89bb37c 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -73,7 +73,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca args = [ 'hyperopt', '--config', 'config.json', - '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpts', ] config = setup_configuration(get_args(args), RunMode.HYPEROPT) @@ -105,7 +105,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo args = [ 'hyperopt', '--config', 'config.json', - '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpts', '--datadir', '/foo/bar', '--ticker-interval', '1m', '--timerange', ':100', @@ -181,7 +181,7 @@ def test_hyperoptresolver_wrongname(mocker, default_conf, caplog) -> None: def test_hyperoptresolver_noname(default_conf): default_conf['hyperopt'] = '' with pytest.raises(OperationalException, - match="No Hyperopt set. Please use `--customhyperopt` to specify " + match="No Hyperopt set. Please use `--hyperopt` to specify " "the Hyperopt class to use."): HyperOptResolver(default_conf) @@ -214,7 +214,7 @@ def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None args = [ 'hyperopt', '--config', 'config.json', - '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpts', '--epochs', '5' ] args = get_args(args) @@ -232,7 +232,7 @@ def test_start(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpts', '--epochs', '5' ] args = get_args(args) @@ -255,7 +255,7 @@ def test_start_no_data(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpts', '--epochs', '5' ] args = get_args(args) @@ -273,7 +273,7 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpts', '--epochs', '5' ] args = get_args(args) From a5c83b66df0274e126be59d997d5aae210589159 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 15 Oct 2019 06:51:03 +0200 Subject: [PATCH 29/30] Add --dry-run to trade command --- docs/bot-usage.md | 5 +++-- freqtrade/configuration/arguments.py | 2 +- freqtrade/configuration/cli_options.py | 5 +++++ freqtrade/configuration/configuration.py | 4 ++++ tests/test_configuration.py | 17 +++++++++++++++++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 8f7e0bbcf..a258ee7f5 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -36,7 +36,7 @@ optional arguments: ``` usage: freqtrade trade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] [--strategy-path PATH] - [--db-url PATH] [--sd-notify] + [--db-url PATH] [--sd-notify] [--dry-run] optional arguments: -h, --help show this help message and exit @@ -44,6 +44,8 @@ optional arguments: deployments (default: `sqlite:///tradesv3.sqlite` for Live Run mode, `sqlite://` for Dry Run). --sd-notify Notify systemd service manager. + --dry-run Enforce dry-run for trading, removes API keys and + simulates trades. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). @@ -63,7 +65,6 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. - ``` ### How to specify which configuration file be used? diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index a8d4b48f1..5735599df 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -12,7 +12,7 @@ ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_dat ARGS_STRATEGY = ["strategy", "strategy_path"] -ARGS_TRADE = ["db_url", "sd_notify"] +ARGS_TRADE = ["db_url", "sd_notify", "dry_run"] ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange", "max_open_trades", "stake_amount"] diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index ee0d94023..400d08e37 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -86,6 +86,11 @@ AVAILABLE_CLI_OPTIONS = { help='Notify systemd service manager.', action='store_true', ), + "dry_run": Arg( + '--dry-run', + help='Enforce dry-run for trading, removes API keys and simulates trades.', + action='store_true', + ), # Optimize common "ticker_interval": Arg( '-i', '--ticker-interval', diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index ac27a5c99..7e992d6dd 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -162,6 +162,10 @@ class Configuration: if 'sd_notify' in self.args and self.args["sd_notify"]: config['internals'].update({'sd_notify': True}) + self._args_to_config(config, argname='dry_run', + logstring='Parameter --dry-run detected, ' + 'overriding dry_run to: {} ...') + def _process_datadir_options(self, config: Dict[str, Any]) -> None: """ Extract information for sys.argv and load directory configurations diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 333a8992a..67c97a0bf 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -304,6 +304,23 @@ def test_load_config_with_params(default_conf, mocker) -> None: assert validated_conf.get('db_url') == DEFAULT_DB_DRYRUN_URL +@pytest.mark.parametrize("config_value,expected,arglist", [ + (True, True, ['trade', '--dry-run']), # Leave config untouched + (False, True, ['trade', '--dry-run']), # Override config untouched + (False, False, ['trade']), # Leave config untouched + (True, True, ['trade']), # Leave config untouched +]) +def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) -> None: + + default_conf['dry_run'] = config_value + patched_configuration_load_config_file(mocker, default_conf) + + configuration = Configuration(Arguments(arglist).get_parsed_arg()) + validated_conf = configuration.load_config() + + assert validated_conf.get('dry_run') is expected + + def test_load_custom_strategy(default_conf, mocker) -> None: default_conf.update({ 'strategy': 'CustomStrategy', From 6fb96183c08342343604cb5c93296f812a4849b1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 15 Oct 2019 12:26:06 +0200 Subject: [PATCH 30/30] Reword help string --- docs/bot-usage.md | 4 ++-- freqtrade/configuration/cli_options.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index a258ee7f5..9af960858 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -44,8 +44,8 @@ optional arguments: deployments (default: `sqlite:///tradesv3.sqlite` for Live Run mode, `sqlite://` for Dry Run). --sd-notify Notify systemd service manager. - --dry-run Enforce dry-run for trading, removes API keys and - simulates trades. + --dry-run Enforce dry-run for trading (removes Exchange secrets + and simulates trades). Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index 400d08e37..b3dcd52c2 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -88,7 +88,7 @@ AVAILABLE_CLI_OPTIONS = { ), "dry_run": Arg( '--dry-run', - help='Enforce dry-run for trading, removes API keys and simulates trades.', + help='Enforce dry-run for trading (removes Exchange secrets and simulates trades).', action='store_true', ), # Optimize common