From e5170582de5b777fe0a6513723d4853ccb9141f9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Jul 2019 21:54:41 +0200 Subject: [PATCH] Adapt tests to new loss-function method --- freqtrade/optimize/hyperopt.py | 6 +-- freqtrade/tests/optimize/test_hyperopt.py | 62 ++++++++++++++++------- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 8650c147f..cc9d9299c 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -18,6 +18,7 @@ from pandas import DataFrame from skopt import Optimizer from skopt.space import Dimension +from freqtrade import OperationalException from freqtrade.configuration import Arguments from freqtrade.data.history import load_data, get_timeframe from freqtrade.optimize.backtesting import Backtesting @@ -71,18 +72,17 @@ class Hyperopt(Backtesting): self.trials: List = [] # Assign loss function - if self.config['loss_function'] == 'legacy': + if self.config.get('loss_function', 'legacy') == 'legacy': self.calculate_loss = hyperopt_loss_legacy elif (self.config['loss_function'] == 'custom' and hasattr(self.custom_hyperopt, 'hyperopt_loss_custom')): self.calculate_loss = self.custom_hyperopt.hyperopt_loss_custom # type: ignore # Implement fallback to avoid odd crashes when custom-hyperopt fails to load. - # TODO: Maybe this should just stop hyperopt completely? if not hasattr(self.custom_hyperopt, 'hyperopt_loss_custom'): logger.warning("Could not load hyperopt configuration. " "Falling back to legacy configuration.") - self.calculate_loss = hyperopt_loss_legacy + raise OperationalException("Could not load hyperopt loss function.") # Populate functions here (hasattr is slow so should not be run during "regular" operations) if hasattr(self.custom_hyperopt, 'populate_buy_trend'): diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 39f83a0e0..889d8cb44 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -2,20 +2,24 @@ import os from datetime import datetime from unittest.mock import MagicMock -from filelock import Timeout import pandas as pd import pytest +from arrow import Arrow +from filelock import Timeout from freqtrade import DependencyException from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.history import load_tickerdata_file -from freqtrade.optimize.default_hyperopt import DefaultHyperOpts -from freqtrade.optimize.hyperopt import Hyperopt, HYPEROPT_LOCKFILE, TICKERDATA_PICKLE from freqtrade.optimize import setup_configuration, start_hyperopt +from freqtrade.optimize.default_hyperopt import DefaultHyperOpts +from freqtrade.optimize.hyperopt import (HYPEROPT_LOCKFILE, TICKERDATA_PICKLE, + Hyperopt) from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver from freqtrade.state import RunMode -from freqtrade.tests.conftest import (get_args, log_has, log_has_re, patch_exchange, +from freqtrade.strategy.interface import SellType +from freqtrade.tests.conftest import (get_args, log_has, log_has_re, + patch_exchange, patched_configuration_load_config_file) @@ -25,6 +29,21 @@ def hyperopt(default_conf, mocker): return Hyperopt(default_conf) +@pytest.fixture(scope='function') +def hyperopt_results(): + return pd.DataFrame( + { + 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], + 'profit_percent': [0.1, 0.2, 0.3], + 'profit_abs': [0.2, 0.4, 0.5], + 'trade_duration': [10, 30, 10], + 'profit': [2, 0, 0], + 'loss': [0, 0, 1], + 'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS] + } + ) + + # Functions for recurrent object patching def create_trials(mocker, hyperopt) -> None: """ @@ -254,26 +273,33 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: ) -def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None: - - correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20) - over = hyperopt.calculate_loss(1, hyperopt.target_trades + 100, 20) - under = hyperopt.calculate_loss(1, hyperopt.target_trades - 100, 20) +def test_loss_calculation_prefer_correct_trade_count(hyperopt, hyperopt_results) -> None: + correct = hyperopt.calculate_loss(hyperopt_results, hyperopt.target_trades) + over = hyperopt.calculate_loss(hyperopt_results, hyperopt.target_trades + 100) + under = hyperopt.calculate_loss(hyperopt_results, hyperopt.target_trades - 100) assert over > correct assert under > correct -def test_loss_calculation_prefer_shorter_trades(hyperopt) -> None: - shorter = hyperopt.calculate_loss(1, 100, 20) - longer = hyperopt.calculate_loss(1, 100, 30) +def test_loss_calculation_prefer_shorter_trades(hyperopt, hyperopt_results) -> None: + resultsb = hyperopt_results.copy() + resultsb['trade_duration'][1] = 20 + + longer = hyperopt.calculate_loss(hyperopt_results, 100) + shorter = hyperopt.calculate_loss(resultsb, 100) assert shorter < longer -def test_loss_calculation_has_limited_profit(hyperopt) -> None: - correct = hyperopt.calculate_loss(hyperopt.expected_max_profit, hyperopt.target_trades, 20) - over = hyperopt.calculate_loss(hyperopt.expected_max_profit * 2, hyperopt.target_trades, 20) - under = hyperopt.calculate_loss(hyperopt.expected_max_profit / 2, hyperopt.target_trades, 20) - assert over == correct +def test_loss_calculation_has_limited_profit(hyperopt, hyperopt_results) -> None: + results_over = hyperopt_results.copy() + results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2 + results_under = hyperopt_results.copy() + results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 + + correct = hyperopt.calculate_loss(hyperopt_results, hyperopt.target_trades) + over = hyperopt.calculate_loss(results_over, hyperopt.target_trades) + under = hyperopt.calculate_loss(results_under, hyperopt.target_trades) + assert over < correct assert under > correct @@ -472,7 +498,7 @@ def test_generate_optimizer(mocker, default_conf) -> None: ) mocker.patch( 'freqtrade.optimize.hyperopt.get_timeframe', - MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) + MagicMock(return_value=(Arrow(2017, 12, 10), Arrow(2017, 12, 13))) ) patch_exchange(mocker) mocker.patch('freqtrade.optimize.hyperopt.load', MagicMock())