Merge branch 'develop' into pr/theluxaz/5710

This commit is contained in:
Matthias
2021-11-03 19:43:36 +01:00
52 changed files with 649 additions and 270 deletions

View File

@@ -315,7 +315,9 @@ class Backtesting:
# Worst case: price ticks tiny bit above open and dives down.
stop_rate = sell_row[OPEN_IDX] * (1 - abs(trade.stop_loss_pct))
assert stop_rate < sell_row[HIGH_IDX]
return stop_rate
# Limit lower-end to candle low to avoid sells below the low.
# This still remains "worst case" - but "worst realistic case".
return max(sell_row[LOW_IDX], stop_rate)
# Set close_rate to stoploss
return trade.stop_loss

View 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

View File

@@ -1,4 +1,3 @@
import io
import logging
from copy import deepcopy
@@ -64,10 +63,11 @@ class HyperoptTools():
'export_time': datetime.now(timezone.utc),
}
logger.info(f"Dumping parameters to {filename}")
rapidjson.dump(final_params, filename.open('w'), indent=2,
default=hyperopt_serializer,
number_mode=rapidjson.NM_NATIVE | rapidjson.NM_NAN
)
with filename.open('w') as f:
rapidjson.dump(final_params, f, indent=2,
default=hyperopt_serializer,
number_mode=rapidjson.NM_NATIVE | rapidjson.NM_NAN
)
@staticmethod
def try_export_params(config: Dict[str, Any], strategy_name: str, params: Dict):

View File

@@ -856,3 +856,13 @@ def show_backtest_results(config: Dict, backtest_stats: Dict):
print(table)
print('=' * len(table.splitlines()[0]))
print('\nFor more details, please look at the detail tables above')
def show_sorted_pairlist(config: Dict, backtest_stats: Dict):
if config.get('backtest_show_pair_list', False):
for strategy, results in backtest_stats['strategy'].items():
print(f"Pairs for Strategy {strategy}: \n[")
for result in results['results_per_pair']:
if result["key"] != 'TOTAL':
print(f'"{result["key"]}", // {round(result["profit_mean_pct"], 2)}%')
print("]")

View File

@@ -7,11 +7,15 @@ class SKDecimal(Integer):
def __init__(self, low, high, decimals=3, prior="uniform", base=10, transform=None,
name=None, dtype=np.int64):
self.decimals = decimals
_low = int(low * pow(10, self.decimals))
_high = int(high * pow(10, self.decimals))
self.pow_dot_one = pow(0.1, self.decimals)
self.pow_ten = pow(10, self.decimals)
_low = int(low * self.pow_ten)
_high = int(high * self.pow_ten)
# trunc to precision to avoid points out of space
self.low_orig = round(_low * pow(0.1, self.decimals), self.decimals)
self.high_orig = round(_high * pow(0.1, self.decimals), self.decimals)
self.low_orig = round(_low * self.pow_dot_one, self.decimals)
self.high_orig = round(_high * self.pow_dot_one, self.decimals)
super().__init__(_low, _high, prior, base, transform, name, dtype)
@@ -25,9 +29,9 @@ class SKDecimal(Integer):
return self.low_orig <= point <= self.high_orig
def transform(self, Xt):
aa = [int(x * pow(10, self.decimals)) for x in Xt]
return super().transform(aa)
return super().transform([int(v * self.pow_ten) for v in Xt])
def inverse_transform(self, Xt):
res = super().inverse_transform(Xt)
return [round(x * pow(0.1, self.decimals), self.decimals) for x in res]
# equivalent to [round(x * pow(0.1, self.decimals), self.decimals) for x in res]
return [int(v) / self.pow_ten for v in res]