2020-10-28 13:36:19 +00:00
|
|
|
from datetime import datetime
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
from freqtrade.exceptions import OperationalException
|
2022-04-30 11:59:23 +00:00
|
|
|
from freqtrade.optimize.hyperopt_loss.hyperopt_loss_short_trade_dur import ShortTradeDurHyperOptLoss
|
2020-10-28 13:36:19 +00:00
|
|
|
from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver
|
|
|
|
|
|
|
|
|
|
|
|
def test_hyperoptlossresolver_noname(default_conf):
|
2024-05-12 14:04:01 +00:00
|
|
|
with pytest.raises(
|
|
|
|
OperationalException,
|
|
|
|
match="No Hyperopt loss set. Please use `--hyperopt-loss` to specify "
|
|
|
|
"the Hyperopt-Loss class to use.",
|
|
|
|
):
|
2020-10-28 13:36:19 +00:00
|
|
|
HyperOptLossResolver.load_hyperoptloss(default_conf)
|
|
|
|
|
|
|
|
|
|
|
|
def test_hyperoptlossresolver(mocker, default_conf) -> None:
|
|
|
|
hl = ShortTradeDurHyperOptLoss
|
|
|
|
mocker.patch(
|
2024-05-12 14:04:01 +00:00
|
|
|
"freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver.load_object",
|
|
|
|
MagicMock(return_value=hl()),
|
2020-10-28 13:36:19 +00:00
|
|
|
)
|
2024-05-12 14:04:01 +00:00
|
|
|
default_conf.update({"hyperopt_loss": "SharpeHyperOptLossDaily"})
|
2020-10-28 13:36:19 +00:00
|
|
|
x = HyperOptLossResolver.load_hyperoptloss(default_conf)
|
|
|
|
assert hasattr(x, "hyperopt_loss_function")
|
|
|
|
|
|
|
|
|
|
|
|
def test_hyperoptlossresolver_wrongname(default_conf) -> None:
|
2024-05-12 14:04:01 +00:00
|
|
|
default_conf.update({"hyperopt_loss": "NonExistingLossClass"})
|
2020-10-28 13:36:19 +00:00
|
|
|
|
2024-05-12 14:04:01 +00:00
|
|
|
with pytest.raises(OperationalException, match=r"Impossible to load HyperoptLoss.*"):
|
2020-10-28 13:36:19 +00:00
|
|
|
HyperOptLossResolver.load_hyperoptloss(default_conf)
|
|
|
|
|
|
|
|
|
|
|
|
def test_loss_calculation_prefer_correct_trade_count(hyperopt_conf, hyperopt_results) -> None:
|
2024-05-12 14:04:01 +00:00
|
|
|
hyperopt_conf.update({"hyperopt_loss": "ShortTradeDurHyperOptLoss"})
|
2020-10-28 13:36:19 +00:00
|
|
|
hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf)
|
2024-05-12 14:04:01 +00:00
|
|
|
correct = hl.hyperopt_loss_function(
|
|
|
|
hyperopt_results, 600, datetime(2019, 1, 1), datetime(2019, 5, 1)
|
|
|
|
)
|
|
|
|
over = hl.hyperopt_loss_function(
|
|
|
|
hyperopt_results, 600 + 100, datetime(2019, 1, 1), datetime(2019, 5, 1)
|
|
|
|
)
|
|
|
|
under = hl.hyperopt_loss_function(
|
|
|
|
hyperopt_results, 600 - 100, datetime(2019, 1, 1), datetime(2019, 5, 1)
|
|
|
|
)
|
2020-10-28 13:36:19 +00:00
|
|
|
assert over > correct
|
|
|
|
assert under > correct
|
|
|
|
|
|
|
|
|
|
|
|
def test_loss_calculation_prefer_shorter_trades(hyperopt_conf, hyperopt_results) -> None:
|
|
|
|
resultsb = hyperopt_results.copy()
|
2024-05-12 14:04:01 +00:00
|
|
|
resultsb.loc[1, "trade_duration"] = 20
|
2020-10-28 13:36:19 +00:00
|
|
|
|
2024-05-12 14:04:01 +00:00
|
|
|
hyperopt_conf.update({"hyperopt_loss": "ShortTradeDurHyperOptLoss"})
|
2020-10-28 13:36:19 +00:00
|
|
|
hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf)
|
2024-05-12 14:04:01 +00:00
|
|
|
longer = hl.hyperopt_loss_function(
|
|
|
|
hyperopt_results, 100, datetime(2019, 1, 1), datetime(2019, 5, 1)
|
|
|
|
)
|
|
|
|
shorter = hl.hyperopt_loss_function(resultsb, 100, datetime(2019, 1, 1), datetime(2019, 5, 1))
|
2020-10-28 13:36:19 +00:00
|
|
|
assert shorter < longer
|
|
|
|
|
|
|
|
|
|
|
|
def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> None:
|
|
|
|
results_over = hyperopt_results.copy()
|
2024-05-12 14:04:01 +00:00
|
|
|
results_over["profit_ratio"] = hyperopt_results["profit_ratio"] * 2
|
2020-10-28 13:36:19 +00:00
|
|
|
results_under = hyperopt_results.copy()
|
2024-05-12 14:04:01 +00:00
|
|
|
results_under["profit_ratio"] = hyperopt_results["profit_ratio"] / 2
|
2020-10-28 13:36:19 +00:00
|
|
|
|
2024-05-12 14:04:01 +00:00
|
|
|
hyperopt_conf.update({"hyperopt_loss": "ShortTradeDurHyperOptLoss"})
|
2020-10-28 13:36:19 +00:00
|
|
|
hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf)
|
2024-05-12 14:04:01 +00:00
|
|
|
correct = hl.hyperopt_loss_function(
|
|
|
|
hyperopt_results, 600, datetime(2019, 1, 1), datetime(2019, 5, 1)
|
|
|
|
)
|
|
|
|
over = hl.hyperopt_loss_function(results_over, 600, datetime(2019, 1, 1), datetime(2019, 5, 1))
|
|
|
|
under = hl.hyperopt_loss_function(
|
|
|
|
results_under, 600, datetime(2019, 1, 1), datetime(2019, 5, 1)
|
|
|
|
)
|
2020-10-28 13:36:19 +00:00
|
|
|
assert over < correct
|
|
|
|
assert under > correct
|
|
|
|
|
|
|
|
|
2024-05-12 14:04:01 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"lossfunction",
|
|
|
|
[
|
|
|
|
"OnlyProfitHyperOptLoss",
|
|
|
|
"SortinoHyperOptLoss",
|
|
|
|
"SortinoHyperOptLossDaily",
|
|
|
|
"SharpeHyperOptLoss",
|
|
|
|
"SharpeHyperOptLossDaily",
|
|
|
|
"MaxDrawDownHyperOptLoss",
|
|
|
|
"MaxDrawDownRelativeHyperOptLoss",
|
|
|
|
"CalmarHyperOptLoss",
|
|
|
|
"ProfitDrawDownHyperOptLoss",
|
|
|
|
],
|
|
|
|
)
|
2021-10-02 12:30:24 +00:00
|
|
|
def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None:
|
2020-10-28 13:36:19 +00:00
|
|
|
results_over = hyperopt_results.copy()
|
2024-05-12 14:04:01 +00:00
|
|
|
results_over["profit_abs"] = hyperopt_results["profit_abs"] * 2 + 0.2
|
|
|
|
results_over["profit_ratio"] = hyperopt_results["profit_ratio"] * 2
|
2020-10-28 13:36:19 +00:00
|
|
|
results_under = hyperopt_results.copy()
|
2024-05-12 14:04:01 +00:00
|
|
|
results_under["profit_abs"] = hyperopt_results["profit_abs"] / 2 - 0.2
|
|
|
|
results_under["profit_ratio"] = hyperopt_results["profit_ratio"] / 2
|
2020-10-28 13:36:19 +00:00
|
|
|
|
2024-05-12 14:04:01 +00:00
|
|
|
default_conf.update({"hyperopt_loss": lossfunction})
|
2020-10-28 13:36:19 +00:00
|
|
|
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
|
2021-10-24 07:01:13 +00:00
|
|
|
correct = hl.hyperopt_loss_function(
|
|
|
|
hyperopt_results,
|
|
|
|
trade_count=len(hyperopt_results),
|
|
|
|
min_date=datetime(2019, 1, 1),
|
|
|
|
max_date=datetime(2019, 5, 1),
|
|
|
|
config=default_conf,
|
|
|
|
processed=None,
|
2024-05-12 14:04:01 +00:00
|
|
|
backtest_stats={"profit_total": hyperopt_results["profit_abs"].sum()},
|
2022-02-06 14:40:54 +00:00
|
|
|
)
|
2021-10-24 07:01:13 +00:00
|
|
|
over = hl.hyperopt_loss_function(
|
|
|
|
results_over,
|
|
|
|
trade_count=len(results_over),
|
|
|
|
min_date=datetime(2019, 1, 1),
|
|
|
|
max_date=datetime(2019, 5, 1),
|
|
|
|
config=default_conf,
|
|
|
|
processed=None,
|
2024-05-12 14:04:01 +00:00
|
|
|
backtest_stats={"profit_total": results_over["profit_abs"].sum()},
|
2021-10-24 07:01:13 +00:00
|
|
|
)
|
|
|
|
under = hl.hyperopt_loss_function(
|
|
|
|
results_under,
|
|
|
|
trade_count=len(results_under),
|
|
|
|
min_date=datetime(2019, 1, 1),
|
|
|
|
max_date=datetime(2019, 5, 1),
|
|
|
|
config=default_conf,
|
|
|
|
processed=None,
|
2024-05-12 14:04:01 +00:00
|
|
|
backtest_stats={"profit_total": results_under["profit_abs"].sum()},
|
2021-10-24 07:01:13 +00:00
|
|
|
)
|
2020-10-28 13:36:19 +00:00
|
|
|
assert over < correct
|
|
|
|
assert under > correct
|