diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 6553ebd13..08f0408ed 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -34,25 +34,25 @@ from tests.conftest import ( def generate_result_metrics(): return { - 'trade_count': 1, - 'total_trades': 1, - 'avg_profit': 0.1, - 'total_profit': 0.001, - 'profit': 0.01, - 'duration': 20.0, - 'wins': 1, - 'draws': 0, - 'losses': 0, - 'profit_mean': 0.01, - 'profit_total_abs': 0.001, - 'profit_total': 0.01, - 'holding_avg': timedelta(minutes=20), - 'max_drawdown': 0.001, - 'max_drawdown_abs': 0.001, - 'loss': 0.001, - 'is_initial_point': 0.001, - 'is_random': False, - 'is_best': 1, + "trade_count": 1, + "total_trades": 1, + "avg_profit": 0.1, + "total_profit": 0.001, + "profit": 0.01, + "duration": 20.0, + "wins": 1, + "draws": 0, + "losses": 0, + "profit_mean": 0.01, + "profit_total_abs": 0.001, + "profit_total": 0.01, + "holding_avg": timedelta(minutes=20), + "max_drawdown": 0.001, + "max_drawdown_abs": 0.001, + "loss": 0.001, + "is_initial_point": 0.001, + "is_random": False, + "is_best": 1, } @@ -60,104 +60,117 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca patched_configuration_load_config_file(mocker, default_conf) args = [ - 'hyperopt', - '--config', 'config.json', - '--strategy', 'HyperoptableStrategy', + "hyperopt", + "--config", + "config.json", + "--strategy", + "HyperoptableStrategy", ] config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) - assert 'max_open_trades' in config - assert 'stake_currency' in config - assert 'stake_amount' in config - assert 'exchange' in config - assert 'pair_whitelist' in config['exchange'] - assert 'datadir' in config - assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'timeframe' in config + assert "max_open_trades" in config + assert "stake_currency" in config + assert "stake_amount" in config + assert "exchange" in config + assert "pair_whitelist" in config["exchange"] + assert "datadir" in config + assert log_has("Using data directory: {} ...".format(config["datadir"]), caplog) + assert "timeframe" in config - assert 'position_stacking' not in config - assert not log_has('Parameter --enable-position-stacking detected ...', caplog) + assert "position_stacking" not in config + assert not log_has("Parameter --enable-position-stacking detected ...", caplog) - assert 'timerange' not in config - assert 'runmode' in config - assert config['runmode'] == RunMode.HYPEROPT + assert "timerange" not in config + assert "runmode" in config + assert config["runmode"] == RunMode.HYPEROPT def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) - mocker.patch( - 'freqtrade.configuration.configuration.create_datadir', - lambda c, x: x - ) + mocker.patch("freqtrade.configuration.configuration.create_datadir", lambda c, x: x) args = [ - 'hyperopt', - '--config', 'config.json', - '--strategy', 'HyperoptableStrategy', - '--datadir', '/foo/bar', - '--timeframe', '1m', - '--timerange', ':100', - '--enable-position-stacking', - '--disable-max-market-positions', - '--epochs', '1000', - '--spaces', 'default', - '--print-all' + "hyperopt", + "--config", + "config.json", + "--strategy", + "HyperoptableStrategy", + "--datadir", + "/foo/bar", + "--timeframe", + "1m", + "--timerange", + ":100", + "--enable-position-stacking", + "--disable-max-market-positions", + "--epochs", + "1000", + "--spaces", + "default", + "--print-all", ] config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) - assert 'max_open_trades' in config - assert 'stake_currency' in config - assert 'stake_amount' in config - assert 'exchange' in config - assert 'pair_whitelist' in config['exchange'] - assert 'datadir' in config - assert config['runmode'] == RunMode.HYPEROPT + assert "max_open_trades" in config + assert "stake_currency" in config + assert "stake_amount" in config + assert "exchange" in config + assert "pair_whitelist" in config["exchange"] + assert "datadir" in config + assert config["runmode"] == RunMode.HYPEROPT - assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'timeframe' in config - assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', - caplog) + assert log_has("Using data directory: {} ...".format(config["datadir"]), caplog) + assert "timeframe" in config + assert log_has("Parameter -i/--timeframe detected ... Using timeframe: 1m ...", caplog) - assert 'position_stacking' in config - assert log_has('Parameter --enable-position-stacking detected ...', caplog) + assert "position_stacking" in config + assert log_has("Parameter --enable-position-stacking detected ...", caplog) - assert 'use_max_market_positions' in config - assert log_has('Parameter --disable-max-market-positions detected ...', caplog) - assert log_has('max_open_trades set to unlimited ...', caplog) + assert "use_max_market_positions" in config + assert log_has("Parameter --disable-max-market-positions detected ...", caplog) + assert log_has("max_open_trades set to unlimited ...", caplog) - assert 'timerange' in config - assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) + assert "timerange" in config + assert log_has("Parameter --timerange detected: {} ...".format(config["timerange"]), caplog) - assert 'epochs' in config - assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 1000 epochs ...', - caplog) + assert "epochs" in config + assert log_has( + "Parameter --epochs detected ... Will run Hyperopt with for 1000 epochs ...", caplog + ) - assert 'spaces' in config - assert log_has('Parameter -s/--spaces detected: {}'.format(config['spaces']), caplog) - assert 'print_all' in config - assert log_has('Parameter --print-all detected ...', caplog) + assert "spaces" in config + assert log_has("Parameter -s/--spaces detected: {}".format(config["spaces"]), caplog) + assert "print_all" in config + assert log_has("Parameter --print-all detected ...", caplog) def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None: - patched_configuration_load_config_file(mocker, default_conf) args = [ - 'hyperopt', - '--config', 'config.json', - '--strategy', 'HyperoptableStrategy', - '--stake-amount', '1', - '--starting-balance', '2' + "hyperopt", + "--config", + "config.json", + "--strategy", + "HyperoptableStrategy", + "--stake-amount", + "1", + "--starting-balance", + "2", ] conf = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) assert isinstance(conf, dict) args = [ - 'hyperopt', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--stake-amount', '1', - '--starting-balance', '0.5' + "hyperopt", + "--config", + "config.json", + "--strategy", + CURRENT_TEST_STRATEGY, + "--stake-amount", + "1", + "--starting-balance", + "0.5", ] with pytest.raises(OperationalException, match=r"Starting balance .* smaller .*"): setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) @@ -167,15 +180,19 @@ def test_start_not_installed(mocker, default_conf, import_fails) -> None: start_mock = MagicMock() patched_configuration_load_config_file(mocker, default_conf) - mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) + mocker.patch("freqtrade.optimize.hyperopt.Hyperopt.start", start_mock) patch_exchange(mocker) args = [ - 'hyperopt', - '--config', 'config.json', - '--strategy', 'HyperoptableStrategy', - '--epochs', '5', - '--hyperopt-loss', 'SharpeHyperOptLossDaily', + "hyperopt", + "--config", + "config.json", + "--strategy", + "HyperoptableStrategy", + "--epochs", + "5", + "--hyperopt-loss", + "SharpeHyperOptLossDaily", ] pargs = get_args(args) @@ -186,15 +203,19 @@ def test_start_not_installed(mocker, default_conf, import_fails) -> None: def test_start_no_hyperopt_allowed(mocker, hyperopt_conf, caplog) -> None: start_mock = MagicMock() patched_configuration_load_config_file(mocker, hyperopt_conf) - mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) + mocker.patch("freqtrade.optimize.hyperopt.Hyperopt.start", start_mock) patch_exchange(mocker) args = [ - 'hyperopt', - '--config', 'config.json', - '--hyperopt', 'HyperoptTestSepFile', - '--hyperopt-loss', 'SharpeHyperOptLossDaily', - '--epochs', '5' + "hyperopt", + "--config", + "config.json", + "--hyperopt", + "HyperoptTestSepFile", + "--hyperopt-loss", + "SharpeHyperOptLossDaily", + "--epochs", + "5", ] pargs = get_args(args) with pytest.raises(OperationalException, match=r"Using separate Hyperopt files has been.*"): @@ -202,24 +223,28 @@ def test_start_no_hyperopt_allowed(mocker, hyperopt_conf, caplog) -> None: def test_start_no_data(mocker, hyperopt_conf, tmp_path) -> None: - hyperopt_conf['user_data_dir'] = tmp_path + hyperopt_conf["user_data_dir"] = tmp_path patched_configuration_load_config_file(mocker, hyperopt_conf) - mocker.patch('freqtrade.data.history.load_pair_history', MagicMock(return_value=pd.DataFrame)) + mocker.patch("freqtrade.data.history.load_pair_history", MagicMock(return_value=pd.DataFrame)) mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) patch_exchange(mocker) args = [ - 'hyperopt', - '--config', 'config.json', - '--strategy', 'HyperoptableStrategy', - '--hyperopt-loss', 'SharpeHyperOptLossDaily', - '--epochs', '5' + "hyperopt", + "--config", + "config.json", + "--strategy", + "HyperoptableStrategy", + "--hyperopt-loss", + "SharpeHyperOptLossDaily", + "--epochs", + "5", ] pargs = get_args(args) - with pytest.raises(OperationalException, match='No data found. Terminating.'): + with pytest.raises(OperationalException, match="No data found. Terminating."): start_hyperopt(pargs) # Cleanup since that failed hyperopt start leaves a lockfile. @@ -232,15 +257,19 @@ def test_start_no_data(mocker, hyperopt_conf, tmp_path) -> None: def test_start_filelock(mocker, hyperopt_conf, caplog) -> None: hyperopt_mock = MagicMock(side_effect=Timeout(Hyperopt.get_lock_filename(hyperopt_conf))) patched_configuration_load_config_file(mocker, hyperopt_conf) - mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.__init__', hyperopt_mock) + mocker.patch("freqtrade.optimize.hyperopt.Hyperopt.__init__", hyperopt_mock) patch_exchange(mocker) args = [ - 'hyperopt', - '--config', 'config.json', - '--strategy', 'HyperoptableStrategy', - '--hyperopt-loss', 'SharpeHyperOptLossDaily', - '--epochs', '5' + "hyperopt", + "--config", + "config.json", + "--strategy", + "HyperoptableStrategy", + "--hyperopt-loss", + "SharpeHyperOptLossDaily", + "--epochs", + "5", ] pargs = get_args(args) start_hyperopt(pargs) @@ -253,27 +282,28 @@ def test_log_results_if_loss_improves(hyperopt, capsys) -> None: hyperopt.print_results( { - 'loss': 1, - 'results_metrics': generate_result_metrics(), - 'total_profit': 0, - 'current_epoch': 2, # This starts from 1 (in a human-friendly manner) - 'is_initial_point': False, - 'is_random': False, - 'is_best': True + "loss": 1, + "results_metrics": generate_result_metrics(), + "total_profit": 0, + "current_epoch": 2, # This starts from 1 (in a human-friendly manner) + "is_initial_point": False, + "is_random": False, + "is_best": True, } ) out, _err = capsys.readouterr() - assert all(x in out - for x in ["Best", "2/2", " 1", "0.10%", "0.00100000 BTC (1.00%)", "00:20:00"]) + assert all( + x in out for x in ["Best", "2/2", " 1", "0.10%", "0.00100000 BTC (1.00%)", "00:20:00"] + ) def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None: hyperopt.current_best_loss = 2 hyperopt.print_results( { - 'is_best': False, - 'loss': 3, - 'current_epoch': 1, + "is_best": False, + "loss": 3, + "current_epoch": 1, } ) assert caplog.record_tuples == [] @@ -281,57 +311,64 @@ def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None: def test_roi_table_generation(hyperopt) -> None: params = { - 'roi_t1': 5, - 'roi_t2': 10, - 'roi_t3': 15, - 'roi_p1': 1, - 'roi_p2': 2, - 'roi_p3': 3, + "roi_t1": 5, + "roi_t2": 10, + "roi_t3": 15, + "roi_p1": 1, + "roi_p2": 2, + "roi_p3": 3, } assert hyperopt.custom_hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0} def test_params_no_optimize_details(hyperopt) -> None: - hyperopt.config['spaces'] = ['buy'] + hyperopt.config["spaces"] = ["buy"] res = hyperopt._get_no_optimize_details() assert isinstance(res, dict) assert "trailing" in res - assert res["trailing"]['trailing_stop'] is False + assert res["trailing"]["trailing_stop"] is False assert "roi" in res - assert res['roi']['0'] == 0.04 + assert res["roi"]["0"] == 0.04 assert "stoploss" in res - assert res['stoploss']['stoploss'] == -0.1 + assert res["stoploss"]["stoploss"] == -0.1 assert "max_open_trades" in res - assert res['max_open_trades']['max_open_trades'] == 1 + assert res["max_open_trades"]["max_open_trades"] == 1 def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None: - dumper = mocker.patch('freqtrade.optimize.hyperopt.dump') - dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result') - mocker.patch('freqtrade.optimize.hyperopt.calculate_market_change', return_value=1.5) - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') + dumper = mocker.patch("freqtrade.optimize.hyperopt.dump") + dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result") + mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5) + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) # Dummy-reduce points to ensure scikit-learn is forced to generate new values - mocker.patch('freqtrade.optimize.hyperopt.INITIAL_POINTS', 2) + mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2) parallel = mocker.patch( - 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{ - 'loss': 1, 'results_explanation': 'foo result', - 'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0}, - 'results_metrics': generate_result_metrics(), - }]) + "freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel", + MagicMock( + return_value=[ + { + "loss": 1, + "results_explanation": "foo result", + "params": {"buy": {}, "sell": {}, "roi": {}, "stoploss": 0.0}, + "results_metrics": generate_result_metrics(), + } + ] + ), ) patch_exchange(mocker) # Co-test loading timeframe from strategy - del hyperopt_conf['timeframe'] + del hyperopt_conf["timeframe"] hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -342,184 +379,217 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None: parallel.assert_called_once() out, _err = capsys.readouterr() - assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out + assert "Best result:\n\n* 1/1: foo result Objective: 1.00000\n" in out # Should be called for historical candle data assert dumper.call_count == 1 assert dumper2.call_count == 1 assert hasattr(hyperopt.backtesting.strategy, "advise_exit") assert hasattr(hyperopt.backtesting.strategy, "advise_entry") - assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades'] + assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"] assert hasattr(hyperopt.backtesting, "_position_stacking") def test_hyperopt_format_results(hyperopt): - bt_result = { - 'results': pd.DataFrame({"pair": ["UNITTEST/BTC", "UNITTEST/BTC", - "UNITTEST/BTC", "UNITTEST/BTC"], - "profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780], - "profit_abs": [0.000003, 0.000011, 0.000014, 0.000003], - "open_date": [dt_utc(2017, 11, 14, 19, 32, 00), - dt_utc(2017, 11, 14, 21, 36, 00), - dt_utc(2017, 11, 14, 22, 12, 00), - dt_utc(2017, 11, 14, 22, 44, 00)], - "close_date": [dt_utc(2017, 11, 14, 21, 35, 00), - dt_utc(2017, 11, 14, 22, 10, 00), - dt_utc(2017, 11, 14, 22, 43, 00), - dt_utc(2017, 11, 14, 22, 58, 00)], - "open_rate": [0.002543, 0.003003, 0.003089, 0.003214], - "close_rate": [0.002546, 0.003014, 0.003103, 0.003217], - "trade_duration": [123, 34, 31, 14], - "is_open": [False, False, False, True], - "is_short": [False, False, False, False], - "stake_amount": [0.01, 0.01, 0.01, 0.01], - "exit_reason": [ExitType.ROI, ExitType.STOP_LOSS, - ExitType.ROI, ExitType.FORCE_EXIT] - }), - 'config': hyperopt.config, - 'locks': [], - 'final_balance': 0.02, - 'rejected_signals': 2, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'canceled_trade_entries': 0, - 'canceled_entry_orders': 0, - 'replaced_entry_orders': 0, - 'backtest_start_time': 1619718665, - 'backtest_end_time': 1619718665, + "results": pd.DataFrame( + { + "pair": ["UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC"], + "profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780], + "profit_abs": [0.000003, 0.000011, 0.000014, 0.000003], + "open_date": [ + dt_utc(2017, 11, 14, 19, 32, 00), + dt_utc(2017, 11, 14, 21, 36, 00), + dt_utc(2017, 11, 14, 22, 12, 00), + dt_utc(2017, 11, 14, 22, 44, 00), + ], + "close_date": [ + dt_utc(2017, 11, 14, 21, 35, 00), + dt_utc(2017, 11, 14, 22, 10, 00), + dt_utc(2017, 11, 14, 22, 43, 00), + dt_utc(2017, 11, 14, 22, 58, 00), + ], + "open_rate": [0.002543, 0.003003, 0.003089, 0.003214], + "close_rate": [0.002546, 0.003014, 0.003103, 0.003217], + "trade_duration": [123, 34, 31, 14], + "is_open": [False, False, False, True], + "is_short": [False, False, False, False], + "stake_amount": [0.01, 0.01, 0.01, 0.01], + "exit_reason": [ + ExitType.ROI, + ExitType.STOP_LOSS, + ExitType.ROI, + ExitType.FORCE_EXIT, + ], + } + ), + "config": hyperopt.config, + "locks": [], + "final_balance": 0.02, + "rejected_signals": 2, + "timedout_entry_orders": 0, + "timedout_exit_orders": 0, + "canceled_trade_entries": 0, + "canceled_entry_orders": 0, + "replaced_entry_orders": 0, + "backtest_start_time": 1619718665, + "backtest_end_time": 1619718665, } - results_metrics = generate_strategy_stats(['XRP/BTC'], '', bt_result, - dt_utc(2017, 11, 14, 19, 32, 00), - dt_utc(2017, 12, 14, 19, 32, 00), market_change=0) + results_metrics = generate_strategy_stats( + ["XRP/BTC"], + "", + bt_result, + dt_utc(2017, 11, 14, 19, 32, 00), + dt_utc(2017, 12, 14, 19, 32, 00), + market_change=0, + ) - results_explanation = HyperoptTools.format_results_explanation_string(results_metrics, 'BTC') - total_profit = results_metrics['profit_total_abs'] + results_explanation = HyperoptTools.format_results_explanation_string(results_metrics, "BTC") + total_profit = results_metrics["profit_total_abs"] results = { - 'loss': 0.0, - 'params_dict': None, - 'params_details': None, - 'results_metrics': results_metrics, - 'results_explanation': results_explanation, - 'total_profit': total_profit, - 'current_epoch': 1, - 'is_initial_point': True, + "loss": 0.0, + "params_dict": None, + "params_details": None, + "results_metrics": results_metrics, + "results_explanation": results_explanation, + "total_profit": total_profit, + "current_epoch": 1, + "is_initial_point": True, } result = HyperoptTools._format_explanation_string(results, 1) - assert ' 0.71%' in result - assert 'Total profit 0.00003100 BTC' in result - assert '0:50:00 min' in result + assert " 0.71%" in result + assert "Total profit 0.00003100 BTC" in result + assert "0:50:00 min" in result def test_populate_indicators(hyperopt, testdatadir) -> None: - data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True) + data = load_data(testdatadir, "1m", ["UNITTEST/BTC"], fill_up_missing=True) dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data) - dataframe = dataframes['UNITTEST/BTC'] + dataframe = dataframes["UNITTEST/BTC"] # Check if some indicators are generated. We will not test all of them - assert 'adx' in dataframe - assert 'macd' in dataframe - assert 'rsi' in dataframe + assert "adx" in dataframe + assert "macd" in dataframe + assert "rsi" in dataframe def test_generate_optimizer(mocker, hyperopt_conf) -> None: - hyperopt_conf.update({'spaces': 'all', - 'hyperopt_min_trades': 1, - }) + hyperopt_conf.update( + { + "spaces": "all", + "hyperopt_min_trades": 1, + } + ) backtest_result = { - 'results': pd.DataFrame({"pair": ["UNITTEST/BTC", "UNITTEST/BTC", - "UNITTEST/BTC", "UNITTEST/BTC"], - "profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780], - "profit_abs": [0.000003, 0.000011, 0.000014, 0.000003], - "open_date": [dt_utc(2017, 11, 14, 19, 32, 00), - dt_utc(2017, 11, 14, 21, 36, 00), - dt_utc(2017, 11, 14, 22, 12, 00), - dt_utc(2017, 11, 14, 22, 44, 00)], - "close_date": [dt_utc(2017, 11, 14, 21, 35, 00), - dt_utc(2017, 11, 14, 22, 10, 00), - dt_utc(2017, 11, 14, 22, 43, 00), - dt_utc(2017, 11, 14, 22, 58, 00)], - "open_rate": [0.002543, 0.003003, 0.003089, 0.003214], - "close_rate": [0.002546, 0.003014, 0.003103, 0.003217], - "trade_duration": [123, 34, 31, 14], - "is_open": [False, False, False, True], - "is_short": [False, False, False, False], - "stake_amount": [0.01, 0.01, 0.01, 0.01], - "exit_reason": [ExitType.ROI, ExitType.STOP_LOSS, - ExitType.ROI, ExitType.FORCE_EXIT] - }), - 'config': hyperopt_conf, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'canceled_trade_entries': 0, - 'canceled_entry_orders': 0, - 'replaced_entry_orders': 0, - 'final_balance': 1000, + "results": pd.DataFrame( + { + "pair": ["UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC"], + "profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780], + "profit_abs": [0.000003, 0.000011, 0.000014, 0.000003], + "open_date": [ + dt_utc(2017, 11, 14, 19, 32, 00), + dt_utc(2017, 11, 14, 21, 36, 00), + dt_utc(2017, 11, 14, 22, 12, 00), + dt_utc(2017, 11, 14, 22, 44, 00), + ], + "close_date": [ + dt_utc(2017, 11, 14, 21, 35, 00), + dt_utc(2017, 11, 14, 22, 10, 00), + dt_utc(2017, 11, 14, 22, 43, 00), + dt_utc(2017, 11, 14, 22, 58, 00), + ], + "open_rate": [0.002543, 0.003003, 0.003089, 0.003214], + "close_rate": [0.002546, 0.003014, 0.003103, 0.003217], + "trade_duration": [123, 34, 31, 14], + "is_open": [False, False, False, True], + "is_short": [False, False, False, False], + "stake_amount": [0.01, 0.01, 0.01, 0.01], + "exit_reason": [ + ExitType.ROI, + ExitType.STOP_LOSS, + ExitType.ROI, + ExitType.FORCE_EXIT, + ], + } + ), + "config": hyperopt_conf, + "locks": [], + "rejected_signals": 20, + "timedout_entry_orders": 0, + "timedout_exit_orders": 0, + "canceled_trade_entries": 0, + "canceled_entry_orders": 0, + "replaced_entry_orders": 0, + "final_balance": 1000, } - mocker.patch('freqtrade.optimize.hyperopt.Backtesting.backtest', return_value=backtest_result) - mocker.patch('freqtrade.optimize.hyperopt.get_timerange', - return_value=(dt_utc(2017, 12, 10), dt_utc(2017, 12, 13))) + mocker.patch("freqtrade.optimize.hyperopt.Backtesting.backtest", return_value=backtest_result) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + return_value=(dt_utc(2017, 12, 10), dt_utc(2017, 12, 13)), + ) patch_exchange(mocker) - mocker.patch.object(Path, 'open') - mocker.patch('freqtrade.configuration.config_validation.validate_config_schema') - mocker.patch('freqtrade.optimize.hyperopt.load', return_value={'XRP/BTC': None}) + mocker.patch.object(Path, "open") + mocker.patch("freqtrade.configuration.config_validation.validate_config_schema") + mocker.patch("freqtrade.optimize.hyperopt.load", return_value={"XRP/BTC": None}) optimizer_param = { - 'buy_plusdi': 0.02, - 'buy_rsi': 35, - 'sell_minusdi': 0.02, - 'sell_rsi': 75, - 'protection_cooldown_lookback': 20, - 'protection_enabled': True, - 'roi_t1': 60.0, - 'roi_t2': 30.0, - 'roi_t3': 20.0, - 'roi_p1': 0.01, - 'roi_p2': 0.01, - 'roi_p3': 0.1, - 'stoploss': -0.4, - 'trailing_stop': True, - 'trailing_stop_positive': 0.02, - 'trailing_stop_positive_offset_p1': 0.05, - 'trailing_only_offset_is_reached': False, - 'max_open_trades': 3, + "buy_plusdi": 0.02, + "buy_rsi": 35, + "sell_minusdi": 0.02, + "sell_rsi": 75, + "protection_cooldown_lookback": 20, + "protection_enabled": True, + "roi_t1": 60.0, + "roi_t2": 30.0, + "roi_t3": 20.0, + "roi_p1": 0.01, + "roi_p2": 0.01, + "roi_p3": 0.1, + "stoploss": -0.4, + "trailing_stop": True, + "trailing_stop_positive": 0.02, + "trailing_stop_positive_offset_p1": 0.05, + "trailing_only_offset_is_reached": False, + "max_open_trades": 3, } response_expected = { - 'loss': 1.9147239021396234, - 'results_explanation': (' 4 trades. 4/0/0 Wins/Draws/Losses. ' - 'Avg profit 0.77%. Median profit 0.71%. Total profit ' - '0.00003100 BTC ( 0.00%). ' - 'Avg duration 0:50:00 min.' - ), - 'params_details': {'buy': {'buy_plusdi': 0.02, - 'buy_rsi': 35, - }, - 'roi': {"0": 0.12000000000000001, - "20.0": 0.02, - "50.0": 0.01, - "110.0": 0}, - 'protection': {'protection_cooldown_lookback': 20, - 'protection_enabled': True, - }, - 'sell': {'sell_minusdi': 0.02, - 'sell_rsi': 75, - }, - 'stoploss': {'stoploss': -0.4}, - 'trailing': {'trailing_only_offset_is_reached': False, - 'trailing_stop': True, - 'trailing_stop_positive': 0.02, - 'trailing_stop_positive_offset': 0.07}, - 'max_open_trades': {'max_open_trades': 3} - }, - 'params_dict': optimizer_param, - 'params_not_optimized': {'buy': {}, 'protection': {}, 'sell': {}}, - 'results_metrics': ANY, - 'total_profit': 3.1e-08 + "loss": 1.9147239021396234, + "results_explanation": ( + " 4 trades. 4/0/0 Wins/Draws/Losses. " + "Avg profit 0.77%. Median profit 0.71%. Total profit " + "0.00003100 BTC ( 0.00%). " + "Avg duration 0:50:00 min." + ), + "params_details": { + "buy": { + "buy_plusdi": 0.02, + "buy_rsi": 35, + }, + "roi": {"0": 0.12000000000000001, "20.0": 0.02, "50.0": 0.01, "110.0": 0}, + "protection": { + "protection_cooldown_lookback": 20, + "protection_enabled": True, + }, + "sell": { + "sell_minusdi": 0.02, + "sell_rsi": 75, + }, + "stoploss": {"stoploss": -0.4}, + "trailing": { + "trailing_only_offset_is_reached": False, + "trailing_stop": True, + "trailing_stop_positive": 0.02, + "trailing_stop_positive_offset": 0.07, + }, + "max_open_trades": {"max_open_trades": 3}, + }, + "params_dict": optimizer_param, + "params_not_optimized": {"buy": {}, "protection": {}, "sell": {}}, + "results_metrics": ANY, + "total_profit": 3.1e-08, } hyperopt = Hyperopt(hyperopt_conf) @@ -533,8 +603,10 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: def test_clean_hyperopt(mocker, hyperopt_conf, caplog): patch_exchange(mocker) - mocker.patch("freqtrade.strategy.hyper.HyperStrategyMixin.load_params_from_file", - MagicMock(return_value={})) + mocker.patch( + "freqtrade.strategy.hyper.HyperStrategyMixin.load_params_from_file", + MagicMock(return_value={}), + ) mocker.patch("freqtrade.optimize.hyperopt.Path.is_file", MagicMock(return_value=True)) unlinkmock = mocker.patch("freqtrade.optimize.hyperopt.Path.unlink", MagicMock()) h = Hyperopt(hyperopt_conf) @@ -544,38 +616,50 @@ def test_clean_hyperopt(mocker, hyperopt_conf, caplog): def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None: - dumper = mocker.patch('freqtrade.optimize.hyperopt.dump') - dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result') - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') - mocker.patch('freqtrade.optimize.hyperopt.calculate_market_change', return_value=1.5) + dumper = mocker.patch("freqtrade.optimize.hyperopt.dump") + dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result") + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") + mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) parallel = mocker.patch( - 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{ - 'loss': 1, 'results_explanation': 'foo result', 'params': {}, - 'params_details': { - 'buy': {'mfi-value': None}, - 'sell': {'sell-mfi-value': None}, - 'roi': {}, 'stoploss': {'stoploss': None}, - 'trailing': {'trailing_stop': None}, - 'max_open_trades': {'max_open_trades': None} - }, - 'results_metrics': generate_result_metrics(), - }]) + "freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel", + MagicMock( + return_value=[ + { + "loss": 1, + "results_explanation": "foo result", + "params": {}, + "params_details": { + "buy": {"mfi-value": None}, + "sell": {"sell-mfi-value": None}, + "roi": {}, + "stoploss": {"stoploss": None}, + "trailing": {"trailing_stop": None}, + "max_open_trades": {"max_open_trades": None}, + }, + "results_metrics": generate_result_metrics(), + } + ] + ), ) patch_exchange(mocker) - hyperopt_conf.update({'spaces': 'all', - 'hyperopt_jobs': 1, - 'print_json': True, - }) + hyperopt_conf.update( + { + "spaces": "all", + "hyperopt_jobs": 1, + "print_json": True, + } + ) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -597,32 +681,41 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None: def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None: - dumper = mocker.patch('freqtrade.optimize.hyperopt.dump') - dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result') - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') - mocker.patch('freqtrade.optimize.hyperopt.calculate_market_change', return_value=1.5) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) + dumper = mocker.patch("freqtrade.optimize.hyperopt.dump") + dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result") + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") + mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5) mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) parallel = mocker.patch( - 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{ - 'loss': 1, 'results_explanation': 'foo result', 'params': {}, - 'params_details': { - 'buy': {'mfi-value': None}, - 'sell': {'sell-mfi-value': None}, - 'roi': {}, 'stoploss': {'stoploss': None} - }, - 'results_metrics': generate_result_metrics(), - }]) + "freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel", + MagicMock( + return_value=[ + { + "loss": 1, + "results_explanation": "foo result", + "params": {}, + "params_details": { + "buy": {"mfi-value": None}, + "sell": {"sell-mfi-value": None}, + "roi": {}, + "stoploss": {"stoploss": None}, + }, + "results_metrics": generate_result_metrics(), + } + ] + ), ) patch_exchange(mocker) - hyperopt_conf.update({'print_json': True}) + hyperopt_conf.update({"print_json": True}) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -633,38 +726,52 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None: parallel.assert_called_once() out, _err = capsys.readouterr() - assert '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}' in out # noqa: E501 + assert ( + '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}' + in out + ) # noqa: E501 # Should be called for historical candle data assert dumper.call_count == 1 assert dumper2.call_count == 1 def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None: - dumper = mocker.patch('freqtrade.optimize.hyperopt.dump') - dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result') - mocker.patch('freqtrade.optimize.hyperopt.calculate_market_change', return_value=1.5) - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) + dumper = mocker.patch("freqtrade.optimize.hyperopt.dump") + dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result") + mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5) + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) parallel = mocker.patch( - 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{ - 'loss': 1, 'results_explanation': 'foo result', 'params': {}, - 'params_details': {'roi': {}, 'stoploss': {'stoploss': None}}, - 'results_metrics': generate_result_metrics(), - }]) + "freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel", + MagicMock( + return_value=[ + { + "loss": 1, + "results_explanation": "foo result", + "params": {}, + "params_details": {"roi": {}, "stoploss": {"stoploss": None}}, + "results_metrics": generate_result_metrics(), + } + ] + ), ) patch_exchange(mocker) - hyperopt_conf.update({'spaces': 'roi stoploss', - 'hyperopt_jobs': 1, - 'print_json': True, - }) + hyperopt_conf.update( + { + "spaces": "roi stoploss", + "hyperopt_jobs": 1, + "print_json": True, + } + ) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -682,27 +789,35 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None: def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> None: - dumper = mocker.patch('freqtrade.optimize.hyperopt.dump') - dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result') - mocker.patch('freqtrade.optimize.hyperopt.calculate_market_change', return_value=1.5) - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) + dumper = mocker.patch("freqtrade.optimize.hyperopt.dump") + dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result") + mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5) + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) parallel = mocker.patch( - 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{ - 'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0}, - 'results_metrics': generate_result_metrics(), - }]) + "freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel", + MagicMock( + return_value=[ + { + "loss": 1, + "results_explanation": "foo result", + "params": {"stoploss": 0.0}, + "results_metrics": generate_result_metrics(), + } + ] + ), ) patch_exchange(mocker) - hyperopt_conf.update({'spaces': 'roi stoploss'}) + hyperopt_conf.update({"spaces": "roi stoploss"}) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -713,32 +828,39 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non parallel.assert_called_once() out, _err = capsys.readouterr() - assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out + assert "Best result:\n\n* 1/1: foo result Objective: 1.00000\n" in out assert dumper.call_count == 1 assert dumper2.call_count == 1 assert hasattr(hyperopt.backtesting.strategy, "advise_exit") assert hasattr(hyperopt.backtesting.strategy, "advise_entry") - assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades'] + assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"] assert hasattr(hyperopt.backtesting, "_position_stacking") def test_simplified_interface_all_failed(mocker, hyperopt_conf, caplog) -> None: - mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) + mocker.patch("freqtrade.optimize.hyperopt.dump", MagicMock()) + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) patch_exchange(mocker) - hyperopt_conf.update({'spaces': 'all', }) + hyperopt_conf.update( + { + "spaces": "all", + } + ) - mocker.patch('freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space', - return_value=[]) + mocker.patch( + "freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space", return_value=[] + ) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -747,7 +869,7 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf, caplog) -> None: with pytest.raises(OperationalException, match=r"The 'protection' space is included into *"): hyperopt.init_spaces() - hyperopt.config['hyperopt_ignore_missing_space'] = True + hyperopt.config["hyperopt_ignore_missing_space"] = True caplog.clear() hyperopt.init_spaces() assert log_has_re(r"The 'protection' space is included into *", caplog) @@ -755,27 +877,35 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf, caplog) -> None: def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None: - dumper = mocker.patch('freqtrade.optimize.hyperopt.dump') - dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result') - mocker.patch('freqtrade.optimize.hyperopt.calculate_market_change', return_value=1.5) - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) + dumper = mocker.patch("freqtrade.optimize.hyperopt.dump") + dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result") + mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5) + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) parallel = mocker.patch( - 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{ - 'loss': 1, 'results_explanation': 'foo result', 'params': {}, - 'results_metrics': generate_result_metrics(), - }]) + "freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel", + MagicMock( + return_value=[ + { + "loss": 1, + "results_explanation": "foo result", + "params": {}, + "results_metrics": generate_result_metrics(), + } + ] + ), ) patch_exchange(mocker) - hyperopt_conf.update({'spaces': 'buy'}) + hyperopt_conf.update({"spaces": "buy"}) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -786,38 +916,50 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None: parallel.assert_called_once() out, _err = capsys.readouterr() - assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out + assert "Best result:\n\n* 1/1: foo result Objective: 1.00000\n" in out assert dumper.called assert dumper.call_count == 1 assert dumper2.call_count == 1 assert hasattr(hyperopt.backtesting.strategy, "advise_exit") assert hasattr(hyperopt.backtesting.strategy, "advise_entry") - assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades'] + assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"] assert hasattr(hyperopt.backtesting, "_position_stacking") def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None: - dumper = mocker.patch('freqtrade.optimize.hyperopt.dump') - dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result') - mocker.patch('freqtrade.optimize.hyperopt.calculate_market_change', return_value=1.5) - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) + dumper = mocker.patch("freqtrade.optimize.hyperopt.dump") + dumper2 = mocker.patch("freqtrade.optimize.hyperopt.Hyperopt._save_result") + mocker.patch("freqtrade.optimize.hyperopt.calculate_market_change", return_value=1.5) + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), ) parallel = mocker.patch( - 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{ - 'loss': 1, 'results_explanation': 'foo result', 'params': {}, - 'results_metrics': generate_result_metrics(), - }]) + "freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel", + MagicMock( + return_value=[ + { + "loss": 1, + "results_explanation": "foo result", + "params": {}, + "results_metrics": generate_result_metrics(), + } + ] + ), ) patch_exchange(mocker) - hyperopt_conf.update({'spaces': 'sell', }) + hyperopt_conf.update( + { + "spaces": "sell", + } + ) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -828,36 +970,42 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None: parallel.assert_called_once() out, _err = capsys.readouterr() - assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out + assert "Best result:\n\n* 1/1: foo result Objective: 1.00000\n" in out assert dumper.called assert dumper.call_count == 1 assert dumper2.call_count == 1 assert hasattr(hyperopt.backtesting.strategy, "advise_exit") assert hasattr(hyperopt.backtesting.strategy, "advise_entry") - assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades'] + assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"] assert hasattr(hyperopt.backtesting, "_position_stacking") -@pytest.mark.parametrize("space", [ - ('buy'), - ('sell'), - ('protection'), -]) +@pytest.mark.parametrize( + "space", + [ + ("buy"), + ("sell"), + ("protection"), + ], +) def test_simplified_interface_failed(mocker, hyperopt_conf, space) -> None: - mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) - mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') - mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', - MagicMock(return_value=(MagicMock(), None))) + mocker.patch("freqtrade.optimize.hyperopt.dump", MagicMock()) + mocker.patch("freqtrade.optimize.hyperopt.file_dump_json") mocker.patch( - 'freqtrade.optimize.hyperopt.get_timerange', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + "freqtrade.optimize.backtesting.Backtesting.load_bt_data", + MagicMock(return_value=(MagicMock(), None)), + ) + mocker.patch( + "freqtrade.optimize.hyperopt.get_timerange", + MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))), + ) + mocker.patch( + "freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space", return_value=[] ) - mocker.patch('freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space', - return_value=[]) patch_exchange(mocker) - hyperopt_conf.update({'spaces': space}) + hyperopt_conf.update({"spaces": space}) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() @@ -869,17 +1017,19 @@ def test_simplified_interface_failed(mocker, hyperopt_conf, space) -> None: def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmp_path, fee) -> None: patch_exchange(mocker) - mocker.patch(f'{EXMS}.get_fee', fee) + mocker.patch(f"{EXMS}.get_fee", fee) # Dummy-reduce points to ensure scikit-learn is forced to generate new values - mocker.patch('freqtrade.optimize.hyperopt.INITIAL_POINTS', 2) - (tmp_path / 'hyperopt_results').mkdir(parents=True) + mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2) + (tmp_path / "hyperopt_results").mkdir(parents=True) # No hyperopt needed - hyperopt_conf.update({ - 'strategy': 'HyperoptableStrategy', - 'user_data_dir': tmp_path, - 'hyperopt_random_state': 42, - 'spaces': ['all'], - }) + hyperopt_conf.update( + { + "strategy": "HyperoptableStrategy", + "user_data_dir": tmp_path, + "hyperopt_random_state": 42, + "spaces": ["all"], + } + ) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) @@ -904,32 +1054,33 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmp_path, fee) -> None assert hyperopt.backtesting.strategy.sell_rsi.value != 74 assert hyperopt.backtesting.strategy.max_open_trades != 1 - hyperopt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: 'ET1' + hyperopt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: "ET1" with pytest.raises(OperationalException, match="Estimator ET1 not supported."): hyperopt.get_optimizer([], 2) @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmp_path, fee) -> None: - mocker.patch(f'{EXMS}.validate_config', MagicMock()) - mocker.patch(f'{EXMS}.get_fee', fee) - mocker.patch(f'{EXMS}._load_markets') - mocker.patch(f'{EXMS}.markets', - PropertyMock(return_value=get_markets())) - (tmp_path / 'hyperopt_results').mkdir(parents=True) + mocker.patch(f"{EXMS}.validate_config", MagicMock()) + mocker.patch(f"{EXMS}.get_fee", fee) + mocker.patch(f"{EXMS}._load_markets") + mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=get_markets())) + (tmp_path / "hyperopt_results").mkdir(parents=True) # Dummy-reduce points to ensure scikit-learn is forced to generate new values - mocker.patch('freqtrade.optimize.hyperopt.INITIAL_POINTS', 2) + mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2) # No hyperopt needed - hyperopt_conf.update({ - 'strategy': 'HyperoptableStrategy', - 'user_data_dir': tmp_path, - 'hyperopt_random_state': 42, - 'spaces': ['all'], - # Enforce parallelity - 'epochs': 2, - 'hyperopt_jobs': 2, - 'fee': fee.return_value, - }) + hyperopt_conf.update( + { + "strategy": "HyperoptableStrategy", + "user_data_dir": tmp_path, + "hyperopt_random_state": 42, + "spaces": ["all"], + # Enforce parallelity + "epochs": 2, + "hyperopt_jobs": 2, + "fee": fee.return_value, + } + ) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.exchange.get_max_leverage = lambda *x, **xx: 1.0 hyperopt.backtesting.exchange.get_min_pair_stake_amount = lambda *x, **xx: 0.00001 @@ -955,23 +1106,28 @@ def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmp_path def test_in_strategy_auto_hyperopt_per_epoch(mocker, hyperopt_conf, tmp_path, fee) -> None: patch_exchange(mocker) - mocker.patch(f'{EXMS}.get_fee', fee) - (tmp_path / 'hyperopt_results').mkdir(parents=True) + mocker.patch(f"{EXMS}.get_fee", fee) + (tmp_path / "hyperopt_results").mkdir(parents=True) - hyperopt_conf.update({ - 'strategy': 'HyperoptableStrategy', - 'user_data_dir': tmp_path, - 'hyperopt_random_state': 42, - 'spaces': ['all'], - 'epochs': 3, - 'analyze_per_epoch': True, - }) - go = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.generate_optimizer', - return_value={ - 'loss': 0.05, - 'results_explanation': 'foo result', 'params': {}, - 'results_metrics': generate_result_metrics(), - }) + hyperopt_conf.update( + { + "strategy": "HyperoptableStrategy", + "user_data_dir": tmp_path, + "hyperopt_random_state": 42, + "spaces": ["all"], + "epochs": 3, + "analyze_per_epoch": True, + } + ) + go = mocker.patch( + "freqtrade.optimize.hyperopt.Hyperopt.generate_optimizer", + return_value={ + "loss": 0.05, + "results_explanation": "foo result", + "params": {}, + "results_metrics": generate_result_metrics(), + }, + ) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) @@ -1014,20 +1170,22 @@ def test_stake_amount_unlimited_max_open_trades(mocker, hyperopt_conf, tmp_path, # This test is to ensure that unlimited max_open_trades are ignored for the backtesting # if we have an unlimited stake amount patch_exchange(mocker) - mocker.patch(f'{EXMS}.get_fee', fee) - (tmp_path / 'hyperopt_results').mkdir(parents=True) - hyperopt_conf.update({ - 'strategy': 'HyperoptableStrategy', - 'user_data_dir': tmp_path, - 'hyperopt_random_state': 42, - 'spaces': ['trades'], - 'stake_amount': 'unlimited' - }) + mocker.patch(f"{EXMS}.get_fee", fee) + (tmp_path / "hyperopt_results").mkdir(parents=True) + hyperopt_conf.update( + { + "strategy": "HyperoptableStrategy", + "user_data_dir": tmp_path, + "hyperopt_random_state": 42, + "spaces": ["trades"], + "stake_amount": "unlimited", + } + ) hyperopt = Hyperopt(hyperopt_conf) - mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._get_params_dict', - return_value={ - 'max_open_trades': -1 - }) + mocker.patch( + "freqtrade.optimize.hyperopt.Hyperopt._get_params_dict", + return_value={"max_open_trades": -1}, + ) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) @@ -1042,19 +1200,21 @@ def test_max_open_trades_dump(mocker, hyperopt_conf, tmp_path, fee, capsys) -> N # This test is to ensure that after hyperopting, max_open_trades is never # saved as inf in the output json params patch_exchange(mocker) - mocker.patch(f'{EXMS}.get_fee', fee) - (tmp_path / 'hyperopt_results').mkdir(parents=True) - hyperopt_conf.update({ - 'strategy': 'HyperoptableStrategy', - 'user_data_dir': tmp_path, - 'hyperopt_random_state': 42, - 'spaces': ['trades'], - }) + mocker.patch(f"{EXMS}.get_fee", fee) + (tmp_path / "hyperopt_results").mkdir(parents=True) + hyperopt_conf.update( + { + "strategy": "HyperoptableStrategy", + "user_data_dir": tmp_path, + "hyperopt_random_state": 42, + "spaces": ["trades"], + } + ) hyperopt = Hyperopt(hyperopt_conf) - mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._get_params_dict', - return_value={ - 'max_open_trades': -1 - }) + mocker.patch( + "freqtrade.optimize.hyperopt.Hyperopt._get_params_dict", + return_value={"max_open_trades": -1}, + ) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) @@ -1062,18 +1222,18 @@ def test_max_open_trades_dump(mocker, hyperopt_conf, tmp_path, fee, capsys) -> N out, _err = capsys.readouterr() - assert 'max_open_trades = -1' in out - assert 'max_open_trades = inf' not in out + assert "max_open_trades = -1" in out + assert "max_open_trades = inf" not in out ############## - hyperopt_conf.update({'print_json': True}) + hyperopt_conf.update({"print_json": True}) hyperopt = Hyperopt(hyperopt_conf) - mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._get_params_dict', - return_value={ - 'max_open_trades': -1 - }) + mocker.patch( + "freqtrade.optimize.hyperopt.Hyperopt._get_params_dict", + return_value={"max_open_trades": -1}, + ) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) @@ -1088,26 +1248,29 @@ def test_max_open_trades_consistency(mocker, hyperopt_conf, tmp_path, fee) -> No # This test is to ensure that max_open_trades is the same across all functions needing it # after it has been changed from the hyperopt patch_exchange(mocker) - mocker.patch(f'{EXMS}.get_fee', return_value=0) + mocker.patch(f"{EXMS}.get_fee", return_value=0) - (tmp_path / 'hyperopt_results').mkdir(parents=True) - hyperopt_conf.update({ - 'strategy': 'HyperoptableStrategy', - 'user_data_dir': tmp_path, - 'hyperopt_random_state': 42, - 'spaces': ['trades'], - 'stake_amount': 'unlimited', - 'dry_run_wallet': 8, - 'available_capital': 8, - 'dry_run': True, - 'epochs': 1 - }) + (tmp_path / "hyperopt_results").mkdir(parents=True) + hyperopt_conf.update( + { + "strategy": "HyperoptableStrategy", + "user_data_dir": tmp_path, + "hyperopt_random_state": 42, + "spaces": ["trades"], + "stake_amount": "unlimited", + "dry_run_wallet": 8, + "available_capital": 8, + "dry_run": True, + "epochs": 1, + } + ) hyperopt = Hyperopt(hyperopt_conf) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) hyperopt.custom_hyperopt.max_open_trades_space = lambda: [ - Integer(1, 10, name='max_open_trades')] + Integer(1, 10, name="max_open_trades") + ] first_time_evaluated = False @@ -1120,12 +1283,14 @@ def test_max_open_trades_consistency(mocker, hyperopt_conf, tmp_path, fee) -> No assert stake_amount == 1 first_time_evaluated = True return stake_amount + return wrapper hyperopt.backtesting.wallets._calculate_unlimited_stake_amount = stake_amount_interceptor( - hyperopt.backtesting.wallets._calculate_unlimited_stake_amount) + hyperopt.backtesting.wallets._calculate_unlimited_stake_amount + ) hyperopt.start() assert hyperopt.backtesting.strategy.max_open_trades == 8 - assert hyperopt.config['max_open_trades'] == 8 + assert hyperopt.config["max_open_trades"] == 8 diff --git a/tests/optimize/test_hyperopt_tools.py b/tests/optimize/test_hyperopt_tools.py index 47aba6b76..c8a54e462 100644 --- a/tests/optimize/test_hyperopt_tools.py +++ b/tests/optimize/test_hyperopt_tools.py @@ -15,13 +15,11 @@ from tests.conftest import CURRENT_TEST_STRATEGY, log_has, log_has_re # Functions for recurrent object patching def create_results() -> List[Dict]: - - return [{'loss': 1, 'result': 'foo', 'params': {}, 'is_best': True}] + return [{"loss": 1, "result": "foo", "params": {}, "is_best": True}] def test_save_results_saves_epochs(hyperopt, tmp_path, caplog) -> None: - - hyperopt.results_file = tmp_path / 'ut_results.fthypt' + hyperopt.results_file = tmp_path / "ut_results.fthypt" hyperopt_epochs = HyperoptTools.load_filtered_results(hyperopt.results_file, {}) assert log_has_re("Hyperopt file .* not found.", caplog) @@ -57,152 +55,296 @@ def test_save_results_saves_epochs(hyperopt, tmp_path, caplog) -> None: def test_load_previous_results2(mocker, testdatadir, caplog) -> None: - results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle' - with pytest.raises(OperationalException, - match=r"Legacy hyperopt results are no longer supported.*"): + results_file = testdatadir / "hyperopt_results_SampleStrategy.pickle" + with pytest.raises( + OperationalException, match=r"Legacy hyperopt results are no longer supported.*" + ): HyperoptTools.load_filtered_results(results_file, {}) -@pytest.mark.parametrize("spaces, expected_results", [ - (['buy'], - {'buy': True, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': False, - 'protection': False, 'trades': False}), - (['sell'], - {'buy': False, 'sell': True, 'roi': False, 'stoploss': False, 'trailing': False, - 'protection': False, 'trades': False}), - (['roi'], - {'buy': False, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False, - 'protection': False, 'trades': False}), - (['stoploss'], - {'buy': False, 'sell': False, 'roi': False, 'stoploss': True, 'trailing': False, - 'protection': False, 'trades': False}), - (['trailing'], - {'buy': False, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': True, - 'protection': False, 'trades': False}), - (['buy', 'sell', 'roi', 'stoploss'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False, - 'protection': False, 'trades': False}), - (['buy', 'sell', 'roi', 'stoploss', 'trailing'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, - 'protection': False, 'trades': False}), - (['buy', 'roi'], - {'buy': True, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False, - 'protection': False, 'trades': False}), - (['all'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, - 'protection': True, 'trades': True}), - (['default'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False, - 'protection': False, 'trades': False}), - (['default', 'trailing'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, - 'protection': False, 'trades': False}), - (['all', 'buy'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, - 'protection': True, 'trades': True}), - (['default', 'buy'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False, - 'protection': False, 'trades': False}), - (['all'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, - 'protection': True, 'trades': True}), - (['protection'], - {'buy': False, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': False, - 'protection': True, 'trades': False}), - (['trades'], - {'buy': False, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': False, - 'protection': False, 'trades': True}), - (['default', 'trades'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False, - 'protection': False, 'trades': True}), -]) +@pytest.mark.parametrize( + "spaces, expected_results", + [ + ( + ["buy"], + { + "buy": True, + "sell": False, + "roi": False, + "stoploss": False, + "trailing": False, + "protection": False, + "trades": False, + }, + ), + ( + ["sell"], + { + "buy": False, + "sell": True, + "roi": False, + "stoploss": False, + "trailing": False, + "protection": False, + "trades": False, + }, + ), + ( + ["roi"], + { + "buy": False, + "sell": False, + "roi": True, + "stoploss": False, + "trailing": False, + "protection": False, + "trades": False, + }, + ), + ( + ["stoploss"], + { + "buy": False, + "sell": False, + "roi": False, + "stoploss": True, + "trailing": False, + "protection": False, + "trades": False, + }, + ), + ( + ["trailing"], + { + "buy": False, + "sell": False, + "roi": False, + "stoploss": False, + "trailing": True, + "protection": False, + "trades": False, + }, + ), + ( + ["buy", "sell", "roi", "stoploss"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": False, + "protection": False, + "trades": False, + }, + ), + ( + ["buy", "sell", "roi", "stoploss", "trailing"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": True, + "protection": False, + "trades": False, + }, + ), + ( + ["buy", "roi"], + { + "buy": True, + "sell": False, + "roi": True, + "stoploss": False, + "trailing": False, + "protection": False, + "trades": False, + }, + ), + ( + ["all"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": True, + "protection": True, + "trades": True, + }, + ), + ( + ["default"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": False, + "protection": False, + "trades": False, + }, + ), + ( + ["default", "trailing"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": True, + "protection": False, + "trades": False, + }, + ), + ( + ["all", "buy"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": True, + "protection": True, + "trades": True, + }, + ), + ( + ["default", "buy"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": False, + "protection": False, + "trades": False, + }, + ), + ( + ["all"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": True, + "protection": True, + "trades": True, + }, + ), + ( + ["protection"], + { + "buy": False, + "sell": False, + "roi": False, + "stoploss": False, + "trailing": False, + "protection": True, + "trades": False, + }, + ), + ( + ["trades"], + { + "buy": False, + "sell": False, + "roi": False, + "stoploss": False, + "trailing": False, + "protection": False, + "trades": True, + }, + ), + ( + ["default", "trades"], + { + "buy": True, + "sell": True, + "roi": True, + "stoploss": True, + "trailing": False, + "protection": False, + "trades": True, + }, + ), + ], +) def test_has_space(hyperopt_conf, spaces, expected_results): - for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing', 'protection', 'trades']: - hyperopt_conf.update({'spaces': spaces}) + for s in ["buy", "sell", "roi", "stoploss", "trailing", "protection", "trades"]: + hyperopt_conf.update({"spaces": spaces}) assert HyperoptTools.has_space(hyperopt_conf, s) == expected_results[s] def test_show_epoch_details(capsys): test_result = { - 'params_details': { - 'trailing': { - 'trailing_stop': True, - 'trailing_stop_positive': 0.02, - 'trailing_stop_positive_offset': 0.04, - 'trailing_only_offset_is_reached': True + "params_details": { + "trailing": { + "trailing_stop": True, + "trailing_stop_positive": 0.02, + "trailing_stop_positive_offset": 0.04, + "trailing_only_offset_is_reached": True, }, - 'roi': { - 0: 0.18, - 90: 0.14, - 225: 0.05, - 430: 0}, + "roi": {0: 0.18, 90: 0.14, 225: 0.05, 430: 0}, }, - 'results_explanation': 'foo result', - 'is_initial_point': False, - 'total_profit': 0, - 'current_epoch': 2, # This starts from 1 (in a human-friendly manner) - 'is_best': True + "results_explanation": "foo result", + "is_initial_point": False, + "total_profit": 0, + "current_epoch": 2, # This starts from 1 (in a human-friendly manner) + "is_best": True, } HyperoptTools.show_epoch_details(test_result, 5, False, no_header=True) captured = capsys.readouterr() - assert '# Trailing stop:' in captured.out + assert "# Trailing stop:" in captured.out # re.match(r"Pairs for .*", captured.out) - assert re.search(r'^\s+trailing_stop = True$', captured.out, re.MULTILINE) - assert re.search(r'^\s+trailing_stop_positive = 0.02$', captured.out, re.MULTILINE) - assert re.search(r'^\s+trailing_stop_positive_offset = 0.04$', captured.out, re.MULTILINE) - assert re.search(r'^\s+trailing_only_offset_is_reached = True$', captured.out, re.MULTILINE) + assert re.search(r"^\s+trailing_stop = True$", captured.out, re.MULTILINE) + assert re.search(r"^\s+trailing_stop_positive = 0.02$", captured.out, re.MULTILINE) + assert re.search(r"^\s+trailing_stop_positive_offset = 0.04$", captured.out, re.MULTILINE) + assert re.search(r"^\s+trailing_only_offset_is_reached = True$", captured.out, re.MULTILINE) - assert '# ROI table:' in captured.out - assert re.search(r'^\s+minimal_roi = \{$', captured.out, re.MULTILINE) - assert re.search(r'^\s+\"90\"\:\s0.14,\s*$', captured.out, re.MULTILINE) + assert "# ROI table:" in captured.out + assert re.search(r"^\s+minimal_roi = \{$", captured.out, re.MULTILINE) + assert re.search(r"^\s+\"90\"\:\s0.14,\s*$", captured.out, re.MULTILINE) def test__pprint_dict(): - params = {'buy_std': 1.2, 'buy_rsi': 31, 'buy_enable': True, 'buy_what': 'asdf'} - non_params = {'buy_notoptimied': 55} + params = {"buy_std": 1.2, "buy_rsi": 31, "buy_enable": True, "buy_what": "asdf"} + non_params = {"buy_notoptimied": 55} x = HyperoptTools._pprint_dict(params, non_params) - assert x == """{ + assert ( + x + == """{ "buy_std": 1.2, "buy_rsi": 31, "buy_enable": True, "buy_what": "asdf", "buy_notoptimied": 55, # value loaded from strategy }""" + ) def test_get_strategy_filename(default_conf, tmp_path): - default_conf['user_data_dir'] = tmp_path - x = HyperoptTools.get_strategy_filename(default_conf, 'StrategyTestV3') + default_conf["user_data_dir"] = tmp_path + x = HyperoptTools.get_strategy_filename(default_conf, "StrategyTestV3") assert isinstance(x, Path) - assert x == Path(__file__).parents[1] / 'strategy/strats/strategy_test_v3.py' + assert x == Path(__file__).parents[1] / "strategy/strats/strategy_test_v3.py" - x = HyperoptTools.get_strategy_filename(default_conf, 'NonExistingStrategy') + x = HyperoptTools.get_strategy_filename(default_conf, "NonExistingStrategy") assert x is None def test_export_params(tmp_path): - filename = tmp_path / f"{CURRENT_TEST_STRATEGY}.json" assert not filename.is_file() params = { "params_details": { - "buy": { - "buy_rsi": 30 - }, - "sell": { - "sell_rsi": 70 - }, - "roi": { - "0": 0.528, - "346": 0.08499, - "507": 0.049, - "1595": 0 - }, - "max_open_trades": { - "max_open_trades": 5 - } + "buy": {"buy_rsi": 30}, + "sell": {"sell_rsi": 70}, + "roi": {"0": 0.528, "346": 0.08499, "507": 0.049, "1595": 0}, + "max_open_trades": {"max_open_trades": 5}, }, "params_not_optimized": { "stoploss": -0.05, @@ -210,19 +352,18 @@ def test_export_params(tmp_path): "trailing_stop": False, "trailing_stop_positive": 0.05, "trailing_stop_positive_offset": 0.1, - "trailing_only_offset_is_reached": True + "trailing_only_offset_is_reached": True, }, - } - + }, } HyperoptTools.export_params(params, CURRENT_TEST_STRATEGY, filename) assert filename.is_file() - with filename.open('r') as f: + with filename.open("r") as f: content = rapidjson.load(f) - assert content['strategy_name'] == CURRENT_TEST_STRATEGY - assert 'params' in content + assert content["strategy_name"] == CURRENT_TEST_STRATEGY + assert "params" in content assert "buy" in content["params"] assert "sell" in content["params"] assert "roi" in content["params"] @@ -232,26 +373,17 @@ def test_export_params(tmp_path): def test_try_export_params(default_conf, tmp_path, caplog, mocker): - default_conf['disableparamexport'] = False - default_conf['user_data_dir'] = tmp_path + default_conf["disableparamexport"] = False + default_conf["user_data_dir"] = tmp_path export_mock = mocker.patch("freqtrade.optimize.hyperopt_tools.HyperoptTools.export_params") filename = tmp_path / f"{CURRENT_TEST_STRATEGY}.json" assert not filename.is_file() params = { "params_details": { - "buy": { - "buy_rsi": 30 - }, - "sell": { - "sell_rsi": 70 - }, - "roi": { - "0": 0.528, - "346": 0.08499, - "507": 0.049, - "1595": 0 - } + "buy": {"buy_rsi": 30}, + "sell": {"sell_rsi": 70}, + "roi": {"0": 0.528, "346": 0.08499, "507": 0.049, "1595": 0}, }, "params_not_optimized": { "stoploss": -0.05, @@ -259,11 +391,10 @@ def test_try_export_params(default_conf, tmp_path, caplog, mocker): "trailing_stop": False, "trailing_stop_positive": 0.05, "trailing_stop_positive_offset": 0.1, - "trailing_only_offset_is_reached": True + "trailing_only_offset_is_reached": True, }, }, FTHYPT_FILEVERSION: 2, - } HyperoptTools.try_export_params(default_conf, "StrategyTestVXXX", params) @@ -275,26 +406,17 @@ def test_try_export_params(default_conf, tmp_path, caplog, mocker): assert export_mock.call_count == 1 assert export_mock.call_args_list[0][0][1] == CURRENT_TEST_STRATEGY - assert export_mock.call_args_list[0][0][2].name == 'strategy_test_v3.json' + assert export_mock.call_args_list[0][0][2].name == "strategy_test_v3.json" def test_params_print(capsys): - params = { - "buy": { - "buy_rsi": 30 - }, - "sell": { - "sell_rsi": 70 - }, + "buy": {"buy_rsi": 30}, + "sell": {"sell_rsi": 70}, } non_optimized = { - "buy": { - "buy_adx": 44 - }, - "sell": { - "sell_adx": 65 - }, + "buy": {"buy_adx": 44}, + "sell": {"sell_adx": 65}, "stoploss": { "stoploss": -0.05, }, @@ -306,14 +428,11 @@ def test_params_print(capsys): "trailing_stop": False, "trailing_stop_positive": 0.05, "trailing_stop_positive_offset": 0.1, - "trailing_only_offset_is_reached": True + "trailing_only_offset_is_reached": True, }, - "max_open_trades": { - "max_open_trades": 5 - } - + "max_open_trades": {"max_open_trades": 5}, } - HyperoptTools._params_pretty_print(params, 'buy', 'No header', non_optimized) + HyperoptTools._params_pretty_print(params, "buy", "No header", non_optimized) captured = capsys.readouterr() assert re.search("# No header", captured.out) @@ -321,36 +440,34 @@ def test_params_print(capsys): assert re.search('"buy_adx": 44, # value loaded.*\n', captured.out) assert not re.search("sell", captured.out) - HyperoptTools._params_pretty_print(params, 'sell', 'Sell Header', non_optimized) + HyperoptTools._params_pretty_print(params, "sell", "Sell Header", non_optimized) captured = capsys.readouterr() assert re.search("# Sell Header", captured.out) assert re.search('"sell_rsi": 70,\n', captured.out) assert re.search('"sell_adx": 65, # value loaded.*\n', captured.out) - HyperoptTools._params_pretty_print(params, 'roi', 'ROI Table:', non_optimized) + HyperoptTools._params_pretty_print(params, "roi", "ROI Table:", non_optimized) captured = capsys.readouterr() assert re.search("# ROI Table: # value loaded.*\n", captured.out) - assert re.search('minimal_roi = {\n', captured.out) + assert re.search("minimal_roi = {\n", captured.out) assert re.search('"20": 0.01\n', captured.out) - HyperoptTools._params_pretty_print(params, 'trailing', 'Trailing stop:', non_optimized) + HyperoptTools._params_pretty_print(params, "trailing", "Trailing stop:", non_optimized) captured = capsys.readouterr() assert re.search("# Trailing stop:", captured.out) - assert re.search('trailing_stop = False # value loaded.*\n', captured.out) - assert re.search('trailing_stop_positive = 0.05 # value loaded.*\n', captured.out) - assert re.search('trailing_stop_positive_offset = 0.1 # value loaded.*\n', captured.out) - assert re.search('trailing_only_offset_is_reached = True # value loaded.*\n', captured.out) + assert re.search("trailing_stop = False # value loaded.*\n", captured.out) + assert re.search("trailing_stop_positive = 0.05 # value loaded.*\n", captured.out) + assert re.search("trailing_stop_positive_offset = 0.1 # value loaded.*\n", captured.out) + assert re.search("trailing_only_offset_is_reached = True # value loaded.*\n", captured.out) - HyperoptTools._params_pretty_print( - params, 'max_open_trades', "Max Open Trades:", non_optimized) + HyperoptTools._params_pretty_print(params, "max_open_trades", "Max Open Trades:", non_optimized) captured = capsys.readouterr() assert re.search("# Max Open Trades:", captured.out) - assert re.search('max_open_trades = 5 # value loaded.*\n', captured.out) + assert re.search("max_open_trades = 5 # value loaded.*\n", captured.out) def test_hyperopt_serializer(): - assert isinstance(hyperopt_serializer(np.int_(5)), int) assert isinstance(hyperopt_serializer(np.bool_(True)), bool) assert isinstance(hyperopt_serializer(np.bool_(False)), bool)