freqtrade_origin/tests/optimize/test_hyperopt.py

1297 lines
44 KiB
Python
Raw Normal View History

2017-12-26 08:08:10 +00:00
# pragma pylint: disable=missing-docstring,W0212,C0103
2022-01-04 15:48:23 +00:00
from datetime import datetime, timedelta
from functools import wraps
2019-09-08 07:54:15 +00:00
from pathlib import Path
from unittest.mock import ANY, MagicMock, PropertyMock
2018-03-17 21:44:47 +00:00
import pandas as pd
import pytest
from filelock import Timeout
from skopt.space import Integer
2020-09-28 17:43:15 +00:00
from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_hyperopt
from freqtrade.data.history import load_data
2022-03-25 07:36:03 +00:00
from freqtrade.enums import ExitType, RunMode
from freqtrade.exceptions import OperationalException
2019-07-21 14:07:06 +00:00
from freqtrade.optimize.hyperopt import Hyperopt
2021-03-29 17:27:19 +00:00
from freqtrade.optimize.hyperopt_auto import HyperOptAuto
from freqtrade.optimize.hyperopt_tools import HyperoptTools
2021-04-29 18:17:13 +00:00
from freqtrade.optimize.optimize_reports import generate_strategy_stats
from freqtrade.optimize.space import SKDecimal
from freqtrade.strategy import IntParameter
2023-05-14 15:46:56 +00:00
from freqtrade.util import dt_utc
2024-05-12 13:08:40 +00:00
from tests.conftest import (
CURRENT_TEST_STRATEGY,
EXMS,
get_args,
get_markets,
log_has,
log_has_re,
patch_exchange,
patched_configuration_load_config_file,
)
2022-01-04 15:39:03 +00:00
def generate_result_metrics():
return {
2024-05-12 14:04:55 +00:00
"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_account": 0.001,
2024-05-12 14:04:55 +00:00
"max_drawdown_abs": 0.001,
"loss": 0.001,
"is_initial_point": 0.001,
"is_random": False,
"is_best": 1,
2022-02-06 04:36:28 +00:00
}
2022-01-04 15:39:03 +00:00
2022-01-04 15:48:23 +00:00
def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf)
args = [
2024-05-12 14:04:55 +00:00
"hyperopt",
"--config",
"config.json",
"--strategy",
"HyperoptableStrategy",
]
2020-01-26 12:33:13 +00:00
config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
2024-05-12 14:04:55 +00:00
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
2024-05-12 14:04:55 +00:00
assert "position_stacking" not in config
assert not log_has("Parameter --enable-position-stacking detected ...", caplog)
2024-05-12 14:04:55 +00:00
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)
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.configuration.configuration.create_datadir", lambda c, x: x)
args = [
2024-05-12 14:04:55 +00:00
"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",
]
2020-01-26 12:33:13 +00:00
config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
2024-05-12 14:04:55 +00:00
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 "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 "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
)
2024-05-12 14:04:55 +00:00
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)
2021-02-26 18:48:06 +00:00
def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None:
2020-03-10 09:42:31 +00:00
patched_configuration_load_config_file(mocker, default_conf)
args = [
2024-05-12 14:04:55 +00:00
"hyperopt",
"--config",
"config.json",
"--strategy",
"HyperoptableStrategy",
"--stake-amount",
"1",
"--starting-balance",
"2",
2020-03-10 09:42:31 +00:00
]
conf = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
assert isinstance(conf, dict)
2020-03-10 09:42:31 +00:00
2021-02-26 18:48:06 +00:00
args = [
2024-05-12 14:04:55 +00:00
"hyperopt",
"--config",
"config.json",
"--strategy",
CURRENT_TEST_STRATEGY,
"--stake-amount",
"1",
"--starting-balance",
"0.5",
2021-02-26 18:48:06 +00:00
]
with pytest.raises(OperationalException, match=r"Starting balance .* smaller .*"):
setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
2020-03-10 09:42:31 +00:00
2020-08-06 07:00:28 +00:00
def test_start_not_installed(mocker, default_conf, import_fails) -> None:
2019-09-24 12:39:28 +00:00
start_mock = MagicMock()
patched_configuration_load_config_file(mocker, default_conf)
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.optimize.hyperopt.Hyperopt.start", start_mock)
2019-09-24 12:39:28 +00:00
patch_exchange(mocker)
args = [
2024-05-12 14:04:55 +00:00
"hyperopt",
"--config",
"config.json",
"--strategy",
"HyperoptableStrategy",
"--epochs",
"5",
"--hyperopt-loss",
"SharpeHyperOptLossDaily",
2019-09-24 12:39:28 +00:00
]
2020-02-10 09:35:48 +00:00
pargs = get_args(args)
2019-09-24 12:39:28 +00:00
with pytest.raises(OperationalException, match=r"Please ensure that the hyperopt dependencies"):
2020-02-10 09:35:48 +00:00
start_hyperopt(pargs)
2019-09-24 12:39:28 +00:00
def test_start_no_hyperopt_allowed(mocker, hyperopt_conf, caplog) -> None:
2018-03-06 06:02:03 +00:00
start_mock = MagicMock()
patched_configuration_load_config_file(mocker, hyperopt_conf)
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.optimize.hyperopt.Hyperopt.start", start_mock)
2018-06-17 20:32:56 +00:00
patch_exchange(mocker)
2018-03-06 06:02:03 +00:00
args = [
2024-05-12 14:04:55 +00:00
"hyperopt",
"--config",
"config.json",
"--hyperopt",
"HyperoptTestSepFile",
"--hyperopt-loss",
"SharpeHyperOptLossDaily",
"--epochs",
"5",
2018-03-06 06:02:03 +00:00
]
2020-02-10 09:35:48 +00:00
pargs = get_args(args)
with pytest.raises(OperationalException, match=r"Using separate Hyperopt files has been.*"):
start_hyperopt(pargs)
2018-03-06 06:02:03 +00:00
2023-11-05 15:25:23 +00:00
def test_start_no_data(mocker, hyperopt_conf, tmp_path) -> None:
2024-05-12 14:04:55 +00:00
hyperopt_conf["user_data_dir"] = tmp_path
patched_configuration_load_config_file(mocker, hyperopt_conf)
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.data.history.load_pair_history", MagicMock(return_value=pd.DataFrame))
mocker.patch(
2024-05-12 14:04:55 +00:00
"freqtrade.optimize.hyperopt.get_timerange",
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
)
patch_exchange(mocker)
args = [
2024-05-12 14:04:55 +00:00
"hyperopt",
"--config",
"config.json",
"--strategy",
"HyperoptableStrategy",
"--hyperopt-loss",
"SharpeHyperOptLossDaily",
"--epochs",
"5",
]
2020-02-10 09:35:48 +00:00
pargs = get_args(args)
2024-05-12 14:04:55 +00:00
with pytest.raises(OperationalException, match="No data found. Terminating."):
2020-02-10 09:35:48 +00:00
start_hyperopt(pargs)
# Cleanup since that failed hyperopt start leaves a lockfile.
2021-12-25 12:47:28 +00:00
try:
Path(Hyperopt.get_lock_filename(hyperopt_conf)).unlink()
2021-12-25 12:47:28 +00:00
except Exception:
pass
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)
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.optimize.hyperopt.Hyperopt.__init__", hyperopt_mock)
patch_exchange(mocker)
args = [
2024-05-12 14:04:55 +00:00
"hyperopt",
"--config",
"config.json",
"--strategy",
"HyperoptableStrategy",
"--hyperopt-loss",
"SharpeHyperOptLossDaily",
"--epochs",
"5",
]
2020-02-10 09:35:48 +00:00
pargs = get_args(args)
start_hyperopt(pargs)
assert log_has("Another running instance of freqtrade Hyperopt detected.", caplog)
def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
hyperopt.current_best_loss = 2
2019-07-30 08:47:46 +00:00
hyperopt.total_epochs = 2
2020-03-01 02:11:00 +00:00
2019-11-27 19:52:43 +00:00
hyperopt.print_results(
{
2024-05-12 14:04:55 +00:00
"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,
}
)
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2024-05-12 14:04:55 +00:00
assert all(
x in out for x in ["Best", "2/2", " 1", "0.10%", "0.00100000 BTC (1.00%)", "00:20:00"]
)
2017-12-26 08:08:10 +00:00
def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
hyperopt.current_best_loss = 2
2019-11-27 19:52:43 +00:00
hyperopt.print_results(
{
2024-05-12 14:04:55 +00:00
"is_best": False,
"loss": 3,
"current_epoch": 1,
}
)
assert caplog.record_tuples == []
def test_roi_table_generation(hyperopt) -> None:
2018-01-25 08:45:53 +00:00
params = {
2024-05-12 14:04:55 +00:00
"roi_t1": 5,
"roi_t2": 10,
"roi_t3": 15,
"roi_p1": 1,
"roi_p2": 2,
"roi_p3": 3,
2018-01-25 08:45:53 +00:00
}
assert hyperopt.custom_hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0}
2021-07-03 06:38:55 +00:00
def test_params_no_optimize_details(hyperopt) -> None:
2024-05-12 14:04:55 +00:00
hyperopt.config["spaces"] = ["buy"]
2021-07-03 06:38:55 +00:00
res = hyperopt._get_no_optimize_details()
assert isinstance(res, dict)
assert "trailing" in res
2024-05-12 14:04:55 +00:00
assert res["trailing"]["trailing_stop"] is False
2021-07-03 06:38:55 +00:00
assert "roi" in res
2024-05-12 14:04:55 +00:00
assert res["roi"]["0"] == 0.04
2021-07-03 06:38:55 +00:00
assert "stoploss" in res
2024-05-12 14:04:55 +00:00
assert res["stoploss"]["stoploss"] == -0.1
assert "max_open_trades" in res
2024-05-12 14:04:55 +00:00
assert res["max_open_trades"]["max_open_trades"] == 1
2021-07-03 06:38:55 +00:00
def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
2024-05-12 14:04:55 +00:00
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")
2020-09-27 17:48:11 +00:00
mocker.patch(
2024-05-12 14:04:55 +00:00
"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
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2)
2018-06-24 12:27:53 +00:00
parallel = mocker.patch(
2024-05-12 14:04:55 +00:00
"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(),
}
]
),
2018-06-24 12:27:53 +00:00
)
2018-06-17 20:32:56 +00:00
patch_exchange(mocker)
# Co-test loading timeframe from strategy
2024-05-12 14:04:55 +00:00
del hyperopt_conf["timeframe"]
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
2019-08-01 20:57:50 +00:00
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
2019-07-30 08:47:46 +00:00
2018-06-24 12:27:53 +00:00
parallel.assert_called_once()
2019-07-30 08:47:46 +00:00
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2024-05-12 14:04:55 +00:00
assert "Best result:\n\n* 1/1: foo result Objective: 1.00000\n" in out
2021-05-12 03:58:25 +00:00
# Should be called for historical candle data
assert dumper.call_count == 1
assert dumper2.call_count == 1
2021-09-22 18:42:31 +00:00
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
2024-05-12 14:04:55 +00:00
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
assert hasattr(hyperopt.backtesting, "_position_stacking")
2021-04-29 18:17:13 +00:00
def test_hyperopt_format_results(hyperopt):
bt_result = {
2024-05-12 14:04:55 +00:00
"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.value,
ExitType.STOP_LOSS.value,
ExitType.ROI.value,
ExitType.FORCE_EXIT.value,
2024-05-12 14:04:55 +00:00
],
}
),
"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,
2021-08-06 22:19:36 +00:00
}
2024-05-12 14:04:55 +00:00
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,
)
2021-04-29 18:17:13 +00:00
2024-05-12 14:04:55 +00:00
results_explanation = HyperoptTools.format_results_explanation_string(results_metrics, "BTC")
total_profit = results_metrics["profit_total_abs"]
2019-12-01 15:01:59 +00:00
results = {
2024-05-12 14:04:55 +00:00
"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,
2019-12-01 15:01:59 +00:00
}
result = HyperoptTools._format_explanation_string(results, 1)
2024-05-12 14:04:55 +00:00
assert " 0.71%" in result
assert "Total profit 0.00003100 BTC" in result
assert "0:50:00 min" in result
2019-09-07 18:56:03 +00:00
def test_populate_indicators(hyperopt, testdatadir) -> None:
2024-05-12 14:04:55 +00:00
data = load_data(testdatadir, "1m", ["UNITTEST/BTC"], fill_up_missing=True)
dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data)
2024-05-12 14:04:55 +00:00
dataframe = dataframes["UNITTEST/BTC"]
2018-03-05 08:35:42 +00:00
# Check if some indicators are generated. We will not test all of them
2024-05-12 14:04:55 +00:00
assert "adx" in dataframe
assert "macd" in dataframe
assert "rsi" in dataframe
2018-03-05 08:35:42 +00:00
def test_generate_optimizer(mocker, hyperopt_conf) -> None:
2024-05-12 14:04:55 +00:00
hyperopt_conf.update(
{
"spaces": "all",
"hyperopt_min_trades": 1,
}
)
2018-03-06 06:02:03 +00:00
backtest_result = {
2024-05-12 14:04:55 +00:00
"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.value,
ExitType.STOP_LOSS.value,
ExitType.ROI.value,
ExitType.FORCE_EXIT.value,
2024-05-12 14:04:55 +00:00
],
}
),
"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,
}
2018-03-06 06:02:03 +00:00
2024-05-12 14:04:55 +00:00
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)),
)
2018-06-17 20:32:56 +00:00
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
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})
2018-03-06 06:02:03 +00:00
optimizer_param = {
2024-05-12 14:04:55 +00:00
"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,
2018-03-06 06:02:03 +00:00
}
response_expected = {
2024-05-12 14:04:55 +00:00
"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,
2018-03-06 06:02:03 +00:00
}
hyperopt = Hyperopt(hyperopt_conf)
2023-05-14 16:31:09 +00:00
hyperopt.min_date = dt_utc(2017, 12, 10)
hyperopt.max_date = dt_utc(2017, 12, 13)
hyperopt.init_spaces()
2018-06-24 12:27:53 +00:00
generate_optimizer_value = hyperopt.generate_optimizer(list(optimizer_param.values()))
2018-03-06 06:02:03 +00:00
assert generate_optimizer_value == response_expected
2019-07-15 18:27:34 +00:00
def test_clean_hyperopt(mocker, hyperopt_conf, caplog):
2019-07-15 18:27:34 +00:00
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
mocker.patch(
"freqtrade.strategy.hyper.HyperStrategyMixin.load_params_from_file",
MagicMock(return_value={}),
)
2019-07-15 18:27:34 +00:00
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)
2019-07-15 18:27:34 +00:00
assert unlinkmock.call_count == 2
assert log_has(f"Removing `{h.data_pickle_file}`.", caplog)
2019-07-16 03:50:27 +00:00
def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
2024-05-12 14:04:55 +00:00
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)
2020-09-27 17:34:47 +00:00
2019-08-15 21:49:49 +00:00
mocker.patch(
2024-05-12 14:04:55 +00:00
"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))),
2019-08-15 21:49:49 +00:00
)
parallel = mocker.patch(
2024-05-12 14:04:55 +00:00
"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(),
}
]
),
2019-08-15 21:49:49 +00:00
)
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
hyperopt_conf.update(
{
"spaces": "all",
"hyperopt_jobs": 1,
"print_json": True,
}
)
2019-08-15 21:49:49 +00:00
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
2019-08-15 21:49:49 +00:00
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
parallel.assert_called_once()
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2020-03-01 02:11:00 +00:00
result_str = (
'{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi"'
':{},"stoploss":null,"trailing_stop":null,"max_open_trades":null}'
2020-03-01 02:11:00 +00:00
)
assert result_str in out # noqa: E501
2021-05-12 03:58:25 +00:00
# Should be called for historical candle data
assert dumper.call_count == 1
assert dumper2.call_count == 1
2019-08-15 21:49:49 +00:00
def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
2024-05-12 14:04:55 +00:00
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)
2019-11-13 20:09:05 +00:00
mocker.patch(
2024-05-12 14:04:55 +00:00
"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))),
2019-11-13 20:09:05 +00:00
)
parallel = mocker.patch(
2024-05-12 14:04:55 +00:00
"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(),
}
]
),
2019-11-13 20:09:05 +00:00
)
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
hyperopt_conf.update({"print_json": True})
2019-11-13 20:09:05 +00:00
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
2019-11-13 20:09:05 +00:00
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
parallel.assert_called_once()
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2024-05-12 14:04:55 +00:00
assert (
'{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}'
in out
) # noqa: E501
2021-05-12 03:58:25 +00:00
# Should be called for historical candle data
assert dumper.call_count == 1
assert dumper2.call_count == 1
2019-11-13 20:09:05 +00:00
def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
2024-05-12 14:04:55 +00:00
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")
2019-08-15 21:49:49 +00:00
mocker.patch(
2024-05-12 14:04:55 +00:00
"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))),
2019-08-15 21:49:49 +00:00
)
parallel = mocker.patch(
2024-05-12 14:04:55 +00:00
"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(),
}
]
),
2019-08-15 21:49:49 +00:00
)
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
hyperopt_conf.update(
{
"spaces": "roi stoploss",
"hyperopt_jobs": 1,
"print_json": True,
}
)
2019-08-15 21:49:49 +00:00
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
2019-08-15 21:49:49 +00:00
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
parallel.assert_called_once()
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2019-08-15 21:49:49 +00:00
assert '{"minimal_roi":{},"stoploss":null}' in out
2021-05-12 03:58:25 +00:00
assert dumper.call_count == 1
assert dumper2.call_count == 1
def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
2024-05-12 14:04:55 +00:00
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(
2024-05-12 14:04:55 +00:00
"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(
2024-05-12 14:04:55 +00:00
"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)
2024-05-12 14:04:55 +00:00
hyperopt_conf.update({"spaces": "roi stoploss"})
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
parallel.assert_called_once()
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2024-05-12 14:04:55 +00:00
assert "Best result:\n\n* 1/1: foo result Objective: 1.00000\n" in out
2021-05-12 03:58:25 +00:00
assert dumper.call_count == 1
assert dumper2.call_count == 1
2021-09-22 18:42:31 +00:00
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
2024-05-12 14:04:55 +00:00
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:
2024-05-12 14:04:55 +00:00
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(
2024-05-12 14:04:55 +00:00
"freqtrade.optimize.hyperopt.get_timerange",
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
)
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
hyperopt_conf.update(
{
"spaces": "all",
}
)
2024-05-12 14:04:55 +00:00
mocker.patch(
"freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space", return_value=[]
)
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
with pytest.raises(OperationalException, match=r"The 'protection' space is included into *"):
hyperopt.init_spaces()
2024-05-12 14:04:55 +00:00
hyperopt.config["hyperopt_ignore_missing_space"] = True
caplog.clear()
hyperopt.init_spaces()
assert log_has_re(r"The 'protection' space is included into *", caplog)
assert hyperopt.protection_space == []
def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
2024-05-12 14:04:55 +00:00
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(
2024-05-12 14:04:55 +00:00
"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(
2024-05-12 14:04:55 +00:00
"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)
2024-05-12 14:04:55 +00:00
hyperopt_conf.update({"spaces": "buy"})
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
parallel.assert_called_once()
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2024-05-12 14:04:55 +00:00
assert "Best result:\n\n* 1/1: foo result Objective: 1.00000\n" in out
assert dumper.called
2021-05-12 03:58:25 +00:00
assert dumper.call_count == 1
assert dumper2.call_count == 1
2021-09-22 18:42:31 +00:00
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
2024-05-12 14:04:55 +00:00
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:
2024-05-12 14:04:55 +00:00
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(
2024-05-12 14:04:55 +00:00
"freqtrade.optimize.hyperopt.get_timerange",
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))),
)
parallel = mocker.patch(
2024-05-12 14:04:55 +00:00
"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)
2024-05-12 14:04:55 +00:00
hyperopt_conf.update(
{
"spaces": "sell",
}
)
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
hyperopt.start()
parallel.assert_called_once()
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2024-05-12 14:04:55 +00:00
assert "Best result:\n\n* 1/1: foo result Objective: 1.00000\n" in out
assert dumper.called
2021-05-12 03:58:25 +00:00
assert dumper.call_count == 1
assert dumper2.call_count == 1
2021-09-22 18:42:31 +00:00
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
2024-05-12 14:04:55 +00:00
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf["max_open_trades"]
assert hasattr(hyperopt.backtesting, "_position_stacking")
2024-05-12 14:04:55 +00:00
@pytest.mark.parametrize(
"space",
[
("buy"),
("sell"),
("protection"),
],
)
def test_simplified_interface_failed(mocker, hyperopt_conf, space) -> None:
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.optimize.hyperopt.dump", MagicMock())
mocker.patch("freqtrade.optimize.hyperopt.file_dump_json")
mocker.patch(
2024-05-12 14:04:55 +00:00
"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=[]
)
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
hyperopt_conf.update({"spaces": space})
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
with pytest.raises(OperationalException, match=f"The '{space}' space is included into *"):
hyperopt.start()
2023-11-05 15:25:23 +00:00
def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmp_path, fee) -> None:
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
mocker.patch(f"{EXMS}.get_fee", fee)
# Dummy-reduce points to ensure scikit-learn is forced to generate new values
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2)
(tmp_path / "hyperopt_results").mkdir(parents=True)
2021-03-29 17:27:19 +00:00
# No hyperopt needed
2024-05-12 14:04:55 +00:00
hyperopt_conf.update(
{
"strategy": "HyperoptableStrategy",
"user_data_dir": tmp_path,
"hyperopt_random_state": 42,
"spaces": ["all"],
}
)
2021-03-29 17:27:19 +00:00
hyperopt = Hyperopt(hyperopt_conf)
2022-02-06 04:36:28 +00:00
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
2021-03-29 17:27:19 +00:00
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
2021-04-24 05:18:35 +00:00
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
2023-03-26 09:30:44 +00:00
assert hyperopt.backtesting.strategy.bot_started is True
assert hyperopt.backtesting.strategy.bot_loop_started is False
2021-04-24 05:18:35 +00:00
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
2021-04-24 05:18:35 +00:00
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
2021-08-04 18:01:28 +00:00
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
assert hyperopt.backtesting.strategy.max_open_trades == 1
2021-04-24 05:18:35 +00:00
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
assert isinstance(buy_rsi_range, range)
# Range from 0 - 50 (inclusive)
assert len(list(buy_rsi_range)) == 51
2021-03-29 17:27:19 +00:00
hyperopt.start()
2021-08-04 18:01:28 +00:00
# All values should've changed.
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value != 30
assert hyperopt.backtesting.strategy.buy_rsi.value != 35
assert hyperopt.backtesting.strategy.sell_rsi.value != 74
assert hyperopt.backtesting.strategy.max_open_trades != 1
2021-04-09 20:15:24 +00:00
2024-05-12 14:04:55 +00:00
hyperopt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: "ET1"
with pytest.raises(OperationalException, match="Estimator ET1 not supported."):
hyperopt.get_optimizer([], 2)
2021-04-09 20:15:24 +00:00
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
2023-11-05 15:25:23 +00:00
def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmp_path, fee) -> None:
2024-05-12 14:04:55 +00:00
mocker.patch(f"{EXMS}.validate_config", MagicMock())
mocker.patch(f"{EXMS}.get_fee", fee)
2024-06-04 05:04:50 +00:00
mocker.patch(f"{EXMS}.reload_markets")
2024-05-12 14:04:55 +00:00
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
2024-05-12 14:04:55 +00:00
mocker.patch("freqtrade.optimize.hyperopt.INITIAL_POINTS", 2)
# No hyperopt needed
2024-05-12 14:04:55 +00:00
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
2022-10-13 17:43:37 +00:00
hyperopt.backtesting.exchange.get_min_pair_stake_amount = lambda *x, **xx: 0.00001
hyperopt.backtesting.exchange.get_max_pair_stake_amount = lambda *x, **xx: 100.0
2022-10-13 17:43:37 +00:00
hyperopt.backtesting.exchange._markets = get_markets()
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
2023-03-26 09:30:44 +00:00
assert hyperopt.backtesting.strategy.bot_started is True
assert hyperopt.backtesting.strategy.bot_loop_started is False
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
assert isinstance(buy_rsi_range, range)
# Range from 0 - 50 (inclusive)
assert len(list(buy_rsi_range)) == 51
hyperopt.start()
2023-11-05 15:25:23 +00:00
def test_in_strategy_auto_hyperopt_per_epoch(mocker, hyperopt_conf, tmp_path, fee) -> None:
2022-09-11 15:51:30 +00:00
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
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(),
},
)
2022-09-11 15:51:30 +00:00
hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
2023-03-26 09:30:44 +00:00
assert hyperopt.backtesting.strategy.bot_loop_started is False
assert hyperopt.backtesting.strategy.bot_started is True
2022-09-11 15:51:30 +00:00
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
assert isinstance(buy_rsi_range, range)
# Range from 0 - 50 (inclusive)
assert len(list(buy_rsi_range)) == 51
hyperopt.start()
# backtesting should be called 3 times (once per epoch)
assert go.call_count == 3
2021-04-09 20:15:24 +00:00
def test_SKDecimal():
space = SKDecimal(1, 2, decimals=2)
assert 1.5 in space
assert 2.5 not in space
assert space.low == 100
assert space.high == 200
assert space.inverse_transform([200]) == [2.0]
assert space.inverse_transform([100]) == [1.0]
assert space.inverse_transform([150, 160]) == [1.5, 1.6]
assert space.transform([1.5]) == [150]
assert space.transform([2.0]) == [200]
assert space.transform([1.0]) == [100]
assert space.transform([1.5, 1.6]) == [150, 160]
2023-11-05 15:25:23 +00:00
def test_stake_amount_unlimited_max_open_trades(mocker, hyperopt_conf, tmp_path, fee) -> None:
# 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)
2024-05-12 14:04:55 +00:00
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)
2024-05-12 14:04:55 +00:00
mocker.patch(
"freqtrade.optimize.hyperopt.Hyperopt._get_params_dict",
return_value={"max_open_trades": -1},
)
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
assert hyperopt.backtesting.strategy.max_open_trades == 1
hyperopt.start()
assert hyperopt.backtesting.strategy.max_open_trades == 1
2023-11-05 15:15:21 +00:00
def test_max_open_trades_dump(mocker, hyperopt_conf, tmp_path, fee, capsys) -> None:
# This test is to ensure that after hyperopting, max_open_trades is never
# saved as inf in the output json params
patch_exchange(mocker)
2024-05-12 14:04:55 +00:00
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)
2024-05-12 14:04:55 +00:00
mocker.patch(
"freqtrade.optimize.hyperopt.Hyperopt._get_params_dict",
return_value={"max_open_trades": -1},
)
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
hyperopt.start()
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
2024-05-12 14:04:55 +00:00
assert "max_open_trades = -1" in out
assert "max_open_trades = inf" not in out
##############
2024-05-12 14:04:55 +00:00
hyperopt_conf.update({"print_json": True})
hyperopt = Hyperopt(hyperopt_conf)
2024-05-12 14:04:55 +00:00
mocker.patch(
"freqtrade.optimize.hyperopt.Hyperopt._get_params_dict",
return_value={"max_open_trades": -1},
)
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
hyperopt.start()
2024-01-24 19:31:38 +00:00
out, _err = capsys.readouterr()
assert '"max_open_trades":-1' in out
2023-11-05 15:15:21 +00:00
def test_max_open_trades_consistency(mocker, hyperopt_conf, tmp_path, fee) -> None:
# 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)
2024-05-12 14:04:55 +00:00
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,
}
)
hyperopt = Hyperopt(hyperopt_conf)
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
hyperopt.custom_hyperopt.max_open_trades_space = lambda: [
2024-05-12 14:04:55 +00:00
Integer(1, 10, name="max_open_trades")
]
first_time_evaluated = False
def stake_amount_interceptor(func):
@wraps(func)
def wrapper(*args, **kwargs):
nonlocal first_time_evaluated
stake_amount = func(*args, **kwargs)
if first_time_evaluated is False:
assert stake_amount == 1
first_time_evaluated = True
return stake_amount
2024-05-12 14:04:55 +00:00
return wrapper
hyperopt.backtesting.wallets._calculate_unlimited_stake_amount = stake_amount_interceptor(
2024-05-12 14:04:55 +00:00
hyperopt.backtesting.wallets._calculate_unlimited_stake_amount
)
hyperopt.start()
assert hyperopt.backtesting.strategy.max_open_trades == 8
2024-05-12 14:04:55 +00:00
assert hyperopt.config["max_open_trades"] == 8