Adapt tests to new loss-function method

This commit is contained in:
Matthias 2019-07-15 21:54:41 +02:00
parent 710443d200
commit e5170582de
2 changed files with 47 additions and 21 deletions

View File

@ -18,6 +18,7 @@ from pandas import DataFrame
from skopt import Optimizer from skopt import Optimizer
from skopt.space import Dimension from skopt.space import Dimension
from freqtrade import OperationalException
from freqtrade.configuration import Arguments from freqtrade.configuration import Arguments
from freqtrade.data.history import load_data, get_timeframe from freqtrade.data.history import load_data, get_timeframe
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
@ -71,18 +72,17 @@ class Hyperopt(Backtesting):
self.trials: List = [] self.trials: List = []
# Assign loss function # Assign loss function
if self.config['loss_function'] == 'legacy': if self.config.get('loss_function', 'legacy') == 'legacy':
self.calculate_loss = hyperopt_loss_legacy self.calculate_loss = hyperopt_loss_legacy
elif (self.config['loss_function'] == 'custom' and elif (self.config['loss_function'] == 'custom' and
hasattr(self.custom_hyperopt, 'hyperopt_loss_custom')): hasattr(self.custom_hyperopt, 'hyperopt_loss_custom')):
self.calculate_loss = self.custom_hyperopt.hyperopt_loss_custom # type: ignore self.calculate_loss = self.custom_hyperopt.hyperopt_loss_custom # type: ignore
# Implement fallback to avoid odd crashes when custom-hyperopt fails to load. # 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'): if not hasattr(self.custom_hyperopt, 'hyperopt_loss_custom'):
logger.warning("Could not load hyperopt configuration. " logger.warning("Could not load hyperopt configuration. "
"Falling back to legacy 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) # Populate functions here (hasattr is slow so should not be run during "regular" operations)
if hasattr(self.custom_hyperopt, 'populate_buy_trend'): if hasattr(self.custom_hyperopt, 'populate_buy_trend'):

View File

@ -2,20 +2,24 @@
import os import os
from datetime import datetime from datetime import datetime
from unittest.mock import MagicMock from unittest.mock import MagicMock
from filelock import Timeout
import pandas as pd import pandas as pd
import pytest import pytest
from arrow import Arrow
from filelock import Timeout
from freqtrade import DependencyException from freqtrade import DependencyException
from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.history import load_tickerdata_file 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 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.resolvers.hyperopt_resolver import HyperOptResolver
from freqtrade.state import RunMode 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) patched_configuration_load_config_file)
@ -25,6 +29,21 @@ def hyperopt(default_conf, mocker):
return Hyperopt(default_conf) 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 # Functions for recurrent object patching
def create_trials(mocker, hyperopt) -> None: 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: def test_loss_calculation_prefer_correct_trade_count(hyperopt, hyperopt_results) -> None:
correct = hyperopt.calculate_loss(hyperopt_results, hyperopt.target_trades)
correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20) over = hyperopt.calculate_loss(hyperopt_results, hyperopt.target_trades + 100)
over = hyperopt.calculate_loss(1, hyperopt.target_trades + 100, 20) under = hyperopt.calculate_loss(hyperopt_results, hyperopt.target_trades - 100)
under = hyperopt.calculate_loss(1, hyperopt.target_trades - 100, 20)
assert over > correct assert over > correct
assert under > correct assert under > correct
def test_loss_calculation_prefer_shorter_trades(hyperopt) -> None: def test_loss_calculation_prefer_shorter_trades(hyperopt, hyperopt_results) -> None:
shorter = hyperopt.calculate_loss(1, 100, 20) resultsb = hyperopt_results.copy()
longer = hyperopt.calculate_loss(1, 100, 30) resultsb['trade_duration'][1] = 20
longer = hyperopt.calculate_loss(hyperopt_results, 100)
shorter = hyperopt.calculate_loss(resultsb, 100)
assert shorter < longer assert shorter < longer
def test_loss_calculation_has_limited_profit(hyperopt) -> None: def test_loss_calculation_has_limited_profit(hyperopt, hyperopt_results) -> None:
correct = hyperopt.calculate_loss(hyperopt.expected_max_profit, hyperopt.target_trades, 20) results_over = hyperopt_results.copy()
over = hyperopt.calculate_loss(hyperopt.expected_max_profit * 2, hyperopt.target_trades, 20) results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2
under = hyperopt.calculate_loss(hyperopt.expected_max_profit / 2, hyperopt.target_trades, 20) results_under = hyperopt_results.copy()
assert over == correct 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 assert under > correct
@ -472,7 +498,7 @@ def test_generate_optimizer(mocker, default_conf) -> None:
) )
mocker.patch( mocker.patch(
'freqtrade.optimize.hyperopt.get_timeframe', '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) patch_exchange(mocker)
mocker.patch('freqtrade.optimize.hyperopt.load', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.load', MagicMock())