ruff format: hyperopt-loss

This commit is contained in:
Matthias
2024-05-12 17:13:50 +02:00
parent ab3dbb7fbc
commit f1ef537dfa
10 changed files with 100 additions and 62 deletions
@@ -4,6 +4,7 @@ CalmarHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
from datetime import datetime from datetime import datetime
from pandas import DataFrame from pandas import DataFrame
@@ -21,15 +22,21 @@ class CalmarHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(
min_date: datetime, max_date: datetime, results: DataFrame,
config: Config, *args, **kwargs) -> float: trade_count: int,
min_date: datetime,
max_date: datetime,
config: Config,
*args,
**kwargs,
) -> float:
""" """
Objective function, returns smaller number for more optimal results. Objective function, returns smaller number for more optimal results.
Uses Calmar Ratio calculation. Uses Calmar Ratio calculation.
""" """
starting_balance = config['dry_run_wallet'] starting_balance = config["dry_run_wallet"]
calmar_ratio = calculate_calmar(results, min_date, max_date, starting_balance) calmar_ratio = calculate_calmar(results, min_date, max_date, starting_balance)
# print(expected_returns_mean, max_drawdown, calmar_ratio) # print(expected_returns_mean, max_drawdown, calmar_ratio)
return -calmar_ratio return -calmar_ratio
@@ -4,6 +4,7 @@ MaxDrawDownHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
from datetime import datetime from datetime import datetime
from pandas import DataFrame from pandas import DataFrame
@@ -13,7 +14,6 @@ from freqtrade.optimize.hyperopt import IHyperOptLoss
class MaxDrawDownHyperOptLoss(IHyperOptLoss): class MaxDrawDownHyperOptLoss(IHyperOptLoss):
""" """
Defines the loss function for hyperopt. Defines the loss function for hyperopt.
@@ -22,19 +22,23 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(
min_date: datetime, max_date: datetime, results: DataFrame,
*args, **kwargs) -> float: trade_count: int,
min_date: datetime,
max_date: datetime,
*args,
**kwargs,
) -> float:
""" """
Objective function. Objective function.
Uses profit ratio weighted max_drawdown when drawdown is available. Uses profit ratio weighted max_drawdown when drawdown is available.
Otherwise directly optimizes profit ratio. Otherwise directly optimizes profit ratio.
""" """
total_profit = results['profit_abs'].sum() total_profit = results["profit_abs"].sum()
try: try:
max_drawdown = calculate_max_drawdown(results, value_col='profit_abs') max_drawdown = calculate_max_drawdown(results, value_col="profit_abs")
except ValueError: except ValueError:
# No losing trade, therefore no drawdown. # No losing trade, therefore no drawdown.
return -total_profit return -total_profit
@@ -4,6 +4,7 @@ MaxDrawDownRelativeHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
from pandas import DataFrame from pandas import DataFrame
from freqtrade.constants import Config from freqtrade.constants import Config
@@ -12,7 +13,6 @@ from freqtrade.optimize.hyperopt import IHyperOptLoss
class MaxDrawDownRelativeHyperOptLoss(IHyperOptLoss): class MaxDrawDownRelativeHyperOptLoss(IHyperOptLoss):
""" """
Defines the loss function for hyperopt. Defines the loss function for hyperopt.
@@ -21,24 +21,20 @@ class MaxDrawDownRelativeHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, config: Config, def hyperopt_loss_function(results: DataFrame, config: Config, *args, **kwargs) -> float:
*args, **kwargs) -> float:
""" """
Objective function. Objective function.
Uses profit ratio weighted max_drawdown when drawdown is available. Uses profit ratio weighted max_drawdown when drawdown is available.
Otherwise directly optimizes profit ratio. Otherwise directly optimizes profit ratio.
""" """
total_profit = results['profit_abs'].sum() total_profit = results["profit_abs"].sum()
try: try:
drawdown_df = calculate_underwater( drawdown_df = calculate_underwater(
results, results, value_col="profit_abs", starting_balance=config["dry_run_wallet"]
value_col='profit_abs',
starting_balance=config['dry_run_wallet']
) )
max_drawdown = abs(min(drawdown_df['drawdown'])) max_drawdown = abs(min(drawdown_df["drawdown"]))
relative_drawdown = max(drawdown_df['drawdown_relative']) relative_drawdown = max(drawdown_df["drawdown_relative"])
if max_drawdown == 0: if max_drawdown == 0:
return -total_profit return -total_profit
return -total_profit / max_drawdown / relative_drawdown return -total_profit / max_drawdown / relative_drawdown
@@ -4,6 +4,7 @@ OnlyProfitHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
from pandas import DataFrame from pandas import DataFrame
from freqtrade.optimize.hyperopt import IHyperOptLoss from freqtrade.optimize.hyperopt import IHyperOptLoss
@@ -17,10 +18,9 @@ class OnlyProfitHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(results: DataFrame, trade_count: int, *args, **kwargs) -> float:
*args, **kwargs) -> float:
""" """
Objective function, returns smaller number for better results. Objective function, returns smaller number for better results.
""" """
total_profit = results['profit_abs'].sum() total_profit = results["profit_abs"].sum()
return -1 * total_profit return -1 * total_profit
@@ -7,6 +7,7 @@ Drawdown objective which can be used for Hyperoptimization.
Possible to change `DRAWDOWN_MULT` to penalize drawdown objective for Possible to change `DRAWDOWN_MULT` to penalize drawdown objective for
individual needs. individual needs.
""" """
from pandas import DataFrame from pandas import DataFrame
from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.data.metrics import calculate_max_drawdown
@@ -4,6 +4,7 @@ SharpeHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
from datetime import datetime from datetime import datetime
from pandas import DataFrame from pandas import DataFrame
@@ -21,15 +22,21 @@ class SharpeHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(
min_date: datetime, max_date: datetime, results: DataFrame,
config: Config, *args, **kwargs) -> float: trade_count: int,
min_date: datetime,
max_date: datetime,
config: Config,
*args,
**kwargs,
) -> float:
""" """
Objective function, returns smaller number for more optimal results. Objective function, returns smaller number for more optimal results.
Uses Sharpe Ratio calculation. Uses Sharpe Ratio calculation.
""" """
starting_balance = config['dry_run_wallet'] starting_balance = config["dry_run_wallet"]
sharp_ratio = calculate_sharpe(results, min_date, max_date, starting_balance) sharp_ratio = calculate_sharpe(results, min_date, max_date, starting_balance)
# print(expected_returns_mean, up_stdev, sharp_ratio) # print(expected_returns_mean, up_stdev, sharp_ratio)
return -sharp_ratio return -sharp_ratio
@@ -4,6 +4,7 @@ SharpeHyperOptLossDaily
This module defines the alternative HyperOptLoss class which can be used for This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
import math import math
from datetime import datetime from datetime import datetime
@@ -20,31 +21,38 @@ class SharpeHyperOptLossDaily(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(
min_date: datetime, max_date: datetime, results: DataFrame,
*args, **kwargs) -> float: trade_count: int,
min_date: datetime,
max_date: datetime,
*args,
**kwargs,
) -> float:
""" """
Objective function, returns smaller number for more optimal results. Objective function, returns smaller number for more optimal results.
Uses Sharpe Ratio calculation. Uses Sharpe Ratio calculation.
""" """
resample_freq = '1D' resample_freq = "1D"
slippage_per_trade_ratio = 0.0005 slippage_per_trade_ratio = 0.0005
days_in_year = 365 days_in_year = 365
annual_risk_free_rate = 0.0 annual_risk_free_rate = 0.0
risk_free_rate = annual_risk_free_rate / days_in_year risk_free_rate = annual_risk_free_rate / days_in_year
# apply slippage per trade to profit_ratio # apply slippage per trade to profit_ratio
results.loc[:, 'profit_ratio_after_slippage'] = \ results.loc[:, "profit_ratio_after_slippage"] = (
results['profit_ratio'] - slippage_per_trade_ratio results["profit_ratio"] - slippage_per_trade_ratio
)
# create the index within the min_date and end max_date # create the index within the min_date and end max_date
t_index = date_range(start=min_date, end=max_date, freq=resample_freq, t_index = date_range(start=min_date, end=max_date, freq=resample_freq, normalize=True)
normalize=True)
sum_daily = ( sum_daily = (
results.resample(resample_freq, on='close_date').agg( results.resample(resample_freq, on="close_date")
{"profit_ratio_after_slippage": 'sum'}).reindex(t_index).fillna(0) .agg({"profit_ratio_after_slippage": "sum"})
.reindex(t_index)
.fillna(0)
) )
total_profit = sum_daily["profit_ratio_after_slippage"] - risk_free_rate total_profit = sum_daily["profit_ratio_after_slippage"] - risk_free_rate
@@ -55,7 +63,7 @@ class SharpeHyperOptLossDaily(IHyperOptLoss):
sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(days_in_year) sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(days_in_year)
else: else:
# Define high (negative) sharpe ratio to be clear that this is NOT optimal. # Define high (negative) sharpe ratio to be clear that this is NOT optimal.
sharp_ratio = -20. sharp_ratio = -20.0
# print(t_index, sum_daily, total_profit) # print(t_index, sum_daily, total_profit)
# print(risk_free_rate, expected_returns_mean, up_stdev, sharp_ratio) # print(risk_free_rate, expected_returns_mean, up_stdev, sharp_ratio)
@@ -3,6 +3,7 @@ ShortTradeDurHyperOptLoss
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
@@ -32,8 +33,7 @@ class ShortTradeDurHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(results: DataFrame, trade_count: int, *args, **kwargs) -> float:
*args, **kwargs) -> float:
""" """
Objective function, returns smaller number for better results Objective function, returns smaller number for better results
This is the Default algorithm This is the Default algorithm
@@ -42,10 +42,10 @@ class ShortTradeDurHyperOptLoss(IHyperOptLoss):
* 0.25: Avoiding trade loss * 0.25: Avoiding trade loss
* 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above * 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above
""" """
total_profit = results['profit_ratio'].sum() total_profit = results["profit_ratio"].sum()
trade_duration = results['trade_duration'].mean() trade_duration = results["trade_duration"].mean()
trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8) trade_loss = 1 - 0.25 * exp(-((trade_count - TARGET_TRADES) ** 2) / 10**5.8)
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT) profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)
duration_loss = 0.4 * min(trade_duration / MAX_ACCEPTED_TRADE_DURATION, 1) duration_loss = 0.4 * min(trade_duration / MAX_ACCEPTED_TRADE_DURATION, 1)
result = trade_loss + profit_loss + duration_loss result = trade_loss + profit_loss + duration_loss
@@ -4,6 +4,7 @@ SortinoHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
from datetime import datetime from datetime import datetime
from pandas import DataFrame from pandas import DataFrame
@@ -21,15 +22,21 @@ class SortinoHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(
min_date: datetime, max_date: datetime, results: DataFrame,
config: Config, *args, **kwargs) -> float: trade_count: int,
min_date: datetime,
max_date: datetime,
config: Config,
*args,
**kwargs,
) -> float:
""" """
Objective function, returns smaller number for more optimal results. Objective function, returns smaller number for more optimal results.
Uses Sortino Ratio calculation. Uses Sortino Ratio calculation.
""" """
starting_balance = config['dry_run_wallet'] starting_balance = config["dry_run_wallet"]
sortino_ratio = calculate_sortino(results, min_date, max_date, starting_balance) sortino_ratio = calculate_sortino(results, min_date, max_date, starting_balance)
# print(expected_returns_mean, down_stdev, sortino_ratio) # print(expected_returns_mean, down_stdev, sortino_ratio)
return -sortino_ratio return -sortino_ratio
@@ -4,6 +4,7 @@ SortinoHyperOptLossDaily
This module defines the alternative HyperOptLoss class which can be used for This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
import math import math
from datetime import datetime from datetime import datetime
@@ -20,9 +21,14 @@ class SortinoHyperOptLossDaily(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(
min_date: datetime, max_date: datetime, results: DataFrame,
*args, **kwargs) -> float: trade_count: int,
min_date: datetime,
max_date: datetime,
*args,
**kwargs,
) -> float:
""" """
Objective function, returns smaller number for more optimal results. Objective function, returns smaller number for more optimal results.
@@ -31,30 +37,32 @@ class SortinoHyperOptLossDaily(IHyperOptLoss):
Sortino Ratio calculated as described in Sortino Ratio calculated as described in
http://www.redrockcapital.com/Sortino__A__Sharper__Ratio_Red_Rock_Capital.pdf http://www.redrockcapital.com/Sortino__A__Sharper__Ratio_Red_Rock_Capital.pdf
""" """
resample_freq = '1D' resample_freq = "1D"
slippage_per_trade_ratio = 0.0005 slippage_per_trade_ratio = 0.0005
days_in_year = 365 days_in_year = 365
minimum_acceptable_return = 0.0 minimum_acceptable_return = 0.0
# apply slippage per trade to profit_ratio # apply slippage per trade to profit_ratio
results.loc[:, 'profit_ratio_after_slippage'] = \ results.loc[:, "profit_ratio_after_slippage"] = (
results['profit_ratio'] - slippage_per_trade_ratio results["profit_ratio"] - slippage_per_trade_ratio
)
# create the index within the min_date and end max_date # create the index within the min_date and end max_date
t_index = date_range(start=min_date, end=max_date, freq=resample_freq, t_index = date_range(start=min_date, end=max_date, freq=resample_freq, normalize=True)
normalize=True)
sum_daily = ( sum_daily = (
results.resample(resample_freq, on='close_date').agg( results.resample(resample_freq, on="close_date")
{"profit_ratio_after_slippage": 'sum'}).reindex(t_index).fillna(0) .agg({"profit_ratio_after_slippage": "sum"})
.reindex(t_index)
.fillna(0)
) )
total_profit = sum_daily["profit_ratio_after_slippage"] - minimum_acceptable_return total_profit = sum_daily["profit_ratio_after_slippage"] - minimum_acceptable_return
expected_returns_mean = total_profit.mean() expected_returns_mean = total_profit.mean()
sum_daily['downside_returns'] = 0.0 sum_daily["downside_returns"] = 0.0
sum_daily.loc[total_profit < 0, 'downside_returns'] = total_profit sum_daily.loc[total_profit < 0, "downside_returns"] = total_profit
total_downside = sum_daily['downside_returns'] total_downside = sum_daily["downside_returns"]
# Here total_downside contains min(0, P - MAR) values, # Here total_downside contains min(0, P - MAR) values,
# where P = sum_daily["profit_ratio_after_slippage"] # where P = sum_daily["profit_ratio_after_slippage"]
down_stdev = math.sqrt((total_downside**2).sum() / len(total_downside)) down_stdev = math.sqrt((total_downside**2).sum() / len(total_downside))
@@ -63,7 +71,7 @@ class SortinoHyperOptLossDaily(IHyperOptLoss):
sortino_ratio = expected_returns_mean / down_stdev * math.sqrt(days_in_year) sortino_ratio = expected_returns_mean / down_stdev * math.sqrt(days_in_year)
else: else:
# Define high (negative) sortino ratio to be clear that this is NOT optimal. # Define high (negative) sortino ratio to be clear that this is NOT optimal.
sortino_ratio = -20. sortino_ratio = -20.0
# print(t_index, sum_daily, total_profit) # print(t_index, sum_daily, total_profit)
# print(minimum_acceptable_return, expected_returns_mean, down_stdev, sortino_ratio) # print(minimum_acceptable_return, expected_returns_mean, down_stdev, sortino_ratio)