Merge pull request #2048 from hroff-1902/hyperopt-loss-onlyprofit2

minor: add OnlyProfitHyperOptLoss
This commit is contained in:
Matthias 2019-07-25 20:18:05 +02:00 committed by GitHub
commit 098a23adc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 16 deletions

View File

@ -156,10 +156,10 @@ Each hyperparameter tuning requires a target. This is usually defined as a loss
By default, FreqTrade uses a loss function, which has been with freqtrade since the beginning and optimizes mostly for short trade duration and avoiding losses. By default, FreqTrade uses a loss function, which has been with freqtrade since the beginning and optimizes mostly for short trade duration and avoiding losses.
A different version this can be used by using the `--hyperopt-loss <Class-name>` argument. A different loss function can be specified by using the `--hyperopt-loss <Class-name>` argument.
This class should be in it's own file within the `user_data/hyperopts/` directory. This class should be in its own file within the `user_data/hyperopts/` directory.
Currently, the following loss functions are builtin: `SharpeHyperOptLoss` and `DefaultHyperOptLoss`. Currently, the following loss functions are builtin: `DefaultHyperOptLoss` (default legacy Freqtrade hyperoptimization loss function), `SharpeHyperOptLoss` (optimizes Sharpe Ratio calculated on the trade returns) and `OnlyProfitHyperOptLoss` (which takes only amount of profit into consideration).
### Creating and using a custom loss function ### Creating and using a custom loss function

View File

@ -3,27 +3,26 @@ DefaultHyperOptLoss
This module defines the default HyperoptLoss class which is being used for This module defines the default HyperoptLoss class which is being used for
Hyperoptimization. Hyperoptimization.
""" """
from math import exp from math import exp
from pandas import DataFrame from pandas import DataFrame
from freqtrade.optimize.hyperopt import IHyperOptLoss from freqtrade.optimize.hyperopt import IHyperOptLoss
# Define some constants:
# set TARGET_TRADES to suit your number concurrent trades so its realistic # Set TARGET_TRADES to suit your number concurrent trades so its realistic
# to the number of days # to the number of days
TARGET_TRADES = 600 TARGET_TRADES = 600
# This is assumed to be expected avg profit * expected trade count. # This is assumed to be expected avg profit * expected trade count.
# For example, for 0.35% avg per trade (or 0.0035 as ratio) and 1100 trades, # For example, for 0.35% avg per trade (or 0.0035 as ratio) and 1100 trades,
# self.expected_max_profit = 3.85 # expected max profit = 3.85
# Check that the reported Σ% values do not exceed this! # Check that the reported Σ% values do not exceed this!
# Note, this is ratio. 3.85 stated above means 385Σ%. # Note, this is ratio. 3.85 stated above means 385Σ%.
EXPECTED_MAX_PROFIT = 3.0 EXPECTED_MAX_PROFIT = 3.0
# max average trade duration in minutes # Max average trade duration in minutes.
# if eval ends with higher value, we consider it a failed eval # If eval ends with higher value, we consider it a failed eval.
MAX_ACCEPTED_TRADE_DURATION = 300 MAX_ACCEPTED_TRADE_DURATION = 300

View File

@ -0,0 +1,38 @@
"""
OnlyProfitHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization.
"""
from pandas import DataFrame
from freqtrade.optimize.hyperopt import IHyperOptLoss
# This is assumed to be expected avg profit * expected trade count.
# For example, for 0.35% avg per trade (or 0.0035 as ratio) and 1100 trades,
# expected max profit = 3.85
#
# Note, this is ratio. 3.85 stated above means 385Σ%, 3.0 means 300Σ%.
#
# In this implementation it's only used in calculation of the resulting value
# of the objective function as a normalization coefficient and does not
# represent any limit for profits as in the Freqtrade legacy default loss function.
EXPECTED_MAX_PROFIT = 3.0
class OnlyProfitHyperOptLoss(IHyperOptLoss):
"""
Defines the loss function for hyperopt.
This implementation takes only profit into account.
"""
@staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int,
*args, **kwargs) -> float:
"""
Objective function, returns smaller number for better results.
"""
total_profit = results.profit_percent.sum()
return 1 - total_profit / EXPECTED_MAX_PROFIT

View File

@ -1,8 +1,9 @@
""" """
IHyperOptLoss interface SharpeHyperOptLoss
This module defines the interface for the loss-function for hyperopts
"""
This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization.
"""
from datetime import datetime from datetime import datetime
from pandas import DataFrame from pandas import DataFrame
@ -13,8 +14,9 @@ from freqtrade.optimize.hyperopt import IHyperOptLoss
class SharpeHyperOptLoss(IHyperOptLoss): class SharpeHyperOptLoss(IHyperOptLoss):
""" """
Defines the a loss function for hyperopt. Defines the loss function for hyperopt.
This implementation uses the sharpe ratio calculation.
This implementation uses the Sharpe Ratio calculation.
""" """
@staticmethod @staticmethod
@ -22,8 +24,9 @@ class SharpeHyperOptLoss(IHyperOptLoss):
min_date: datetime, max_date: datetime, min_date: datetime, max_date: datetime,
*args, **kwargs) -> float: *args, **kwargs) -> float:
""" """
Objective function, returns smaller number for more optimal results Objective function, returns smaller number for more optimal results.
Using sharpe ratio calculation
Uses Sharpe Ratio calculation.
""" """
total_profit = results.profit_percent total_profit = results.profit_percent
days_period = (max_date - min_date).days days_period = (max_date - min_date).days

View File

@ -337,6 +337,24 @@ def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> N
assert under > correct assert under > correct
def test_onlyprofit_loss_prefers_higher_profits(default_conf, 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
default_conf.update({'hyperopt_loss': 'OnlyProfitHyperOptLoss'})
hl = HyperOptLossResolver(default_conf).hyperoptloss
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct
assert under > correct
def test_log_results_if_loss_improves(hyperopt, capsys) -> None: def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
hyperopt.current_best_loss = 2 hyperopt.current_best_loss = 2
hyperopt.log_results( hyperopt.log_results(