diff --git a/docs/utils.md b/docs/utils.md index 793c84a93..8c7e381ff 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -432,9 +432,9 @@ usage: freqtrade hyperopt-list [-h] [-v] [--logfile FILE] [-V] [-c PATH] [--max-trades INT] [--min-avg-time FLOAT] [--max-avg-time FLOAT] [--min-avg-profit FLOAT] [--max-avg-profit FLOAT] - [--min-total-profit FLOAT] - [--max-total-profit FLOAT] [--no-color] - [--print-json] [--no-details] + [--min-total-profit FLOAT] [--max-total-profit FLOAT] + [--min-objective FLOAT] [--max-objective FLOAT] + [--no-color] [--print-json] [--no-details] [--export-csv FILE] optional arguments: @@ -453,6 +453,10 @@ optional arguments: Select epochs on above total profit. --max-total-profit FLOAT Select epochs on below total profit. + --min-objective FLOAT + Select epochs on above objective (- is added by default). + --max-objective FLOAT + Select epochs on below objective (- is added by default). --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. diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index e6f6f8167..4a87def88 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -73,6 +73,7 @@ ARGS_HYPEROPT_LIST = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_list_min_avg_time", "hyperopt_list_max_avg_time", "hyperopt_list_min_avg_profit", "hyperopt_list_max_avg_profit", "hyperopt_list_min_total_profit", "hyperopt_list_max_total_profit", + "hyperopt_list_min_objective", "hyperopt_list_max_objective", "print_colorized", "print_json", "hyperopt_list_no_details", "export_csv"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 3ed2f81d1..8eb5c3ce8 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -455,37 +455,49 @@ AVAILABLE_CLI_OPTIONS = { ), "hyperopt_list_min_avg_time": Arg( '--min-avg-time', - help='Select epochs on above average time.', + help='Select epochs above average time.', type=float, metavar='FLOAT', ), "hyperopt_list_max_avg_time": Arg( '--max-avg-time', - help='Select epochs on under average time.', + help='Select epochs below average time.', type=float, metavar='FLOAT', ), "hyperopt_list_min_avg_profit": Arg( '--min-avg-profit', - help='Select epochs on above average profit.', + help='Select epochs above average profit.', type=float, metavar='FLOAT', ), "hyperopt_list_max_avg_profit": Arg( '--max-avg-profit', - help='Select epochs on below average profit.', + help='Select epochs below average profit.', type=float, metavar='FLOAT', ), "hyperopt_list_min_total_profit": Arg( '--min-total-profit', - help='Select epochs on above total profit.', + help='Select epochs above total profit.', type=float, metavar='FLOAT', ), "hyperopt_list_max_total_profit": Arg( '--max-total-profit', - help='Select epochs on below total profit.', + help='Select epochs below total profit.', + type=float, + metavar='FLOAT', + ), + "hyperopt_list_min_objective": Arg( + '--min-objective', + help='Select epochs above objective.', + type=float, + metavar='FLOAT', + ), + "hyperopt_list_max_objective": Arg( + '--max-objective', + help='Select epochs below objective.', type=float, metavar='FLOAT', ), diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index 517f47d16..4fae51e28 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -35,7 +35,9 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: 'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None), 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None), 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None), - 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None) + 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None), + 'filter_min_objective': config.get('hyperopt_list_min_objective', None), + 'filter_max_objective': config.get('hyperopt_list_max_objective', None), } results_file = (config['user_data_dir'] / @@ -45,7 +47,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: epochs = Hyperopt.load_previous_results(results_file) total_epochs = len(epochs) - epochs = _hyperopt_filter_epochs(epochs, filteroptions) + epochs = hyperopt_filter_epochs(epochs, filteroptions) if print_colorized: colorama_init(autoreset=True) @@ -92,14 +94,16 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None: 'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None), 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None), 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None), - 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None) + 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None), + 'filter_min_objective': config.get('hyperopt_list_min_objective', None), + 'filter_max_objective': config.get('hyperopt_list_max_objective', None) } # Previous evaluations epochs = Hyperopt.load_previous_results(results_file) total_epochs = len(epochs) - epochs = _hyperopt_filter_epochs(epochs, filteroptions) + epochs = hyperopt_filter_epochs(epochs, filteroptions) filtered_epochs = len(epochs) if n > filtered_epochs: @@ -119,7 +123,7 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None: header_str="Epoch details") -def _hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: +def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: """ Filter our items from the list of hyperopt results """ @@ -127,6 +131,24 @@ def _hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: epochs = [x for x in epochs if x['is_best']] if filteroptions['only_profitable']: epochs = [x for x in epochs if x['results_metrics']['profit'] > 0] + + epochs = _hyperopt_filter_epochs_trade_count(epochs, filteroptions) + + epochs = _hyperopt_filter_epochs_duration(epochs, filteroptions) + + epochs = _hyperopt_filter_epochs_profit(epochs, filteroptions) + + epochs = _hyperopt_filter_epochs_objective(epochs, filteroptions) + + logger.info(f"{len(epochs)} " + + ("best " if filteroptions['only_best'] else "") + + ("profitable " if filteroptions['only_profitable'] else "") + + "epochs found.") + return epochs + + +def _hyperopt_filter_epochs_trade_count(epochs: List, filteroptions: dict) -> List: + if filteroptions['filter_min_trades'] > 0: epochs = [ x for x in epochs @@ -137,6 +159,11 @@ def _hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: x for x in epochs if x['results_metrics']['trade_count'] < filteroptions['filter_max_trades'] ] + return epochs + + +def _hyperopt_filter_epochs_duration(epochs: List, filteroptions: dict) -> List: + if filteroptions['filter_min_avg_time'] is not None: epochs = [x for x in epochs if x['results_metrics']['trade_count'] > 0] epochs = [ @@ -149,6 +176,12 @@ def _hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: x for x in epochs if x['results_metrics']['duration'] < filteroptions['filter_max_avg_time'] ] + + return epochs + + +def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List: + if filteroptions['filter_min_avg_profit'] is not None: epochs = [x for x in epochs if x['results_metrics']['trade_count'] > 0] epochs = [ @@ -173,10 +206,18 @@ def _hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: x for x in epochs if x['results_metrics']['profit'] < filteroptions['filter_max_total_profit'] ] + return epochs - logger.info(f"{len(epochs)} " + - ("best " if filteroptions['only_best'] else "") + - ("profitable " if filteroptions['only_profitable'] else "") + - "epochs found.") + +def _hyperopt_filter_epochs_objective(epochs: List, filteroptions: dict) -> List: + + if filteroptions['filter_min_objective'] is not None: + epochs = [x for x in epochs if x['results_metrics']['trade_count'] > 0] + + epochs = [x for x in epochs if x['loss'] < filteroptions['filter_min_objective']] + if filteroptions['filter_max_objective'] is not None: + epochs = [x for x in epochs if x['results_metrics']['trade_count'] > 0] + + epochs = [x for x in epochs if x['loss'] > filteroptions['filter_max_objective']] return epochs diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 139e42084..08a600176 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -334,6 +334,12 @@ class Configuration: self._args_to_config(config, argname='hyperopt_list_max_total_profit', logstring='Parameter --max-total-profit detected: {}') + self._args_to_config(config, argname='hyperopt_list_min_objective', + logstring='Parameter --min-objective detected: {}') + + self._args_to_config(config, argname='hyperopt_list_max_objective', + logstring='Parameter --max-objective detected: {}') + self._args_to_config(config, argname='hyperopt_list_no_details', logstring='Parameter --no-details detected: {}') diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 3ec7e4798..69d80d2cd 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -736,7 +736,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): args = [ "hyperopt-list", - "--no-details" + "--no-details", ] pargs = get_args(args) pargs['config'] = None @@ -749,7 +749,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): args = [ "hyperopt-list", "--best", - "--no-details" + "--no-details", ] pargs = get_args(args) pargs['config'] = None @@ -763,7 +763,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): args = [ "hyperopt-list", "--profitable", - "--no-details" + "--no-details", ] pargs = get_args(args) pargs['config'] = None @@ -776,7 +776,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): " 11/12", " 12/12"]) args = [ "hyperopt-list", - "--profitable" + "--profitable", ] pargs = get_args(args) pargs['config'] = None @@ -792,7 +792,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): "hyperopt-list", "--no-details", "--no-color", - "--min-trades", "20" + "--min-trades", "20", ] pargs = get_args(args) pargs['config'] = None @@ -806,7 +806,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): "hyperopt-list", "--profitable", "--no-details", - "--max-trades", "20" + "--max-trades", "20", ] pargs = get_args(args) pargs['config'] = None @@ -821,7 +821,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): "hyperopt-list", "--profitable", "--no-details", - "--min-avg-profit", "0.11" + "--min-avg-profit", "0.11", ] pargs = get_args(args) pargs['config'] = None @@ -835,7 +835,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): args = [ "hyperopt-list", "--no-details", - "--max-avg-profit", "0.10" + "--max-avg-profit", "0.10", ] pargs = get_args(args) pargs['config'] = None @@ -849,7 +849,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): args = [ "hyperopt-list", "--no-details", - "--min-total-profit", "0.4" + "--min-total-profit", "0.4", ] pargs = get_args(args) pargs['config'] = None @@ -863,7 +863,35 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): args = [ "hyperopt-list", "--no-details", - "--max-total-profit", "0.4" + "--max-total-profit", "0.4", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", + " 9/12", " 11/12"]) + assert all(x not in captured.out + for x in [" 4/12", " 10/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--min-objective", "0.1", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 10/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", + " 9/12", " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--max-objective", "0.1", ] pargs = get_args(args) pargs['config'] = None @@ -878,7 +906,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): "hyperopt-list", "--profitable", "--no-details", - "--min-avg-time", "2000" + "--min-avg-time", "2000", ] pargs = get_args(args) pargs['config'] = None @@ -892,7 +920,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): args = [ "hyperopt-list", "--no-details", - "--max-avg-time", "1500" + "--max-avg-time", "1500", ] pargs = get_args(args) pargs['config'] = None @@ -906,7 +934,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results): args = [ "hyperopt-list", "--no-details", - "--export-csv", "test_file.csv" + "--export-csv", "test_file.csv", ] pargs = get_args(args) pargs['config'] = None