Merge pull request #5607 from TreborNamor/develop
a new hyperopt loss created that uses calmar ratio
This commit is contained in:
commit
201fe108bc
@ -116,7 +116,7 @@ optional arguments:
|
|||||||
ShortTradeDurHyperOptLoss, OnlyProfitHyperOptLoss,
|
ShortTradeDurHyperOptLoss, OnlyProfitHyperOptLoss,
|
||||||
SharpeHyperOptLoss, SharpeHyperOptLossDaily,
|
SharpeHyperOptLoss, SharpeHyperOptLossDaily,
|
||||||
SortinoHyperOptLoss, SortinoHyperOptLossDaily,
|
SortinoHyperOptLoss, SortinoHyperOptLossDaily,
|
||||||
MaxDrawDownHyperOptLoss
|
CalmarHyperOptLoss, MaxDrawDownHyperOptLoss
|
||||||
--disable-param-export
|
--disable-param-export
|
||||||
Disable automatic hyperopt parameter export.
|
Disable automatic hyperopt parameter export.
|
||||||
--ignore-missing-spaces, --ignore-unparameterized-spaces
|
--ignore-missing-spaces, --ignore-unparameterized-spaces
|
||||||
@ -524,6 +524,7 @@ Currently, the following loss functions are builtin:
|
|||||||
* `SortinoHyperOptLoss` - optimizes Sortino Ratio calculated on trade returns relative to **downside** standard deviation.
|
* `SortinoHyperOptLoss` - optimizes Sortino Ratio calculated on trade returns relative to **downside** standard deviation.
|
||||||
* `SortinoHyperOptLossDaily` - optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation.
|
* `SortinoHyperOptLossDaily` - optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation.
|
||||||
* `MaxDrawDownHyperOptLoss` - Optimizes Maximum drawdown.
|
* `MaxDrawDownHyperOptLoss` - Optimizes Maximum drawdown.
|
||||||
|
* `CalmarHyperOptLoss` - Optimizes Calmar Ratio calculated on trade returns relative to max drawdown.
|
||||||
|
|
||||||
Creation of a custom loss function is covered in the [Advanced Hyperopt](advanced-hyperopt.md) part of the documentation.
|
Creation of a custom loss function is covered in the [Advanced Hyperopt](advanced-hyperopt.md) part of the documentation.
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
|
|||||||
HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss',
|
HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss',
|
||||||
'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily',
|
'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily',
|
||||||
'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily',
|
'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily',
|
||||||
|
'CalmarHyperOptLoss',
|
||||||
'MaxDrawDownHyperOptLoss']
|
'MaxDrawDownHyperOptLoss']
|
||||||
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
|
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
|
||||||
'AgeFilter', 'OffsetFilter', 'PerformanceFilter',
|
'AgeFilter', 'OffsetFilter', 'PerformanceFilter',
|
||||||
@ -53,7 +54,6 @@ ENV_VAR_PREFIX = 'FREQTRADE__'
|
|||||||
|
|
||||||
NON_OPEN_EXCHANGE_STATES = ('cancelled', 'canceled', 'closed', 'expired')
|
NON_OPEN_EXCHANGE_STATES = ('cancelled', 'canceled', 'closed', 'expired')
|
||||||
|
|
||||||
|
|
||||||
# Define decimals per coin for outputs
|
# Define decimals per coin for outputs
|
||||||
# Only used for outputs.
|
# Only used for outputs.
|
||||||
DECIMAL_PER_COIN_FALLBACK = 3 # Should be low to avoid listing all possible FIAT's
|
DECIMAL_PER_COIN_FALLBACK = 3 # Should be low to avoid listing all possible FIAT's
|
||||||
@ -67,7 +67,6 @@ DUST_PER_COIN = {
|
|||||||
'ETH': 0.01
|
'ETH': 0.01
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Source files with destination directories within user-directory
|
# Source files with destination directories within user-directory
|
||||||
USER_DATA_FILES = {
|
USER_DATA_FILES = {
|
||||||
'sample_strategy.py': USERPATH_STRATEGIES,
|
'sample_strategy.py': USERPATH_STRATEGIES,
|
||||||
|
64
freqtrade/optimize/hyperopt_loss_calmar.py
Normal file
64
freqtrade/optimize/hyperopt_loss_calmar.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""
|
||||||
|
CalmarHyperOptLoss
|
||||||
|
|
||||||
|
This module defines the alternative HyperOptLoss class which can be used for
|
||||||
|
Hyperoptimization.
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
from math import sqrt as msqrt
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
from freqtrade.data.btanalysis import calculate_max_drawdown
|
||||||
|
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
||||||
|
|
||||||
|
|
||||||
|
class CalmarHyperOptLoss(IHyperOptLoss):
|
||||||
|
"""
|
||||||
|
Defines the loss function for hyperopt.
|
||||||
|
|
||||||
|
This implementation uses the Calmar Ratio calculation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def hyperopt_loss_function(
|
||||||
|
results: DataFrame,
|
||||||
|
trade_count: int,
|
||||||
|
min_date: datetime,
|
||||||
|
max_date: datetime,
|
||||||
|
config: Dict,
|
||||||
|
processed: Dict[str, DataFrame],
|
||||||
|
backtest_stats: Dict[str, Any],
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
) -> float:
|
||||||
|
"""
|
||||||
|
Objective function, returns smaller number for more optimal results.
|
||||||
|
|
||||||
|
Uses Calmar Ratio calculation.
|
||||||
|
"""
|
||||||
|
total_profit = backtest_stats["profit_total"]
|
||||||
|
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 * 100
|
||||||
|
|
||||||
|
# calculate max drawdown
|
||||||
|
try:
|
||||||
|
_, _, _, high_val, low_val = calculate_max_drawdown(
|
||||||
|
results, value_col="profit_abs"
|
||||||
|
)
|
||||||
|
max_drawdown = (high_val - low_val) / high_val
|
||||||
|
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)
|
||||||
|
return -calmar_ratio
|
@ -85,6 +85,8 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) ->
|
|||||||
"SharpeHyperOptLoss",
|
"SharpeHyperOptLoss",
|
||||||
"SharpeHyperOptLossDaily",
|
"SharpeHyperOptLossDaily",
|
||||||
"MaxDrawDownHyperOptLoss",
|
"MaxDrawDownHyperOptLoss",
|
||||||
|
"CalmarHyperOptLoss",
|
||||||
|
|
||||||
])
|
])
|
||||||
def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None:
|
def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None:
|
||||||
results_over = hyperopt_results.copy()
|
results_over = hyperopt_results.copy()
|
||||||
@ -96,11 +98,32 @@ def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunct
|
|||||||
|
|
||||||
default_conf.update({'hyperopt_loss': lossfunction})
|
default_conf.update({'hyperopt_loss': lossfunction})
|
||||||
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
|
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
|
||||||
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
|
correct = hl.hyperopt_loss_function(
|
||||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
hyperopt_results,
|
||||||
over = hl.hyperopt_loss_function(results_over, len(results_over),
|
trade_count=len(hyperopt_results),
|
||||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
min_date=datetime(2019, 1, 1),
|
||||||
under = hl.hyperopt_loss_function(results_under, len(results_under),
|
max_date=datetime(2019, 5, 1),
|
||||||
datetime(2019, 1, 1), datetime(2019, 5, 1))
|
config=default_conf,
|
||||||
|
processed=None,
|
||||||
|
backtest_stats={'profit_total': hyperopt_results['profit_abs'].sum()}
|
||||||
|
)
|
||||||
|
over = hl.hyperopt_loss_function(
|
||||||
|
results_over,
|
||||||
|
trade_count=len(results_over),
|
||||||
|
min_date=datetime(2019, 1, 1),
|
||||||
|
max_date=datetime(2019, 5, 1),
|
||||||
|
config=default_conf,
|
||||||
|
processed=None,
|
||||||
|
backtest_stats={'profit_total': results_over['profit_abs'].sum()}
|
||||||
|
)
|
||||||
|
under = hl.hyperopt_loss_function(
|
||||||
|
results_under,
|
||||||
|
trade_count=len(results_under),
|
||||||
|
min_date=datetime(2019, 1, 1),
|
||||||
|
max_date=datetime(2019, 5, 1),
|
||||||
|
config=default_conf,
|
||||||
|
processed=None,
|
||||||
|
backtest_stats={'profit_total': results_under['profit_abs'].sum()}
|
||||||
|
)
|
||||||
assert over < correct
|
assert over < correct
|
||||||
assert under > correct
|
assert under > correct
|
||||||
|
Loading…
Reference in New Issue
Block a user