Merge pull request #2879 from freqtrade/sortino_hyperopt_loss

Sortino hyperopt loss
This commit is contained in:
hroff-1902
2020-02-29 11:36:27 +03:00
committed by GitHub
6 changed files with 174 additions and 15 deletions

View File

@@ -257,7 +257,8 @@ AVAILABLE_CLI_OPTIONS = {
help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). '
'Different functions can generate completely different results, '
'since the target for optimization is different. Built-in Hyperopt-loss-functions are: '
'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss, SharpeHyperOptLossDaily.'
'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss, SharpeHyperOptLossDaily, '
'SortinoHyperOptLoss, SortinoHyperOptLossDaily.'
'(default: `%(default)s`).',
metavar='NAME',
default=constants.DEFAULT_HYPEROPT_LOSS,

View File

@@ -0,0 +1,49 @@
"""
SortinoHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization.
"""
from datetime import datetime
from pandas import DataFrame
import numpy as np
from freqtrade.optimize.hyperopt import IHyperOptLoss
class SortinoHyperOptLoss(IHyperOptLoss):
"""
Defines the loss function for hyperopt.
This implementation uses the Sortino Ratio calculation.
"""
@staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int,
min_date: datetime, max_date: datetime,
*args, **kwargs) -> float:
"""
Objective function, returns smaller number for more optimal results.
Uses Sortino Ratio calculation.
"""
total_profit = results["profit_percent"]
days_period = (max_date - min_date).days
# adding slippage of 0.1% per trade
total_profit = total_profit - 0.0005
expected_returns_mean = total_profit.sum() / days_period
results['downside_returns'] = 0
results.loc[total_profit < 0, 'downside_returns'] = results['profit_percent']
down_stdev = np.std(results['downside_returns'])
if np.std(total_profit) != 0.0:
sortino_ratio = expected_returns_mean / down_stdev * np.sqrt(365)
else:
# Define high (negative) sortino ratio to be clear that this is NOT optimal.
sortino_ratio = -20.
# print(expected_returns_mean, down_stdev, sortino_ratio)
return -sortino_ratio

View File

@@ -0,0 +1,70 @@
"""
SortinoHyperOptLossDaily
This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization.
"""
import math
from datetime import datetime
from pandas import DataFrame, date_range
from freqtrade.optimize.hyperopt import IHyperOptLoss
class SortinoHyperOptLossDaily(IHyperOptLoss):
"""
Defines the loss function for hyperopt.
This implementation uses the Sortino Ratio calculation.
"""
@staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int,
min_date: datetime, max_date: datetime,
*args, **kwargs) -> float:
"""
Objective function, returns smaller number for more optimal results.
Uses Sortino Ratio calculation.
Sortino Ratio calculated as described in
http://www.redrockcapital.com/Sortino__A__Sharper__Ratio_Red_Rock_Capital.pdf
"""
resample_freq = '1D'
slippage_per_trade_ratio = 0.0005
days_in_year = 365
minimum_acceptable_return = 0.0
# apply slippage per trade to profit_percent
results.loc[:, 'profit_percent_after_slippage'] = \
results['profit_percent'] - slippage_per_trade_ratio
# create the index within the min_date and end max_date
t_index = date_range(start=min_date, end=max_date, freq=resample_freq,
normalize=True)
sum_daily = (
results.resample(resample_freq, on='close_time').agg(
{"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0)
)
total_profit = sum_daily["profit_percent_after_slippage"] - minimum_acceptable_return
expected_returns_mean = total_profit.mean()
sum_daily['downside_returns'] = 0
sum_daily.loc[total_profit < 0, 'downside_returns'] = total_profit
total_downside = sum_daily['downside_returns']
# Here total_downside contains min(0, P - MAR) values,
# where P = sum_daily["profit_percent_after_slippage"]
down_stdev = math.sqrt((total_downside**2).sum() / len(total_downside))
if (down_stdev != 0.):
sortino_ratio = expected_returns_mean / down_stdev * math.sqrt(days_in_year)
else:
# Define high (negative) sortino ratio to be clear that this is NOT optimal.
sortino_ratio = -20.
# print(t_index, sum_daily, total_profit)
# print(minimum_acceptable_return, expected_returns_mean, down_stdev, sortino_ratio)
return -sortino_ratio