Merge pull request #7987 from stash86/bt-metrics

update calmar, sharpe, and sortino hyperopt losses to use latest formula
This commit is contained in:
Matthias 2023-01-08 10:37:24 +01:00 committed by GitHub
commit 8d4f7341c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 18 additions and 72 deletions

View File

@ -5,13 +5,11 @@ This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization. Hyperoptimization.
""" """
from datetime import datetime from datetime import datetime
from math import sqrt as msqrt
from typing import Any, Dict
from pandas import DataFrame from pandas import DataFrame
from freqtrade.constants import Config from freqtrade.constants import Config
from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.data.metrics import calculate_calmar
from freqtrade.optimize.hyperopt import IHyperOptLoss from freqtrade.optimize.hyperopt import IHyperOptLoss
@ -23,42 +21,15 @@ class CalmarHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function( def hyperopt_loss_function(results: DataFrame, trade_count: int,
results: DataFrame, min_date: datetime, max_date: datetime,
trade_count: int, config: Config, *args, **kwargs) -> float:
min_date: datetime,
max_date: datetime,
config: Config,
processed: Dict[str, DataFrame],
backtest_stats: Dict[str, Any],
*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.
""" """
total_profit = backtest_stats["profit_total"] starting_balance = config['dry_run_wallet']
days_period = (max_date - min_date).days calmar_ratio = calculate_calmar(results, min_date, max_date, starting_balance)
# adding slippage of 0.1% per trade
total_profit = total_profit - 0.0005
expected_returns_mean = total_profit.sum() / days_period * 100
# calculate max drawdown
try:
_, _, _, _, _, max_drawdown = calculate_max_drawdown(
results, value_col="profit_abs"
)
except ValueError:
max_drawdown = 0
if max_drawdown != 0:
calmar_ratio = expected_returns_mean / max_drawdown * msqrt(365)
else:
# Define high (negative) calmar ratio to be clear that this is NOT optimal.
calmar_ratio = -20.0
# print(expected_returns_mean, max_drawdown, calmar_ratio) # print(expected_returns_mean, max_drawdown, calmar_ratio)
return -calmar_ratio return -calmar_ratio

View File

@ -6,9 +6,10 @@ Hyperoptimization.
""" """
from datetime import datetime from datetime import datetime
import numpy as np
from pandas import DataFrame from pandas import DataFrame
from freqtrade.constants import Config
from freqtrade.data.metrics import calculate_sharpe
from freqtrade.optimize.hyperopt import IHyperOptLoss from freqtrade.optimize.hyperopt import IHyperOptLoss
@ -22,25 +23,13 @@ class SharpeHyperOptLoss(IHyperOptLoss):
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(results: DataFrame, trade_count: int,
min_date: datetime, max_date: datetime, min_date: datetime, max_date: datetime,
*args, **kwargs) -> float: 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.
""" """
total_profit = results["profit_ratio"] starting_balance = config['dry_run_wallet']
days_period = (max_date - min_date).days sharp_ratio = calculate_sharpe(results, min_date, max_date, starting_balance)
# adding slippage of 0.1% per trade
total_profit = total_profit - 0.0005
expected_returns_mean = total_profit.sum() / days_period
up_stdev = np.std(total_profit)
if up_stdev != 0:
sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365)
else:
# Define high (negative) sharpe ratio to be clear that this is NOT optimal.
sharp_ratio = -20.
# print(expected_returns_mean, up_stdev, sharp_ratio) # print(expected_returns_mean, up_stdev, sharp_ratio)
return -sharp_ratio return -sharp_ratio

View File

@ -6,9 +6,10 @@ Hyperoptimization.
""" """
from datetime import datetime from datetime import datetime
import numpy as np
from pandas import DataFrame from pandas import DataFrame
from freqtrade.constants import Config
from freqtrade.data.metrics import calculate_sortino
from freqtrade.optimize.hyperopt import IHyperOptLoss from freqtrade.optimize.hyperopt import IHyperOptLoss
@ -22,28 +23,13 @@ class SortinoHyperOptLoss(IHyperOptLoss):
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(results: DataFrame, trade_count: int,
min_date: datetime, max_date: datetime, min_date: datetime, max_date: datetime,
*args, **kwargs) -> float: 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.
""" """
total_profit = results["profit_ratio"] starting_balance = config['dry_run_wallet']
days_period = (max_date - min_date).days sortino_ratio = calculate_sortino(results, min_date, max_date, starting_balance)
# 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_ratio']
down_stdev = np.std(results['downside_returns'])
if down_stdev != 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) # print(expected_returns_mean, down_stdev, sortino_ratio)
return -sortino_ratio return -sortino_ratio

View File

@ -48,8 +48,8 @@ def hyperopt_results():
return pd.DataFrame( return pd.DataFrame(
{ {
'pair': ['ETH/USDT', 'ETH/USDT', 'ETH/USDT', 'ETH/USDT'], 'pair': ['ETH/USDT', 'ETH/USDT', 'ETH/USDT', 'ETH/USDT'],
'profit_ratio': [-0.1, 0.2, -0.1, 0.3], 'profit_ratio': [-0.1, 0.2, -0.12, 0.3],
'profit_abs': [-0.2, 0.4, -0.2, 0.6], 'profit_abs': [-0.2, 0.4, -0.21, 0.6],
'trade_duration': [10, 30, 10, 10], 'trade_duration': [10, 30, 10, 10],
'amount': [0.1, 0.1, 0.1, 0.1], 'amount': [0.1, 0.1, 0.1, 0.1],
'exit_reason': [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI], 'exit_reason': [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI],