From cd2336887cc74b6a518d84f32d7aa6f1691b4824 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Sep 2019 17:14:04 +0200 Subject: [PATCH 01/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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 From 2d34c0f52de724e87f728ca1c749128eb3cfb253 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 20 Oct 2019 19:35:38 +0200 Subject: [PATCH 31/37] Update helpstring exports --- docs/bot-usage.md | 47 +++++++++++++------------- freqtrade/configuration/cli_options.py | 2 +- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index d2b9757f5..8c85965a4 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -9,19 +9,21 @@ This page explains the different parameters of the bot and how to run it. ``` usage: freqtrade [-h] [-V] - {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,download-data,plot-dataframe,plot-profit} + {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,download-data,plot-dataframe,plot-profit} ... Free, open source crypto trading bot positional arguments: - {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,download-data,plot-dataframe,plot-profit} + {trade,backtesting,edge,hyperopt,create-userdir,list-exchanges,list-timeframes,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. + list-timeframes Print available ticker intervals (timeframes) for the + exchange. download-data Download backtesting data. plot-dataframe Plot candles with indicators. plot-profit Generate plot showing profits. @@ -29,6 +31,7 @@ positional arguments: optional arguments: -h, --help show this help message and exit -V, --version show program's version number and exit + ``` ### Bot trading commands @@ -190,7 +193,8 @@ 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] + [--stake_amount STAKE_AMOUNT] [--fee FLOAT] + [--eps] [--dmmp] [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] [--export EXPORT] [--export-filename PATH] @@ -225,11 +229,10 @@ optional arguments: --export EXPORT Export backtest results, argument are: trades. Example: `--export=trades` --export-filename PATH - 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` + Save backtest results to the file with this filename. + 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). @@ -246,8 +249,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. ``` @@ -268,9 +271,9 @@ 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] - [--hyperopt NAME] [--hyperopt-path PATH] - [--eps] [-e INT] + [--stake_amount STAKE_AMOUNT] [--fee FLOAT] + [--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] [-j JOBS] [--random-state INT] [--min-trades INT] @@ -287,8 +290,9 @@ optional arguments: Specify max_open_trades to use. --stake_amount STAKE_AMOUNT Specify stake_amount. - --hyperopt NAME - Specify hyperopt class name which will be used by the + --fee FLOAT Specify fee ratio. Will be applied twice (on trade + entry and exit). + --hyperopt NAME Specify hyperopt class name which will be used by the bot. --hyperopt-path PATH Specify additional lookup path for Hyperopts and Hyperopt Loss functions. @@ -328,7 +332,6 @@ optional arguments: 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. @@ -344,10 +347,9 @@ 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. - ``` ## Edge commands @@ -359,7 +361,7 @@ 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] + [--fee FLOAT] [--stoplosses STOPLOSS_RANGE] optional arguments: -h, --help show this help message and exit @@ -395,10 +397,9 @@ 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. - ``` To understand edge and how to read the results, please read the [edge documentation](edge.md). diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index a62de10eb..ac72cff0b 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -140,7 +140,7 @@ AVAILABLE_CLI_OPTIONS = { ), "exportfilename": Arg( '--export-filename', - help='Save backtest results to the file with this filename (default: `%(default)s`). ' + help='Save backtest results to the file with this filename. ' 'Requires `--export` to be set as well. ' 'Example: `--export-filename=user_data/backtest_results/backtest_today.json`', metavar='PATH', From 1c503f39b2226ae19133a7c03131cede6905f03b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 20 Oct 2019 19:54:38 +0200 Subject: [PATCH 32/37] Handle some merge aftermaths --- .travis.yml | 2 +- freqtrade/configuration/arguments.py | 39 ++++++++++++++++------------ tests/optimize/test_hyperopt.py | 38 +++++++++++++-------------- tests/strategy/test_strategy.py | 2 +- tests/test_arguments.py | 4 +-- tests/test_utils.py | 4 +-- 6 files changed, 47 insertions(+), 42 deletions(-) diff --git a/.travis.yml b/.travis.yml index 14466d2c4..d5d4ad8c5 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 --hyperopt DefaultHyperOpts + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy --hyperopt DefaultHyperOpt name: hyperopt - script: flake8 name: flake8 diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index b093a5015..bc58fc8e8 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -153,41 +153,46 @@ class Arguments: 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.', - parents=[_common_parser], - ) + 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 list-timeframes subcommand list_timeframes_cmd = subparsers.add_parser( 'list-timeframes', - help='Print available ticker intervals (timeframes) for the exchange.' + help='Print available ticker intervals (timeframes) for the exchange.', + parents=[_common_parser], ) list_timeframes_cmd.set_defaults(func=start_list_timeframes) self._build_args(optionlist=ARGS_LIST_TIMEFRAMES, parser=list_timeframes_cmd) # Add download-data subcommand - download_data_cmd = subparsers.add_parser('download-data', - help='Download backtesting data.', - parents=[_common_parser], - ) + 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 - plot_dataframe_cmd = subparsers.add_parser('plot-dataframe', - help='Plot candles with indicators.', - parents=[_common_parser, _strategy_parser], - ) + plot_dataframe_cmd = subparsers.add_parser( + 'plot-dataframe', + help='Plot candles with indicators.', + 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) # Plot profit - plot_profit_cmd = subparsers.add_parser('plot-profit', - help='Generate plot showing profits.', - parents=[_common_parser], - ) + 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) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index c9ecb63dc..6bed0f8cc 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -27,7 +27,7 @@ from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, def hyperopt(default_conf, mocker): default_conf.update({ 'spaces': ['all'], - 'hyperopt': 'DefaultHyperOpts', + 'hyperopt': 'DefaultHyperOpt', }) patch_exchange(mocker) return Hyperopt(default_conf) @@ -73,7 +73,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpt', ] 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', - '--hyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpt', '--datadir', '/foo/bar', '--ticker-interval', '1m', '--timerange', ':100', @@ -160,7 +160,7 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt', MagicMock(return_value=hyperopt(default_conf)) ) - default_conf.update({'hyperopt': 'DefaultHyperOpts'}) + default_conf.update({'hyperopt': 'DefaultHyperOpt'}) x = HyperOptResolver(default_conf).hyperopt assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_sell_trend') @@ -214,7 +214,7 @@ def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpt', '--epochs', '5' ] args = get_args(args) @@ -232,7 +232,7 @@ def test_start(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpt', '--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', - '--hyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpt', '--epochs', '5' ] args = get_args(args) @@ -273,7 +273,7 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpts', + '--hyperopt', 'DefaultHyperOpt', '--epochs', '5' ] args = get_args(args) @@ -425,7 +425,7 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', - 'hyperopt': 'DefaultHyperOpts', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -530,7 +530,7 @@ 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': 'DefaultHyperOpt', 'timerange': None, 'spaces': 'all', 'hyperopt_min_trades': 1, @@ -597,7 +597,7 @@ 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': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -614,7 +614,7 @@ 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': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -644,7 +644,7 @@ 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': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -682,7 +682,7 @@ 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': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -721,7 +721,7 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys) patch_exchange(mocker) default_conf.update({'config': 'config.json.example', - 'hyperopt': 'DefaultHyperOpts', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -763,7 +763,7 @@ def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) - patch_exchange(mocker) default_conf.update({'config': 'config.json.example', - 'hyperopt': 'DefaultHyperOpts', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -797,7 +797,7 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', - 'hyperopt': 'DefaultHyperOpts', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'buy', @@ -843,7 +843,7 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None patch_exchange(mocker) default_conf.update({'config': 'config.json.example', - 'hyperopt': 'DefaultHyperOpts', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': 'sell', @@ -891,7 +891,7 @@ def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, metho patch_exchange(mocker) default_conf.update({'config': 'config.json.example', - 'hyperopt': 'DefaultHyperOpts', + 'hyperopt': 'DefaultHyperOpt', 'epochs': 1, 'timerange': None, 'spaces': space, diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 285ac1bbf..97affc99c 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -61,7 +61,7 @@ def test_load_strategy_invalid_directory(result, caplog, default_conf): assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog) - assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) + assert 'rsi' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) def test_load_not_found_strategy(default_conf): diff --git a/tests/test_arguments.py b/tests/test_arguments.py index be711aeda..d8fbace0f 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -186,7 +186,7 @@ def test_config_notallowed(mocker) -> None: ] pargs = Arguments(args).get_parsed_arg() - assert pargs["config"] is None + assert "config" not in pargs # When file exists: mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) @@ -195,7 +195,7 @@ def test_config_notallowed(mocker) -> None: ] pargs = Arguments(args).get_parsed_arg() # config is not added even if it exists, since create-userdir is in the notallowed list - assert pargs["config"] is None + assert "config" not in pargs def test_config_notrequired(mocker) -> None: diff --git a/tests/test_utils.py b/tests/test_utils.py index f798dcfb8..4e76bb6ca 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -95,8 +95,8 @@ def test_list_timeframes(mocker, capsys): # Test with --config config.json.example args = [ - '--config', 'config.json.example', "list-timeframes", + '--config', 'config.json.example', ] start_list_timeframes(get_args(args)) captured = capsys.readouterr() @@ -139,8 +139,8 @@ def test_list_timeframes(mocker, capsys): # Test with --one-column args = [ - '--config', 'config.json.example', "list-timeframes", + '--config', 'config.json.example', "--one-column", ] start_list_timeframes(get_args(args)) From e1edf363076b9b6397e0f2d78a7323f922fb5989 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 24 Oct 2019 06:22:05 +0200 Subject: [PATCH 33/37] Fix test failures --- freqtrade/configuration/arguments.py | 6 +++-- tests/test_utils.py | 34 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index e62921af8..29d0d98a2 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -179,7 +179,8 @@ class Arguments: # Add list-markets subcommand list_markets_cmd = subparsers.add_parser( 'list-markets', - help='Print markets on exchange.' + help='Print markets on exchange.', + parents=[_common_parser], ) list_markets_cmd.set_defaults(func=partial(start_list_markets, pairs_only=False)) self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_markets_cmd) @@ -187,7 +188,8 @@ class Arguments: # Add list-pairs subcommand list_pairs_cmd = subparsers.add_parser( 'list-pairs', - help='Print pairs on exchange.' + help='Print pairs on exchange.', + parents=[_common_parser], ) list_pairs_cmd.set_defaults(func=partial(start_list_markets, pairs_only=True)) self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_pairs_cmd) diff --git a/tests/test_utils.py b/tests/test_utils.py index c598cfd76..8cd9c3aef 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -182,8 +182,8 @@ def test_list_markets(mocker, markets, capsys): # Test with --config config.json.example args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--print-list", ] start_list_markets(get_args(args), False) @@ -208,8 +208,8 @@ def test_list_markets(mocker, markets, capsys): patch_exchange(mocker, api_mock=api_mock, id="bittrex") # Test with --all: all markets args = [ - '--config', 'config.json.example', "list-markets", "--all", + '--config', 'config.json.example', "--print-list", ] start_list_markets(get_args(args), False) @@ -221,8 +221,8 @@ def test_list_markets(mocker, markets, capsys): # Test list-pairs subcommand: active pairs args = [ - '--config', 'config.json.example', "list-pairs", + '--config', 'config.json.example', "--print-list", ] start_list_markets(get_args(args), True) @@ -233,8 +233,8 @@ def test_list_markets(mocker, markets, capsys): # Test list-pairs subcommand with --all: all pairs args = [ - '--config', 'config.json.example', "list-pairs", "--all", + '--config', 'config.json.example', "--print-list", ] start_list_markets(get_args(args), True) @@ -246,8 +246,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=ETH, LTC args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "ETH", "LTC", "--print-list", ] @@ -259,8 +259,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--print-list", ] @@ -272,8 +272,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, quote=USDT, USD args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--quote", "USDT", "USD", "--print-list", ] @@ -285,8 +285,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, quote=USDT args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--quote", "USDT", "--print-list", ] @@ -298,8 +298,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=USDT args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--quote", "USDT", "--print-list", ] @@ -311,8 +311,8 @@ def test_list_markets(mocker, markets, capsys): # active pairs, base=LTC, quote=USDT args = [ - '--config', 'config.json.example', "list-pairs", + '--config', 'config.json.example', "--base", "LTC", "--quote", "USDT", "--print-list", ] @@ -324,8 +324,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=USDT, NONEXISTENT args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--quote", "USDT", "NONEXISTENT", "--print-list", ] @@ -337,8 +337,8 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=NONEXISTENT args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--quote", "NONEXISTENT", "--print-list", ] @@ -350,8 +350,8 @@ def test_list_markets(mocker, markets, capsys): # Test tabular output args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', ] start_list_markets(get_args(args), False) captured = capsys.readouterr() @@ -360,8 +360,8 @@ def test_list_markets(mocker, markets, capsys): # Test tabular output, no markets found args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--base", "LTC", "--quote", "NONEXISTENT", ] start_list_markets(get_args(args), False) @@ -372,8 +372,8 @@ def test_list_markets(mocker, markets, capsys): # Test --print-json args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--print-json" ] start_list_markets(get_args(args), False) @@ -383,8 +383,8 @@ def test_list_markets(mocker, markets, capsys): # Test --print-csv args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--print-csv" ] start_list_markets(get_args(args), False) @@ -395,8 +395,8 @@ def test_list_markets(mocker, markets, capsys): # Test --one-column args = [ - '--config', 'config.json.example', "list-markets", + '--config', 'config.json.example', "--one-column" ] start_list_markets(get_args(args), False) From 13255b370c270a556f2e5883de0ac88abc801667 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 24 Oct 2019 06:30:07 +0200 Subject: [PATCH 34/37] Allow non-config to parse config --- freqtrade/configuration/configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 7bb49dc31..393fd78be 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -93,7 +93,7 @@ class Configuration: :return: Configuration dictionary """ # Load all configs - config: Dict[str, Any] = self.load_from_files(self.args["config"]) + config: Dict[str, Any] = self.load_from_files(self.args.get("config", [])) # Keep a copy of the original configuration file config['original_config'] = deepcopy(config) From 5b62ad876e0792cdd98909ef6904d9ef5bcd6376 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 13 Nov 2019 09:38:06 +0100 Subject: [PATCH 35/37] Remove hyperopts occurances --- .travis.yml | 2 +- docs/bot-usage.md | 4 ++-- freqtrade/configuration/cli_options.py | 4 ++-- freqtrade/optimize/__init__.py | 2 +- freqtrade/optimize/hyperopt_interface.py | 6 +++--- freqtrade/optimize/hyperopt_loss_interface.py | 4 ++-- freqtrade/resolvers/hyperopt_resolver.py | 2 +- freqtrade/utils.py | 2 +- user_data/hyperopts/sample_hyperopt.py | 2 +- user_data/hyperopts/sample_hyperopt_advanced.py | 2 +- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5a8093ec..8aaff553f 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 SampleStrategy --hyperopt SampleHyperOpts + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt name: hyperopt - script: flake8 name: flake8 diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 8c85965a4..b88e33bd5 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -131,7 +131,7 @@ You can add the entry "user_data_dir" setting to your configuration, to always p Alternatively, pass in `--userdir` to every command. The bot will fail to start if the directory does not exist, but will create necessary subdirectories. -This directory should contain your custom strategies, custom hyperopts and hyperopt loss functions, backtesting historical data (downloaded using either backtesting command or the download script) and plot outputs. +This directory should contain your custom strategies, custom hyperopt and hyperopt loss functions, backtesting historical data (downloaded using either backtesting command or the download script) and plot outputs. It is recommended to use version control to keep track of changes to your strategies. @@ -294,7 +294,7 @@ optional arguments: entry and exit). --hyperopt NAME Specify hyperopt class name which will be used by the bot. - --hyperopt-path PATH Specify additional lookup path for Hyperopts and + --hyperopt-path PATH Specify additional lookup path for Hyperopt and Hyperopt Loss functions. --eps, --enable-position-stacking Allow buying the same pair multiple times (position diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index ff2178108..6dc5ef026 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -166,7 +166,7 @@ AVAILABLE_CLI_OPTIONS = { ), "hyperopt_path": Arg( '--hyperopt-path', - help='Specify additional lookup path for Hyperopts and Hyperopt Loss functions.', + help='Specify additional lookup path for Hyperopt and Hyperopt Loss functions.', metavar='PATH', ), "epochs": Arg( @@ -239,7 +239,7 @@ 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, diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 3adf5eb43..1f2f588ef 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -78,7 +78,7 @@ def start_hyperopt(args: Dict[str, Any]) -> None: except Timeout: logger.info("Another running instance of freqtrade Hyperopt detected.") logger.info("Simultaneous execution of multiple Hyperopt commands is not supported. " - "Hyperopt module is resource hungry. Please run your Hyperopts sequentially " + "Hyperopt module is resource hungry. Please run your Hyperopt sequentially " "or on separate machines.") logger.info("Quitting now.") # TODO: return False here in order to help freqtrade to exit diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 142f305df..5cfd98632 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -1,6 +1,6 @@ """ IHyperOpt interface -This module defines the interface to apply for hyperopts +This module defines the interface to apply for hyperopt """ import logging import math @@ -27,8 +27,8 @@ def _format_exception_message(method: str, space: str) -> str: class IHyperOpt(ABC): """ - Interface for freqtrade hyperopts - Defines the mandatory structure must follow any custom hyperopts + Interface for freqtrade hyperopt + Defines the mandatory structure must follow any custom hyperopt Class attributes you can use: ticker_interval -> int: value of the ticker interval to use for the strategy diff --git a/freqtrade/optimize/hyperopt_loss_interface.py b/freqtrade/optimize/hyperopt_loss_interface.py index b11b6e661..879a9f0e9 100644 --- a/freqtrade/optimize/hyperopt_loss_interface.py +++ b/freqtrade/optimize/hyperopt_loss_interface.py @@ -1,6 +1,6 @@ """ IHyperOptLoss interface -This module defines the interface for the loss-function for hyperopts +This module defines the interface for the loss-function for hyperopt """ from abc import ABC, abstractmethod @@ -11,7 +11,7 @@ from pandas import DataFrame class IHyperOptLoss(ABC): """ - Interface for freqtrade hyperopts Loss functions. + Interface for freqtrade hyperopt Loss functions. Defines the custom loss function (`hyperopt_loss_function()` which is evaluated every epoch.) """ ticker_interval: str diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 72816a9ce..df1ff182c 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -1,7 +1,7 @@ # pragma pylint: disable=attribute-defined-outside-init """ -This module load custom hyperopts +This module load custom hyperopt """ import logging from pathlib import Path diff --git a/freqtrade/utils.py b/freqtrade/utils.py index ce1f8a7c5..ee7e3296f 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -70,7 +70,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: def start_create_userdir(args: Dict[str, Any]) -> None: """ - Create "user_data" directory to contain user data strategies, hyperopts, ...) + Create "user_data" directory to contain user data strategies, hyperopt, ...) :param args: Cli args from Arguments() :return: None """ diff --git a/user_data/hyperopts/sample_hyperopt.py b/user_data/hyperopts/sample_hyperopt.py index 2721ab405..3be05f121 100644 --- a/user_data/hyperopts/sample_hyperopt.py +++ b/user_data/hyperopts/sample_hyperopt.py @@ -12,7 +12,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.optimize.hyperopt_interface import IHyperOpt -class SampleHyperOpts(IHyperOpt): +class SampleHyperOpt(IHyperOpt): """ This is a sample Hyperopt to inspire you. Feel free to customize it. diff --git a/user_data/hyperopts/sample_hyperopt_advanced.py b/user_data/hyperopts/sample_hyperopt_advanced.py index c5d28878c..66182edcf 100644 --- a/user_data/hyperopts/sample_hyperopt_advanced.py +++ b/user_data/hyperopts/sample_hyperopt_advanced.py @@ -14,7 +14,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.optimize.hyperopt_interface import IHyperOpt -class AdvancedSampleHyperOpts(IHyperOpt): +class AdvancedSampleHyperOpt(IHyperOpt): """ This is a sample hyperopt to inspire you. Feel free to customize it. From c42c5a1f85fb38eeefa72a2b0df2da0b18fd25a3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 13 Nov 2019 10:03:59 +0100 Subject: [PATCH 36/37] Adjust "requires subcommand" message --- freqtrade/main.py | 9 ++++++--- tests/test_main.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index d984ff487..0a2adf71a 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -38,9 +38,12 @@ def main(sysargv: List[str] = None) -> None: 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`" + "Usage of Freqtrade requires a subcommand to be specified.\n" + "To have the previous behavior (bot executing trades in live/dry-run modes, " + "depending on the value of the `dry_run` setting in the config), run freqtrade " + "as `freqtrade trade [options...]`.\n" + "To see the full list of options available, please use " + "`freqtrade --help` or `freqtrade --help`." ) except SystemExit as e: diff --git a/tests/test_main.py b/tests/test_main.py index dac960886..4e97c375d 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -18,7 +18,7 @@ from tests.conftest import (log_has, log_has_re, patch_exchange, def test_parse_args_None(caplog) -> None: with pytest.raises(SystemExit): main([]) - assert log_has_re(r"Usage of freqtrade requires a subcommand\.", caplog) + assert log_has_re(r"Usage of Freqtrade requires a subcommand.*", caplog) def test_parse_args_backtesting(mocker) -> None: From 66619204bacefeb3f42d52a6d8f1fbd50cea5a8a Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 13 Nov 2019 11:13:48 +0100 Subject: [PATCH 37/37] re-add hyperopts multiple ... --- docs/bot-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index b88e33bd5..4665878d4 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -131,7 +131,7 @@ You can add the entry "user_data_dir" setting to your configuration, to always p Alternatively, pass in `--userdir` to every command. The bot will fail to start if the directory does not exist, but will create necessary subdirectories. -This directory should contain your custom strategies, custom hyperopt and hyperopt loss functions, backtesting historical data (downloaded using either backtesting command or the download script) and plot outputs. +This directory should contain your custom strategies, custom hyperopts and hyperopt loss functions, backtesting historical data (downloaded using either backtesting command or the download script) and plot outputs. It is recommended to use version control to keep track of changes to your strategies.