from datetime import datetime from unittest.mock import MagicMock import pytest from freqtrade.exceptions import OperationalException from freqtrade.optimize.hyperopt_loss.hyperopt_loss_short_trade_dur import ShortTradeDurHyperOptLoss from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver def test_hyperoptlossresolver_noname(default_conf): with pytest.raises(OperationalException, match="No Hyperopt loss set. Please use `--hyperopt-loss` to specify " "the Hyperopt-Loss class to use."): HyperOptLossResolver.load_hyperoptloss(default_conf) def test_hyperoptlossresolver(mocker, default_conf) -> None: hl = ShortTradeDurHyperOptLoss mocker.patch( 'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver.load_object', MagicMock(return_value=hl()) ) default_conf.update({'hyperopt_loss': 'SharpeHyperOptLossDaily'}) x = HyperOptLossResolver.load_hyperoptloss(default_conf) assert hasattr(x, "hyperopt_loss_function") def test_hyperoptlossresolver_wrongname(default_conf) -> None: default_conf.update({'hyperopt_loss': "NonExistingLossClass"}) with pytest.raises(OperationalException, match=r'Impossible to load HyperoptLoss.*'): HyperOptLossResolver.load_hyperoptloss(default_conf) def test_loss_calculation_prefer_correct_trade_count(hyperopt_conf, hyperopt_results) -> None: hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) 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)) assert over > correct assert under > correct def test_loss_calculation_prefer_shorter_trades(hyperopt_conf, hyperopt_results) -> None: resultsb = hyperopt_results.copy() resultsb.loc[1, 'trade_duration'] = 20 hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) 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)) assert shorter < longer def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> None: results_over = hyperopt_results.copy() results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 results_under = hyperopt_results.copy() results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) 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)) assert over < correct assert under > correct @pytest.mark.parametrize('lossfunction', [ "OnlyProfitHyperOptLoss", "SortinoHyperOptLoss", "SortinoHyperOptLossDaily", "SharpeHyperOptLoss", "SharpeHyperOptLossDaily", "MaxDrawDownHyperOptLoss", "MaxDrawDownRelativeHyperOptLoss", "CalmarHyperOptLoss", "ProfitDrawDownHyperOptLoss", ]) def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None: results_over = hyperopt_results.copy() results_over['profit_abs'] = hyperopt_results['profit_abs'] * 2 + 0.2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 results_under = hyperopt_results.copy() results_under['profit_abs'] = hyperopt_results['profit_abs'] / 2 - 0.2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 default_conf.update({'hyperopt_loss': lossfunction}) hl = HyperOptLossResolver.load_hyperoptloss(default_conf) 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, backtest_stats={'profit_total': hyperopt_results['profit_abs'].sum()} ) 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, backtest_stats={'profit_total': results_over['profit_abs'].sum()} ) 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, backtest_stats={'profit_total': results_under['profit_abs'].sum()} ) assert over < correct assert under > correct