2020-01-26 12:08:58 +00:00
|
|
|
import logging
|
|
|
|
from operator import itemgetter
|
|
|
|
from typing import Any, Dict, List
|
|
|
|
|
|
|
|
from colorama import init as colorama_init
|
|
|
|
|
2020-01-26 12:55:48 +00:00
|
|
|
from freqtrade.configuration import setup_utils_configuration
|
2020-09-28 18:21:55 +00:00
|
|
|
from freqtrade.data.btanalysis import get_latest_hyperopt_file
|
2021-06-08 19:20:35 +00:00
|
|
|
from freqtrade.enums import RunMode
|
2020-01-26 12:08:58 +00:00
|
|
|
from freqtrade.exceptions import OperationalException
|
2021-05-01 11:32:34 +00:00
|
|
|
from freqtrade.optimize.optimize_reports import show_backtest_result
|
2020-01-26 12:08:58 +00:00
|
|
|
|
2020-09-28 17:39:41 +00:00
|
|
|
|
2020-01-26 12:08:58 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2020-08-11 18:37:01 +00:00
|
|
|
|
2020-01-26 12:08:58 +00:00
|
|
|
def start_hyperopt_list(args: Dict[str, Any]) -> None:
|
|
|
|
"""
|
|
|
|
List hyperopt epochs previously evaluated
|
|
|
|
"""
|
2021-03-17 19:43:51 +00:00
|
|
|
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
2020-01-26 12:08:58 +00:00
|
|
|
|
|
|
|
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
|
|
|
|
|
|
|
print_colorized = config.get('print_colorized', False)
|
|
|
|
print_json = config.get('print_json', False)
|
2020-03-05 00:58:33 +00:00
|
|
|
export_csv = config.get('export_csv', None)
|
2020-01-26 12:08:58 +00:00
|
|
|
no_details = config.get('hyperopt_list_no_details', False)
|
|
|
|
no_header = False
|
|
|
|
|
2020-02-08 22:21:42 +00:00
|
|
|
filteroptions = {
|
|
|
|
'only_best': config.get('hyperopt_list_best', False),
|
|
|
|
'only_profitable': config.get('hyperopt_list_profitable', False),
|
2020-02-11 15:02:08 +00:00
|
|
|
'filter_min_trades': config.get('hyperopt_list_min_trades', 0),
|
|
|
|
'filter_max_trades': config.get('hyperopt_list_max_trades', 0),
|
2020-02-10 19:54:31 +00:00
|
|
|
'filter_min_avg_time': config.get('hyperopt_list_min_avg_time', None),
|
|
|
|
'filter_max_avg_time': config.get('hyperopt_list_max_avg_time', None),
|
|
|
|
'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None),
|
2020-02-11 20:29:55 +00:00
|
|
|
'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None),
|
|
|
|
'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None),
|
2020-03-22 01:22:06 +00:00
|
|
|
'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None),
|
|
|
|
'filter_min_objective': config.get('hyperopt_list_min_objective', None),
|
2020-03-27 02:01:51 +00:00
|
|
|
'filter_max_objective': config.get('hyperopt_list_max_objective', None),
|
2020-02-08 22:21:42 +00:00
|
|
|
}
|
2020-02-08 23:16:11 +00:00
|
|
|
|
2020-09-27 15:00:23 +00:00
|
|
|
results_file = get_latest_hyperopt_file(
|
|
|
|
config['user_data_dir'] / 'hyperopt_results',
|
|
|
|
config.get('hyperoptexportfilename'))
|
2020-01-26 12:08:58 +00:00
|
|
|
|
|
|
|
# Previous evaluations
|
2021-03-17 19:43:51 +00:00
|
|
|
epochs = HyperoptTools.load_previous_results(results_file)
|
2020-04-28 20:14:02 +00:00
|
|
|
total_epochs = len(epochs)
|
2020-01-26 12:08:58 +00:00
|
|
|
|
2020-08-11 18:37:01 +00:00
|
|
|
epochs = hyperopt_filter_epochs(epochs, filteroptions)
|
2020-01-26 12:08:58 +00:00
|
|
|
|
|
|
|
if print_colorized:
|
|
|
|
colorama_init(autoreset=True)
|
|
|
|
|
2020-03-05 18:43:43 +00:00
|
|
|
if not export_csv:
|
|
|
|
try:
|
2021-03-17 19:43:51 +00:00
|
|
|
print(HyperoptTools.get_result_table(config, epochs, total_epochs,
|
|
|
|
not filteroptions['only_best'],
|
|
|
|
print_colorized, 0))
|
2020-03-05 18:43:43 +00:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
print('User interrupted..')
|
2020-01-26 12:08:58 +00:00
|
|
|
|
2020-04-28 20:14:02 +00:00
|
|
|
if epochs and not no_details:
|
|
|
|
sorted_epochs = sorted(epochs, key=itemgetter('loss'))
|
|
|
|
results = sorted_epochs[0]
|
2021-06-13 09:24:24 +00:00
|
|
|
HyperoptTools.show_epoch_details(results, total_epochs, print_json, no_header)
|
2020-03-05 18:43:43 +00:00
|
|
|
|
2020-04-28 20:14:02 +00:00
|
|
|
if epochs and export_csv:
|
2021-03-17 19:43:51 +00:00
|
|
|
HyperoptTools.export_csv_file(
|
2020-04-28 20:14:02 +00:00
|
|
|
config, epochs, total_epochs, not filteroptions['only_best'], export_csv
|
2020-03-05 00:58:33 +00:00
|
|
|
)
|
2020-01-26 12:08:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def start_hyperopt_show(args: Dict[str, Any]) -> None:
|
|
|
|
"""
|
|
|
|
Show details of a hyperopt epoch previously evaluated
|
|
|
|
"""
|
2021-03-17 19:43:51 +00:00
|
|
|
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
2020-01-26 12:08:58 +00:00
|
|
|
|
|
|
|
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
|
|
|
|
2020-02-18 21:46:53 +00:00
|
|
|
print_json = config.get('print_json', False)
|
|
|
|
no_header = config.get('hyperopt_show_no_header', False)
|
2020-09-27 15:00:23 +00:00
|
|
|
results_file = get_latest_hyperopt_file(
|
|
|
|
config['user_data_dir'] / 'hyperopt_results',
|
|
|
|
config.get('hyperoptexportfilename'))
|
|
|
|
|
2020-02-18 21:46:53 +00:00
|
|
|
n = config.get('hyperopt_show_index', -1)
|
|
|
|
|
2020-02-08 22:21:42 +00:00
|
|
|
filteroptions = {
|
|
|
|
'only_best': config.get('hyperopt_list_best', False),
|
|
|
|
'only_profitable': config.get('hyperopt_list_profitable', False),
|
2020-02-11 15:02:08 +00:00
|
|
|
'filter_min_trades': config.get('hyperopt_list_min_trades', 0),
|
|
|
|
'filter_max_trades': config.get('hyperopt_list_max_trades', 0),
|
2020-02-10 19:54:31 +00:00
|
|
|
'filter_min_avg_time': config.get('hyperopt_list_min_avg_time', None),
|
|
|
|
'filter_max_avg_time': config.get('hyperopt_list_max_avg_time', None),
|
|
|
|
'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None),
|
2020-02-11 20:29:55 +00:00
|
|
|
'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None),
|
|
|
|
'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None),
|
2020-03-22 01:22:06 +00:00
|
|
|
'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None),
|
|
|
|
'filter_min_objective': config.get('hyperopt_list_min_objective', None),
|
|
|
|
'filter_max_objective': config.get('hyperopt_list_max_objective', None)
|
2020-02-08 22:21:42 +00:00
|
|
|
}
|
2020-01-26 12:08:58 +00:00
|
|
|
|
|
|
|
# Previous evaluations
|
2021-03-17 19:43:51 +00:00
|
|
|
epochs = HyperoptTools.load_previous_results(results_file)
|
2020-04-28 20:14:02 +00:00
|
|
|
total_epochs = len(epochs)
|
2020-01-26 12:08:58 +00:00
|
|
|
|
2020-08-11 18:37:01 +00:00
|
|
|
epochs = hyperopt_filter_epochs(epochs, filteroptions)
|
2020-04-28 20:14:02 +00:00
|
|
|
filtered_epochs = len(epochs)
|
2020-01-26 12:08:58 +00:00
|
|
|
|
2020-04-28 20:14:02 +00:00
|
|
|
if n > filtered_epochs:
|
2020-01-26 12:08:58 +00:00
|
|
|
raise OperationalException(
|
2020-04-28 20:14:02 +00:00
|
|
|
f"The index of the epoch to show should be less than {filtered_epochs + 1}.")
|
|
|
|
if n < -filtered_epochs:
|
2020-01-26 12:08:58 +00:00
|
|
|
raise OperationalException(
|
2020-04-28 20:14:02 +00:00
|
|
|
f"The index of the epoch to show should be greater than {-filtered_epochs - 1}.")
|
2020-01-26 12:08:58 +00:00
|
|
|
|
|
|
|
# Translate epoch index from human-readable format to pythonic
|
|
|
|
if n > 0:
|
|
|
|
n -= 1
|
|
|
|
|
2020-04-28 20:14:02 +00:00
|
|
|
if epochs:
|
|
|
|
val = epochs[n]
|
2021-05-01 11:32:34 +00:00
|
|
|
|
|
|
|
metrics = val['results_metrics']
|
|
|
|
if 'strategy_name' in metrics:
|
2021-05-29 14:49:28 +00:00
|
|
|
strategy_name = metrics['strategy_name']
|
|
|
|
show_backtest_result(strategy_name, metrics,
|
2021-05-01 11:32:34 +00:00
|
|
|
metrics['stake_currency'])
|
|
|
|
|
2021-05-29 14:49:28 +00:00
|
|
|
# Export parameters ...
|
|
|
|
# TODO: make this optional? otherwise it'll overwrite previous parameters ...
|
|
|
|
fn = HyperoptTools.get_strategy_filename(config, strategy_name)
|
2021-05-29 14:56:36 +00:00
|
|
|
if fn:
|
|
|
|
HyperoptTools.export_params(val, strategy_name, fn.with_suffix('.json'))
|
|
|
|
else:
|
|
|
|
logger.warn("Strategy not found, not exporting parameter file.")
|
2021-05-29 14:49:28 +00:00
|
|
|
|
2021-06-13 09:24:24 +00:00
|
|
|
HyperoptTools.show_epoch_details(val, total_epochs, print_json, no_header,
|
|
|
|
header_str="Epoch details")
|
2020-01-26 12:08:58 +00:00
|
|
|
|
|
|
|
|
2020-08-11 18:37:01 +00:00
|
|
|
def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List:
|
2020-01-26 12:08:58 +00:00
|
|
|
"""
|
|
|
|
Filter our items from the list of hyperopt results
|
2021-05-02 18:07:22 +00:00
|
|
|
TODO: after 2021.5 remove all "legacy" mode queries.
|
2020-01-26 12:08:58 +00:00
|
|
|
"""
|
2020-02-08 22:21:42 +00:00
|
|
|
if filteroptions['only_best']:
|
2020-04-28 20:14:02 +00:00
|
|
|
epochs = [x for x in epochs if x['is_best']]
|
2020-02-08 22:21:42 +00:00
|
|
|
if filteroptions['only_profitable']:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = [x for x in epochs if x['results_metrics'].get(
|
|
|
|
'profit', x['results_metrics'].get('profit_total', 0)) > 0]
|
2020-08-11 18:37:01 +00:00
|
|
|
|
|
|
|
epochs = _hyperopt_filter_epochs_trade_count(epochs, filteroptions)
|
|
|
|
|
|
|
|
epochs = _hyperopt_filter_epochs_duration(epochs, filteroptions)
|
|
|
|
|
|
|
|
epochs = _hyperopt_filter_epochs_profit(epochs, filteroptions)
|
|
|
|
|
|
|
|
epochs = _hyperopt_filter_epochs_objective(epochs, filteroptions)
|
|
|
|
|
2020-08-12 08:39:53 +00:00
|
|
|
logger.info(f"{len(epochs)} " +
|
|
|
|
("best " if filteroptions['only_best'] else "") +
|
|
|
|
("profitable " if filteroptions['only_profitable'] else "") +
|
|
|
|
"epochs found.")
|
2020-08-11 18:37:01 +00:00
|
|
|
return epochs
|
|
|
|
|
|
|
|
|
2021-05-02 18:07:22 +00:00
|
|
|
def _hyperopt_filter_epochs_trade(epochs: List, trade_count: int):
|
|
|
|
"""
|
|
|
|
Filter epochs with trade-counts > trades
|
|
|
|
"""
|
|
|
|
return [
|
|
|
|
x for x in epochs
|
|
|
|
if x['results_metrics'].get(
|
|
|
|
'trade_count', x['results_metrics'].get('total_trades', 0)
|
|
|
|
) > trade_count
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2020-08-11 18:37:01 +00:00
|
|
|
def _hyperopt_filter_epochs_trade_count(epochs: List, filteroptions: dict) -> List:
|
|
|
|
|
2020-02-11 15:02:08 +00:00
|
|
|
if filteroptions['filter_min_trades'] > 0:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, filteroptions['filter_min_trades'])
|
|
|
|
|
2020-02-11 15:02:08 +00:00
|
|
|
if filteroptions['filter_max_trades'] > 0:
|
2020-04-28 20:14:02 +00:00
|
|
|
epochs = [
|
|
|
|
x for x in epochs
|
2021-05-02 18:07:22 +00:00
|
|
|
if x['results_metrics'].get(
|
|
|
|
'trade_count', x['results_metrics'].get('total_trades')
|
|
|
|
) < filteroptions['filter_max_trades']
|
2020-03-01 23:14:01 +00:00
|
|
|
]
|
2020-08-11 18:37:01 +00:00
|
|
|
return epochs
|
|
|
|
|
|
|
|
|
|
|
|
def _hyperopt_filter_epochs_duration(epochs: List, filteroptions: dict) -> List:
|
|
|
|
|
2021-05-02 18:07:22 +00:00
|
|
|
def get_duration_value(x):
|
|
|
|
# Duration in minutes ...
|
|
|
|
if 'duration' in x['results_metrics']:
|
|
|
|
return x['results_metrics']['duration']
|
|
|
|
else:
|
|
|
|
# New mode
|
2021-05-31 18:00:47 +00:00
|
|
|
if 'holding_avg_s' in x['results_metrics']:
|
|
|
|
avg = x['results_metrics']['holding_avg_s']
|
|
|
|
return avg // 60
|
|
|
|
raise OperationalException(
|
|
|
|
"Holding-average not available. Please omit the filter on average time, "
|
|
|
|
"or rerun hyperopt with this version")
|
2021-05-02 18:07:22 +00:00
|
|
|
|
2020-02-10 19:54:31 +00:00
|
|
|
if filteroptions['filter_min_avg_time'] is not None:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, 0)
|
2020-04-28 20:14:02 +00:00
|
|
|
epochs = [
|
|
|
|
x for x in epochs
|
2021-05-02 18:07:22 +00:00
|
|
|
if get_duration_value(x) > filteroptions['filter_min_avg_time']
|
2020-03-01 23:14:01 +00:00
|
|
|
]
|
2020-02-10 19:54:31 +00:00
|
|
|
if filteroptions['filter_max_avg_time'] is not None:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, 0)
|
2020-04-28 20:14:02 +00:00
|
|
|
epochs = [
|
|
|
|
x for x in epochs
|
2021-05-02 18:07:22 +00:00
|
|
|
if get_duration_value(x) < filteroptions['filter_max_avg_time']
|
2020-03-01 23:14:01 +00:00
|
|
|
]
|
2020-08-11 18:37:01 +00:00
|
|
|
|
|
|
|
return epochs
|
|
|
|
|
|
|
|
|
|
|
|
def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List:
|
|
|
|
|
2020-02-10 19:54:31 +00:00
|
|
|
if filteroptions['filter_min_avg_profit'] is not None:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, 0)
|
2020-04-28 20:14:02 +00:00
|
|
|
epochs = [
|
|
|
|
x for x in epochs
|
2021-05-02 18:07:22 +00:00
|
|
|
if x['results_metrics'].get(
|
2021-05-02 18:41:45 +00:00
|
|
|
'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100
|
2021-05-02 18:07:22 +00:00
|
|
|
) > filteroptions['filter_min_avg_profit']
|
2020-03-01 23:14:01 +00:00
|
|
|
]
|
2020-02-11 20:29:55 +00:00
|
|
|
if filteroptions['filter_max_avg_profit'] is not None:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, 0)
|
2020-04-28 20:14:02 +00:00
|
|
|
epochs = [
|
|
|
|
x for x in epochs
|
2021-05-02 18:07:22 +00:00
|
|
|
if x['results_metrics'].get(
|
2021-05-02 18:41:45 +00:00
|
|
|
'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100
|
2021-05-02 18:07:22 +00:00
|
|
|
) < filteroptions['filter_max_avg_profit']
|
2020-03-01 23:14:01 +00:00
|
|
|
]
|
2020-02-10 19:54:31 +00:00
|
|
|
if filteroptions['filter_min_total_profit'] is not None:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, 0)
|
2020-04-28 20:14:02 +00:00
|
|
|
epochs = [
|
|
|
|
x for x in epochs
|
2021-05-02 18:07:22 +00:00
|
|
|
if x['results_metrics'].get(
|
2021-05-02 18:41:45 +00:00
|
|
|
'profit', x['results_metrics'].get('profit_total_abs', 0)
|
2021-05-02 18:07:22 +00:00
|
|
|
) > filteroptions['filter_min_total_profit']
|
2020-03-01 23:14:01 +00:00
|
|
|
]
|
2020-02-11 20:29:55 +00:00
|
|
|
if filteroptions['filter_max_total_profit'] is not None:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, 0)
|
2020-04-28 20:14:02 +00:00
|
|
|
epochs = [
|
|
|
|
x for x in epochs
|
2021-05-02 18:07:22 +00:00
|
|
|
if x['results_metrics'].get(
|
2021-05-02 18:41:45 +00:00
|
|
|
'profit', x['results_metrics'].get('profit_total_abs', 0)
|
2021-05-02 18:07:22 +00:00
|
|
|
) < filteroptions['filter_max_total_profit']
|
2020-03-01 23:14:01 +00:00
|
|
|
]
|
2020-08-11 18:37:01 +00:00
|
|
|
return epochs
|
|
|
|
|
|
|
|
|
|
|
|
def _hyperopt_filter_epochs_objective(epochs: List, filteroptions: dict) -> List:
|
|
|
|
|
2020-03-22 01:22:06 +00:00
|
|
|
if filteroptions['filter_min_objective'] is not None:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, 0)
|
2020-08-12 08:39:53 +00:00
|
|
|
|
|
|
|
epochs = [x for x in epochs if x['loss'] < filteroptions['filter_min_objective']]
|
2020-03-22 01:22:06 +00:00
|
|
|
if filteroptions['filter_max_objective'] is not None:
|
2021-05-02 18:07:22 +00:00
|
|
|
epochs = _hyperopt_filter_epochs_trade(epochs, 0)
|
2020-02-08 23:16:11 +00:00
|
|
|
|
2020-08-12 08:39:53 +00:00
|
|
|
epochs = [x for x in epochs if x['loss'] > filteroptions['filter_max_objective']]
|
2020-01-26 12:08:58 +00:00
|
|
|
|
2020-04-28 20:14:02 +00:00
|
|
|
return epochs
|