extract result-printing from hyperopt class
This commit is contained in:
parent
b05de6d468
commit
76ca3c219f
@ -17,7 +17,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None:
|
|||||||
"""
|
"""
|
||||||
List hyperopt epochs previously evaluated
|
List hyperopt epochs previously evaluated
|
||||||
"""
|
"""
|
||||||
from freqtrade.optimize.hyperopt import Hyperopt
|
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||||
|
|
||||||
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None:
|
|||||||
config.get('hyperoptexportfilename'))
|
config.get('hyperoptexportfilename'))
|
||||||
|
|
||||||
# Previous evaluations
|
# Previous evaluations
|
||||||
epochs = Hyperopt.load_previous_results(results_file)
|
epochs = HyperoptTools.load_previous_results(results_file)
|
||||||
total_epochs = len(epochs)
|
total_epochs = len(epochs)
|
||||||
|
|
||||||
epochs = hyperopt_filter_epochs(epochs, filteroptions)
|
epochs = hyperopt_filter_epochs(epochs, filteroptions)
|
||||||
@ -57,18 +57,19 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None:
|
|||||||
|
|
||||||
if not export_csv:
|
if not export_csv:
|
||||||
try:
|
try:
|
||||||
print(Hyperopt.get_result_table(config, epochs, total_epochs,
|
print(HyperoptTools.get_result_table(config, epochs, total_epochs,
|
||||||
not filteroptions['only_best'], print_colorized, 0))
|
not filteroptions['only_best'],
|
||||||
|
print_colorized, 0))
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('User interrupted..')
|
print('User interrupted..')
|
||||||
|
|
||||||
if epochs and not no_details:
|
if epochs and not no_details:
|
||||||
sorted_epochs = sorted(epochs, key=itemgetter('loss'))
|
sorted_epochs = sorted(epochs, key=itemgetter('loss'))
|
||||||
results = sorted_epochs[0]
|
results = sorted_epochs[0]
|
||||||
Hyperopt.print_epoch_details(results, total_epochs, print_json, no_header)
|
HyperoptTools.print_epoch_details(results, total_epochs, print_json, no_header)
|
||||||
|
|
||||||
if epochs and export_csv:
|
if epochs and export_csv:
|
||||||
Hyperopt.export_csv_file(
|
HyperoptTools.export_csv_file(
|
||||||
config, epochs, total_epochs, not filteroptions['only_best'], export_csv
|
config, epochs, total_epochs, not filteroptions['only_best'], export_csv
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None:
|
|||||||
"""
|
"""
|
||||||
Show details of a hyperopt epoch previously evaluated
|
Show details of a hyperopt epoch previously evaluated
|
||||||
"""
|
"""
|
||||||
from freqtrade.optimize.hyperopt import Hyperopt
|
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||||
|
|
||||||
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Previous evaluations
|
# Previous evaluations
|
||||||
epochs = Hyperopt.load_previous_results(results_file)
|
epochs = HyperoptTools.load_previous_results(results_file)
|
||||||
total_epochs = len(epochs)
|
total_epochs = len(epochs)
|
||||||
|
|
||||||
epochs = hyperopt_filter_epochs(epochs, filteroptions)
|
epochs = hyperopt_filter_epochs(epochs, filteroptions)
|
||||||
@ -124,8 +125,8 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None:
|
|||||||
|
|
||||||
if epochs:
|
if epochs:
|
||||||
val = epochs[n]
|
val = epochs[n]
|
||||||
Hyperopt.print_epoch_details(val, total_epochs, print_json, no_header,
|
HyperoptTools.print_epoch_details(val, total_epochs, print_json, no_header,
|
||||||
header_str="Epoch details")
|
header_str="Epoch details")
|
||||||
|
|
||||||
|
|
||||||
def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List:
|
def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List:
|
||||||
|
@ -4,36 +4,31 @@
|
|||||||
This module contains the hyperopt logic
|
This module contains the hyperopt logic
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import io
|
|
||||||
import locale
|
import locale
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
import warnings
|
import warnings
|
||||||
from collections import OrderedDict
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pprint import pformat
|
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
import progressbar
|
import progressbar
|
||||||
import rapidjson
|
|
||||||
import tabulate
|
|
||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
from colorama import init as colorama_init
|
from colorama import init as colorama_init
|
||||||
from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects
|
from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects
|
||||||
from pandas import DataFrame, isna, json_normalize
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN
|
||||||
from freqtrade.data.converter import trim_dataframe
|
from freqtrade.data.converter import trim_dataframe
|
||||||
from freqtrade.data.history import get_timerange
|
from freqtrade.data.history import get_timerange
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.misc import file_dump_json, plural
|
||||||
from freqtrade.misc import file_dump_json, plural, round_dict
|
|
||||||
from freqtrade.optimize.backtesting import Backtesting
|
from freqtrade.optimize.backtesting import Backtesting
|
||||||
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
|
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
|
||||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401
|
from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401
|
||||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401
|
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401
|
||||||
|
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||||
from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver, HyperOptResolver
|
from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver, HyperOptResolver
|
||||||
from freqtrade.strategy import IStrategy
|
from freqtrade.strategy import IStrategy
|
||||||
|
|
||||||
@ -169,15 +164,6 @@ class Hyperopt:
|
|||||||
file_dump_json(latest_filename, {'latest_hyperopt': str(self.results_file.name)},
|
file_dump_json(latest_filename, {'latest_hyperopt': str(self.results_file.name)},
|
||||||
log=False)
|
log=False)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _read_results(results_file: Path) -> List:
|
|
||||||
"""
|
|
||||||
Read hyperopt results from file
|
|
||||||
"""
|
|
||||||
logger.info("Reading epochs from '%s'", results_file)
|
|
||||||
data = load(results_file)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _get_params_details(self, params: Dict) -> Dict:
|
def _get_params_details(self, params: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Return the params for each space
|
Return the params for each space
|
||||||
@ -200,102 +186,16 @@ class Hyperopt:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def print_epoch_details(results, total_epochs: int, print_json: bool,
|
|
||||||
no_header: bool = False, header_str: str = None) -> None:
|
|
||||||
"""
|
|
||||||
Display details of the hyperopt result
|
|
||||||
"""
|
|
||||||
params = results.get('params_details', {})
|
|
||||||
|
|
||||||
# Default header string
|
|
||||||
if header_str is None:
|
|
||||||
header_str = "Best result"
|
|
||||||
|
|
||||||
if not no_header:
|
|
||||||
explanation_str = Hyperopt._format_explanation_string(results, total_epochs)
|
|
||||||
print(f"\n{header_str}:\n\n{explanation_str}\n")
|
|
||||||
|
|
||||||
if print_json:
|
|
||||||
result_dict: Dict = {}
|
|
||||||
for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']:
|
|
||||||
Hyperopt._params_update_for_json(result_dict, params, s)
|
|
||||||
print(rapidjson.dumps(result_dict, default=str, number_mode=rapidjson.NM_NATIVE))
|
|
||||||
|
|
||||||
else:
|
|
||||||
Hyperopt._params_pretty_print(params, 'buy', "Buy hyperspace params:")
|
|
||||||
Hyperopt._params_pretty_print(params, 'sell', "Sell hyperspace params:")
|
|
||||||
Hyperopt._params_pretty_print(params, 'roi', "ROI table:")
|
|
||||||
Hyperopt._params_pretty_print(params, 'stoploss', "Stoploss:")
|
|
||||||
Hyperopt._params_pretty_print(params, 'trailing', "Trailing stop:")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _params_update_for_json(result_dict, params, space: str) -> None:
|
|
||||||
if space in params:
|
|
||||||
space_params = Hyperopt._space_params(params, space)
|
|
||||||
if space in ['buy', 'sell']:
|
|
||||||
result_dict.setdefault('params', {}).update(space_params)
|
|
||||||
elif space == 'roi':
|
|
||||||
# TODO: get rid of OrderedDict when support for python 3.6 will be
|
|
||||||
# dropped (dicts keep the order as the language feature)
|
|
||||||
|
|
||||||
# Convert keys in min_roi dict to strings because
|
|
||||||
# rapidjson cannot dump dicts with integer keys...
|
|
||||||
# OrderedDict is used to keep the numeric order of the items
|
|
||||||
# in the dict.
|
|
||||||
result_dict['minimal_roi'] = OrderedDict(
|
|
||||||
(str(k), v) for k, v in space_params.items()
|
|
||||||
)
|
|
||||||
else: # 'stoploss', 'trailing'
|
|
||||||
result_dict.update(space_params)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _params_pretty_print(params, space: str, header: str) -> None:
|
|
||||||
if space in params:
|
|
||||||
space_params = Hyperopt._space_params(params, space, 5)
|
|
||||||
params_result = f"\n# {header}\n"
|
|
||||||
if space == 'stoploss':
|
|
||||||
params_result += f"stoploss = {space_params.get('stoploss')}"
|
|
||||||
elif space == 'roi':
|
|
||||||
# TODO: get rid of OrderedDict when support for python 3.6 will be
|
|
||||||
# dropped (dicts keep the order as the language feature)
|
|
||||||
minimal_roi_result = rapidjson.dumps(
|
|
||||||
OrderedDict(
|
|
||||||
(str(k), v) for k, v in space_params.items()
|
|
||||||
),
|
|
||||||
default=str, indent=4, number_mode=rapidjson.NM_NATIVE)
|
|
||||||
params_result += f"minimal_roi = {minimal_roi_result}"
|
|
||||||
elif space == 'trailing':
|
|
||||||
|
|
||||||
for k, v in space_params.items():
|
|
||||||
params_result += f'{k} = {v}\n'
|
|
||||||
|
|
||||||
else:
|
|
||||||
params_result += f"{space}_params = {pformat(space_params, indent=4)}"
|
|
||||||
params_result = params_result.replace("}", "\n}").replace("{", "{\n ")
|
|
||||||
|
|
||||||
params_result = params_result.replace("\n", "\n ")
|
|
||||||
print(params_result)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _space_params(params, space: str, r: int = None) -> Dict:
|
|
||||||
d = params[space]
|
|
||||||
# Round floats to `r` digits after the decimal point if requested
|
|
||||||
return round_dict(d, r) if r else d
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_best_loss(results, current_best_loss: float) -> bool:
|
|
||||||
return results['loss'] < current_best_loss
|
|
||||||
|
|
||||||
def print_results(self, results) -> None:
|
def print_results(self, results) -> None:
|
||||||
"""
|
"""
|
||||||
Log results if it is better than any previous evaluation
|
Log results if it is better than any previous evaluation
|
||||||
|
TODO: this should be moved to HyperoptTools too
|
||||||
"""
|
"""
|
||||||
is_best = results['is_best']
|
is_best = results['is_best']
|
||||||
|
|
||||||
if self.print_all or is_best:
|
if self.print_all or is_best:
|
||||||
print(
|
print(
|
||||||
self.get_result_table(
|
HyperoptTools.get_result_table(
|
||||||
self.config, results, self.total_epochs,
|
self.config, results, self.total_epochs,
|
||||||
self.print_all, self.print_colorized,
|
self.print_all, self.print_colorized,
|
||||||
self.hyperopt_table_header
|
self.hyperopt_table_header
|
||||||
@ -303,166 +203,6 @@ class Hyperopt:
|
|||||||
)
|
)
|
||||||
self.hyperopt_table_header = 2
|
self.hyperopt_table_header = 2
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _format_explanation_string(results, total_epochs) -> str:
|
|
||||||
return (("*" if results['is_initial_point'] else " ") +
|
|
||||||
f"{results['current_epoch']:5d}/{total_epochs}: " +
|
|
||||||
f"{results['results_explanation']} " +
|
|
||||||
f"Objective: {results['loss']:.5f}")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_result_table(config: dict, results: list, total_epochs: int, highlight_best: bool,
|
|
||||||
print_colorized: bool, remove_header: int) -> str:
|
|
||||||
"""
|
|
||||||
Log result table
|
|
||||||
"""
|
|
||||||
if not results:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
tabulate.PRESERVE_WHITESPACE = True
|
|
||||||
|
|
||||||
trials = json_normalize(results, max_level=1)
|
|
||||||
trials['Best'] = ''
|
|
||||||
if 'results_metrics.winsdrawslosses' not in trials.columns:
|
|
||||||
# Ensure compatibility with older versions of hyperopt results
|
|
||||||
trials['results_metrics.winsdrawslosses'] = 'N/A'
|
|
||||||
|
|
||||||
trials = trials[['Best', 'current_epoch', 'results_metrics.trade_count',
|
|
||||||
'results_metrics.winsdrawslosses',
|
|
||||||
'results_metrics.avg_profit', 'results_metrics.total_profit',
|
|
||||||
'results_metrics.profit', 'results_metrics.duration',
|
|
||||||
'loss', 'is_initial_point', 'is_best']]
|
|
||||||
trials.columns = ['Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit',
|
|
||||||
'Total profit', 'Profit', 'Avg duration', 'Objective',
|
|
||||||
'is_initial_point', 'is_best']
|
|
||||||
trials['is_profit'] = False
|
|
||||||
trials.loc[trials['is_initial_point'], 'Best'] = '* '
|
|
||||||
trials.loc[trials['is_best'], 'Best'] = 'Best'
|
|
||||||
trials.loc[trials['is_initial_point'] & trials['is_best'], 'Best'] = '* Best'
|
|
||||||
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
|
|
||||||
trials['Trades'] = trials['Trades'].astype(str)
|
|
||||||
|
|
||||||
trials['Epoch'] = trials['Epoch'].apply(
|
|
||||||
lambda x: '{}/{}'.format(str(x).rjust(len(str(total_epochs)), ' '), total_epochs)
|
|
||||||
)
|
|
||||||
trials['Avg profit'] = trials['Avg profit'].apply(
|
|
||||||
lambda x: '{:,.2f}%'.format(x).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ')
|
|
||||||
)
|
|
||||||
trials['Avg duration'] = trials['Avg duration'].apply(
|
|
||||||
lambda x: '{:,.1f} m'.format(x).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ')
|
|
||||||
)
|
|
||||||
trials['Objective'] = trials['Objective'].apply(
|
|
||||||
lambda x: '{:,.5f}'.format(x).rjust(8, ' ') if x != 100000 else "N/A".rjust(8, ' ')
|
|
||||||
)
|
|
||||||
|
|
||||||
trials['Profit'] = trials.apply(
|
|
||||||
lambda x: '{:,.8f} {} {}'.format(
|
|
||||||
x['Total profit'], config['stake_currency'],
|
|
||||||
'({:,.2f}%)'.format(x['Profit']).rjust(10, ' ')
|
|
||||||
).rjust(25+len(config['stake_currency']))
|
|
||||||
if x['Total profit'] != 0.0 else '--'.rjust(25+len(config['stake_currency'])),
|
|
||||||
axis=1
|
|
||||||
)
|
|
||||||
trials = trials.drop(columns=['Total profit'])
|
|
||||||
|
|
||||||
if print_colorized:
|
|
||||||
for i in range(len(trials)):
|
|
||||||
if trials.loc[i]['is_profit']:
|
|
||||||
for j in range(len(trials.loc[i])-3):
|
|
||||||
trials.iat[i, j] = "{}{}{}".format(Fore.GREEN,
|
|
||||||
str(trials.loc[i][j]), Fore.RESET)
|
|
||||||
if trials.loc[i]['is_best'] and highlight_best:
|
|
||||||
for j in range(len(trials.loc[i])-3):
|
|
||||||
trials.iat[i, j] = "{}{}{}".format(Style.BRIGHT,
|
|
||||||
str(trials.loc[i][j]), Style.RESET_ALL)
|
|
||||||
|
|
||||||
trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit'])
|
|
||||||
if remove_header > 0:
|
|
||||||
table = tabulate.tabulate(
|
|
||||||
trials.to_dict(orient='list'), tablefmt='orgtbl',
|
|
||||||
headers='keys', stralign="right"
|
|
||||||
)
|
|
||||||
|
|
||||||
table = table.split("\n", remove_header)[remove_header]
|
|
||||||
elif remove_header < 0:
|
|
||||||
table = tabulate.tabulate(
|
|
||||||
trials.to_dict(orient='list'), tablefmt='psql',
|
|
||||||
headers='keys', stralign="right"
|
|
||||||
)
|
|
||||||
table = "\n".join(table.split("\n")[0:remove_header])
|
|
||||||
else:
|
|
||||||
table = tabulate.tabulate(
|
|
||||||
trials.to_dict(orient='list'), tablefmt='psql',
|
|
||||||
headers='keys', stralign="right"
|
|
||||||
)
|
|
||||||
return table
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def export_csv_file(config: dict, results: list, total_epochs: int, highlight_best: bool,
|
|
||||||
csv_file: str) -> None:
|
|
||||||
"""
|
|
||||||
Log result to csv-file
|
|
||||||
"""
|
|
||||||
if not results:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Verification for overwrite
|
|
||||||
if Path(csv_file).is_file():
|
|
||||||
logger.error(f"CSV file already exists: {csv_file}")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
io.open(csv_file, 'w+').close()
|
|
||||||
except IOError:
|
|
||||||
logger.error(f"Failed to create CSV file: {csv_file}")
|
|
||||||
return
|
|
||||||
|
|
||||||
trials = json_normalize(results, max_level=1)
|
|
||||||
trials['Best'] = ''
|
|
||||||
trials['Stake currency'] = config['stake_currency']
|
|
||||||
|
|
||||||
base_metrics = ['Best', 'current_epoch', 'results_metrics.trade_count',
|
|
||||||
'results_metrics.avg_profit', 'results_metrics.median_profit',
|
|
||||||
'results_metrics.total_profit',
|
|
||||||
'Stake currency', 'results_metrics.profit', 'results_metrics.duration',
|
|
||||||
'loss', 'is_initial_point', 'is_best']
|
|
||||||
param_metrics = [("params_dict."+param) for param in results[0]['params_dict'].keys()]
|
|
||||||
trials = trials[base_metrics + param_metrics]
|
|
||||||
|
|
||||||
base_columns = ['Best', 'Epoch', 'Trades', 'Avg profit', 'Median profit', 'Total profit',
|
|
||||||
'Stake currency', 'Profit', 'Avg duration', 'Objective',
|
|
||||||
'is_initial_point', 'is_best']
|
|
||||||
param_columns = list(results[0]['params_dict'].keys())
|
|
||||||
trials.columns = base_columns + param_columns
|
|
||||||
|
|
||||||
trials['is_profit'] = False
|
|
||||||
trials.loc[trials['is_initial_point'], 'Best'] = '*'
|
|
||||||
trials.loc[trials['is_best'], 'Best'] = 'Best'
|
|
||||||
trials.loc[trials['is_initial_point'] & trials['is_best'], 'Best'] = '* Best'
|
|
||||||
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
|
|
||||||
trials['Epoch'] = trials['Epoch'].astype(str)
|
|
||||||
trials['Trades'] = trials['Trades'].astype(str)
|
|
||||||
|
|
||||||
trials['Total profit'] = trials['Total profit'].apply(
|
|
||||||
lambda x: '{:,.8f}'.format(x) if x != 0.0 else ""
|
|
||||||
)
|
|
||||||
trials['Profit'] = trials['Profit'].apply(
|
|
||||||
lambda x: '{:,.2f}'.format(x) if not isna(x) else ""
|
|
||||||
)
|
|
||||||
trials['Avg profit'] = trials['Avg profit'].apply(
|
|
||||||
lambda x: '{:,.2f}%'.format(x) if not isna(x) else ""
|
|
||||||
)
|
|
||||||
trials['Avg duration'] = trials['Avg duration'].apply(
|
|
||||||
lambda x: '{:,.1f} m'.format(x) if not isna(x) else ""
|
|
||||||
)
|
|
||||||
trials['Objective'] = trials['Objective'].apply(
|
|
||||||
lambda x: '{:,.5f}'.format(x) if x != 100000 else ""
|
|
||||||
)
|
|
||||||
|
|
||||||
trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit'])
|
|
||||||
trials.to_csv(csv_file, index=False, header=True, mode='w', encoding='UTF-8')
|
|
||||||
logger.info(f"CSV file created: {csv_file}")
|
|
||||||
|
|
||||||
def has_space(self, space: str) -> bool:
|
def has_space(self, space: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Tell if the space value is contained in the configuration
|
Tell if the space value is contained in the configuration
|
||||||
@ -626,22 +366,6 @@ class Hyperopt:
|
|||||||
return parallel(delayed(
|
return parallel(delayed(
|
||||||
wrap_non_picklable_objects(self.generate_optimizer))(v, i) for v in asked)
|
wrap_non_picklable_objects(self.generate_optimizer))(v, i) for v in asked)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def load_previous_results(results_file: Path) -> List:
|
|
||||||
"""
|
|
||||||
Load data for epochs from the file if we have one
|
|
||||||
"""
|
|
||||||
epochs: List = []
|
|
||||||
if results_file.is_file() and results_file.stat().st_size > 0:
|
|
||||||
epochs = Hyperopt._read_results(results_file)
|
|
||||||
# Detection of some old format, without 'is_best' field saved
|
|
||||||
if epochs[0].get('is_best') is None:
|
|
||||||
raise OperationalException(
|
|
||||||
"The file with Hyperopt results is incompatible with this version "
|
|
||||||
"of Freqtrade and cannot be loaded.")
|
|
||||||
logger.info(f"Loaded {len(epochs)} previous evaluations from disk.")
|
|
||||||
return epochs
|
|
||||||
|
|
||||||
def _set_random_state(self, random_state: Optional[int]) -> int:
|
def _set_random_state(self, random_state: Optional[int]) -> int:
|
||||||
return random_state or random.randint(1, 2**16 - 1)
|
return random_state or random.randint(1, 2**16 - 1)
|
||||||
|
|
||||||
@ -734,7 +458,7 @@ class Hyperopt:
|
|||||||
|
|
||||||
logger.debug(f"Optimizer epoch evaluated: {val}")
|
logger.debug(f"Optimizer epoch evaluated: {val}")
|
||||||
|
|
||||||
is_best = self.is_best_loss(val, self.current_best_loss)
|
is_best = HyperoptTools.is_best_loss(val, self.current_best_loss)
|
||||||
# This value is assigned here and not in the optimization method
|
# This value is assigned here and not in the optimization method
|
||||||
# to keep proper order in the list of results. That's because
|
# to keep proper order in the list of results. That's because
|
||||||
# evaluations can take different time. Here they are aligned in the
|
# evaluations can take different time. Here they are aligned in the
|
||||||
@ -762,7 +486,7 @@ class Hyperopt:
|
|||||||
if self.epochs:
|
if self.epochs:
|
||||||
sorted_epochs = sorted(self.epochs, key=itemgetter('loss'))
|
sorted_epochs = sorted(self.epochs, key=itemgetter('loss'))
|
||||||
best_epoch = sorted_epochs[0]
|
best_epoch = sorted_epochs[0]
|
||||||
self.print_epoch_details(best_epoch, self.total_epochs, self.print_json)
|
HyperoptTools.print_epoch_details(best_epoch, self.total_epochs, self.print_json)
|
||||||
else:
|
else:
|
||||||
# This is printed when Ctrl+C is pressed quickly, before first epochs have
|
# This is printed when Ctrl+C is pressed quickly, before first epochs have
|
||||||
# a chance to be evaluated.
|
# a chance to be evaluated.
|
||||||
|
294
freqtrade/optimize/hyperopt_tools.py
Normal file
294
freqtrade/optimize/hyperopt_tools.py
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
from collections import OrderedDict
|
||||||
|
from pathlib import Path
|
||||||
|
from pprint import pformat
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
import rapidjson
|
||||||
|
import tabulate
|
||||||
|
from colorama import Fore, Style
|
||||||
|
from joblib import load
|
||||||
|
from pandas import isna, json_normalize
|
||||||
|
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
|
from freqtrade.misc import round_dict
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class HyperoptTools():
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _read_results(results_file: Path) -> List:
|
||||||
|
"""
|
||||||
|
Read hyperopt results from file
|
||||||
|
"""
|
||||||
|
logger.info("Reading epochs from '%s'", results_file)
|
||||||
|
data = load(results_file)
|
||||||
|
return data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_previous_results(results_file: Path) -> List:
|
||||||
|
"""
|
||||||
|
Load data for epochs from the file if we have one
|
||||||
|
"""
|
||||||
|
epochs: List = []
|
||||||
|
if results_file.is_file() and results_file.stat().st_size > 0:
|
||||||
|
epochs = HyperoptTools._read_results(results_file)
|
||||||
|
# Detection of some old format, without 'is_best' field saved
|
||||||
|
if epochs[0].get('is_best') is None:
|
||||||
|
raise OperationalException(
|
||||||
|
"The file with HyperoptTools results is incompatible with this version "
|
||||||
|
"of Freqtrade and cannot be loaded.")
|
||||||
|
logger.info(f"Loaded {len(epochs)} previous evaluations from disk.")
|
||||||
|
return epochs
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def print_epoch_details(results, total_epochs: int, print_json: bool,
|
||||||
|
no_header: bool = False, header_str: str = None) -> None:
|
||||||
|
"""
|
||||||
|
Display details of the hyperopt result
|
||||||
|
"""
|
||||||
|
params = results.get('params_details', {})
|
||||||
|
|
||||||
|
# Default header string
|
||||||
|
if header_str is None:
|
||||||
|
header_str = "Best result"
|
||||||
|
|
||||||
|
if not no_header:
|
||||||
|
explanation_str = HyperoptTools._format_explanation_string(results, total_epochs)
|
||||||
|
print(f"\n{header_str}:\n\n{explanation_str}\n")
|
||||||
|
|
||||||
|
if print_json:
|
||||||
|
result_dict: Dict = {}
|
||||||
|
for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']:
|
||||||
|
HyperoptTools._params_update_for_json(result_dict, params, s)
|
||||||
|
print(rapidjson.dumps(result_dict, default=str, number_mode=rapidjson.NM_NATIVE))
|
||||||
|
|
||||||
|
else:
|
||||||
|
HyperoptTools._params_pretty_print(params, 'buy', "Buy hyperspace params:")
|
||||||
|
HyperoptTools._params_pretty_print(params, 'sell', "Sell hyperspace params:")
|
||||||
|
HyperoptTools._params_pretty_print(params, 'roi', "ROI table:")
|
||||||
|
HyperoptTools._params_pretty_print(params, 'stoploss', "Stoploss:")
|
||||||
|
HyperoptTools._params_pretty_print(params, 'trailing', "Trailing stop:")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _params_update_for_json(result_dict, params, space: str) -> None:
|
||||||
|
if space in params:
|
||||||
|
space_params = HyperoptTools._space_params(params, space)
|
||||||
|
if space in ['buy', 'sell']:
|
||||||
|
result_dict.setdefault('params', {}).update(space_params)
|
||||||
|
elif space == 'roi':
|
||||||
|
# TODO: get rid of OrderedDict when support for python 3.6 will be
|
||||||
|
# dropped (dicts keep the order as the language feature)
|
||||||
|
|
||||||
|
# Convert keys in min_roi dict to strings because
|
||||||
|
# rapidjson cannot dump dicts with integer keys...
|
||||||
|
# OrderedDict is used to keep the numeric order of the items
|
||||||
|
# in the dict.
|
||||||
|
result_dict['minimal_roi'] = OrderedDict(
|
||||||
|
(str(k), v) for k, v in space_params.items()
|
||||||
|
)
|
||||||
|
else: # 'stoploss', 'trailing'
|
||||||
|
result_dict.update(space_params)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _params_pretty_print(params, space: str, header: str) -> None:
|
||||||
|
if space in params:
|
||||||
|
space_params = HyperoptTools._space_params(params, space, 5)
|
||||||
|
params_result = f"\n# {header}\n"
|
||||||
|
if space == 'stoploss':
|
||||||
|
params_result += f"stoploss = {space_params.get('stoploss')}"
|
||||||
|
elif space == 'roi':
|
||||||
|
# TODO: get rid of OrderedDict when support for python 3.6 will be
|
||||||
|
# dropped (dicts keep the order as the language feature)
|
||||||
|
minimal_roi_result = rapidjson.dumps(
|
||||||
|
OrderedDict(
|
||||||
|
(str(k), v) for k, v in space_params.items()
|
||||||
|
),
|
||||||
|
default=str, indent=4, number_mode=rapidjson.NM_NATIVE)
|
||||||
|
params_result += f"minimal_roi = {minimal_roi_result}"
|
||||||
|
elif space == 'trailing':
|
||||||
|
|
||||||
|
for k, v in space_params.items():
|
||||||
|
params_result += f'{k} = {v}\n'
|
||||||
|
|
||||||
|
else:
|
||||||
|
params_result += f"{space}_params = {pformat(space_params, indent=4)}"
|
||||||
|
params_result = params_result.replace("}", "\n}").replace("{", "{\n ")
|
||||||
|
|
||||||
|
params_result = params_result.replace("\n", "\n ")
|
||||||
|
print(params_result)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _space_params(params, space: str, r: int = None) -> Dict:
|
||||||
|
d = params[space]
|
||||||
|
# Round floats to `r` digits after the decimal point if requested
|
||||||
|
return round_dict(d, r) if r else d
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_best_loss(results, current_best_loss: float) -> bool:
|
||||||
|
return results['loss'] < current_best_loss
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_explanation_string(results, total_epochs) -> str:
|
||||||
|
return (("*" if results['is_initial_point'] else " ") +
|
||||||
|
f"{results['current_epoch']:5d}/{total_epochs}: " +
|
||||||
|
f"{results['results_explanation']} " +
|
||||||
|
f"Objective: {results['loss']:.5f}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_result_table(config: dict, results: list, total_epochs: int, highlight_best: bool,
|
||||||
|
print_colorized: bool, remove_header: int) -> str:
|
||||||
|
"""
|
||||||
|
Log result table
|
||||||
|
"""
|
||||||
|
if not results:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
tabulate.PRESERVE_WHITESPACE = True
|
||||||
|
|
||||||
|
trials = json_normalize(results, max_level=1)
|
||||||
|
trials['Best'] = ''
|
||||||
|
if 'results_metrics.winsdrawslosses' not in trials.columns:
|
||||||
|
# Ensure compatibility with older versions of hyperopt results
|
||||||
|
trials['results_metrics.winsdrawslosses'] = 'N/A'
|
||||||
|
|
||||||
|
trials = trials[['Best', 'current_epoch', 'results_metrics.trade_count',
|
||||||
|
'results_metrics.winsdrawslosses',
|
||||||
|
'results_metrics.avg_profit', 'results_metrics.total_profit',
|
||||||
|
'results_metrics.profit', 'results_metrics.duration',
|
||||||
|
'loss', 'is_initial_point', 'is_best']]
|
||||||
|
trials.columns = ['Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit',
|
||||||
|
'Total profit', 'Profit', 'Avg duration', 'Objective',
|
||||||
|
'is_initial_point', 'is_best']
|
||||||
|
trials['is_profit'] = False
|
||||||
|
trials.loc[trials['is_initial_point'], 'Best'] = '* '
|
||||||
|
trials.loc[trials['is_best'], 'Best'] = 'Best'
|
||||||
|
trials.loc[trials['is_initial_point'] & trials['is_best'], 'Best'] = '* Best'
|
||||||
|
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
|
||||||
|
trials['Trades'] = trials['Trades'].astype(str)
|
||||||
|
|
||||||
|
trials['Epoch'] = trials['Epoch'].apply(
|
||||||
|
lambda x: '{}/{}'.format(str(x).rjust(len(str(total_epochs)), ' '), total_epochs)
|
||||||
|
)
|
||||||
|
trials['Avg profit'] = trials['Avg profit'].apply(
|
||||||
|
lambda x: '{:,.2f}%'.format(x).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ')
|
||||||
|
)
|
||||||
|
trials['Avg duration'] = trials['Avg duration'].apply(
|
||||||
|
lambda x: '{:,.1f} m'.format(x).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ')
|
||||||
|
)
|
||||||
|
trials['Objective'] = trials['Objective'].apply(
|
||||||
|
lambda x: '{:,.5f}'.format(x).rjust(8, ' ') if x != 100000 else "N/A".rjust(8, ' ')
|
||||||
|
)
|
||||||
|
|
||||||
|
trials['Profit'] = trials.apply(
|
||||||
|
lambda x: '{:,.8f} {} {}'.format(
|
||||||
|
x['Total profit'], config['stake_currency'],
|
||||||
|
'({:,.2f}%)'.format(x['Profit']).rjust(10, ' ')
|
||||||
|
).rjust(25+len(config['stake_currency']))
|
||||||
|
if x['Total profit'] != 0.0 else '--'.rjust(25+len(config['stake_currency'])),
|
||||||
|
axis=1
|
||||||
|
)
|
||||||
|
trials = trials.drop(columns=['Total profit'])
|
||||||
|
|
||||||
|
if print_colorized:
|
||||||
|
for i in range(len(trials)):
|
||||||
|
if trials.loc[i]['is_profit']:
|
||||||
|
for j in range(len(trials.loc[i])-3):
|
||||||
|
trials.iat[i, j] = "{}{}{}".format(Fore.GREEN,
|
||||||
|
str(trials.loc[i][j]), Fore.RESET)
|
||||||
|
if trials.loc[i]['is_best'] and highlight_best:
|
||||||
|
for j in range(len(trials.loc[i])-3):
|
||||||
|
trials.iat[i, j] = "{}{}{}".format(Style.BRIGHT,
|
||||||
|
str(trials.loc[i][j]), Style.RESET_ALL)
|
||||||
|
|
||||||
|
trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit'])
|
||||||
|
if remove_header > 0:
|
||||||
|
table = tabulate.tabulate(
|
||||||
|
trials.to_dict(orient='list'), tablefmt='orgtbl',
|
||||||
|
headers='keys', stralign="right"
|
||||||
|
)
|
||||||
|
|
||||||
|
table = table.split("\n", remove_header)[remove_header]
|
||||||
|
elif remove_header < 0:
|
||||||
|
table = tabulate.tabulate(
|
||||||
|
trials.to_dict(orient='list'), tablefmt='psql',
|
||||||
|
headers='keys', stralign="right"
|
||||||
|
)
|
||||||
|
table = "\n".join(table.split("\n")[0:remove_header])
|
||||||
|
else:
|
||||||
|
table = tabulate.tabulate(
|
||||||
|
trials.to_dict(orient='list'), tablefmt='psql',
|
||||||
|
headers='keys', stralign="right"
|
||||||
|
)
|
||||||
|
return table
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def export_csv_file(config: dict, results: list, total_epochs: int, highlight_best: bool,
|
||||||
|
csv_file: str) -> None:
|
||||||
|
"""
|
||||||
|
Log result to csv-file
|
||||||
|
"""
|
||||||
|
if not results:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Verification for overwrite
|
||||||
|
if Path(csv_file).is_file():
|
||||||
|
logger.error(f"CSV file already exists: {csv_file}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
io.open(csv_file, 'w+').close()
|
||||||
|
except IOError:
|
||||||
|
logger.error(f"Failed to create CSV file: {csv_file}")
|
||||||
|
return
|
||||||
|
|
||||||
|
trials = json_normalize(results, max_level=1)
|
||||||
|
trials['Best'] = ''
|
||||||
|
trials['Stake currency'] = config['stake_currency']
|
||||||
|
|
||||||
|
base_metrics = ['Best', 'current_epoch', 'results_metrics.trade_count',
|
||||||
|
'results_metrics.avg_profit', 'results_metrics.median_profit',
|
||||||
|
'results_metrics.total_profit',
|
||||||
|
'Stake currency', 'results_metrics.profit', 'results_metrics.duration',
|
||||||
|
'loss', 'is_initial_point', 'is_best']
|
||||||
|
param_metrics = [("params_dict."+param) for param in results[0]['params_dict'].keys()]
|
||||||
|
trials = trials[base_metrics + param_metrics]
|
||||||
|
|
||||||
|
base_columns = ['Best', 'Epoch', 'Trades', 'Avg profit', 'Median profit', 'Total profit',
|
||||||
|
'Stake currency', 'Profit', 'Avg duration', 'Objective',
|
||||||
|
'is_initial_point', 'is_best']
|
||||||
|
param_columns = list(results[0]['params_dict'].keys())
|
||||||
|
trials.columns = base_columns + param_columns
|
||||||
|
|
||||||
|
trials['is_profit'] = False
|
||||||
|
trials.loc[trials['is_initial_point'], 'Best'] = '*'
|
||||||
|
trials.loc[trials['is_best'], 'Best'] = 'Best'
|
||||||
|
trials.loc[trials['is_initial_point'] & trials['is_best'], 'Best'] = '* Best'
|
||||||
|
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
|
||||||
|
trials['Epoch'] = trials['Epoch'].astype(str)
|
||||||
|
trials['Trades'] = trials['Trades'].astype(str)
|
||||||
|
|
||||||
|
trials['Total profit'] = trials['Total profit'].apply(
|
||||||
|
lambda x: '{:,.8f}'.format(x) if x != 0.0 else ""
|
||||||
|
)
|
||||||
|
trials['Profit'] = trials['Profit'].apply(
|
||||||
|
lambda x: '{:,.2f}'.format(x) if not isna(x) else ""
|
||||||
|
)
|
||||||
|
trials['Avg profit'] = trials['Avg profit'].apply(
|
||||||
|
lambda x: '{:,.2f}%'.format(x) if not isna(x) else ""
|
||||||
|
)
|
||||||
|
trials['Avg duration'] = trials['Avg duration'].apply(
|
||||||
|
lambda x: '{:,.1f} m'.format(x) if not isna(x) else ""
|
||||||
|
)
|
||||||
|
trials['Objective'] = trials['Objective'].apply(
|
||||||
|
lambda x: '{:,.5f}'.format(x) if x != 100000 else ""
|
||||||
|
)
|
||||||
|
|
||||||
|
trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit'])
|
||||||
|
trials.to_csv(csv_file, index=False, header=True, mode='w', encoding='UTF-8')
|
||||||
|
logger.info(f"CSV file created: {csv_file}")
|
@ -920,7 +920,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
|
|||||||
|
|
||||||
def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.optimize.hyperopt.Hyperopt.load_previous_results',
|
'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results',
|
||||||
MagicMock(return_value=hyperopt_results)
|
MagicMock(return_value=hyperopt_results)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1152,7 +1152,7 @@ def test_hyperopt_list(mocker, capsys, caplog, hyperopt_results):
|
|||||||
|
|
||||||
def test_hyperopt_show(mocker, capsys, hyperopt_results):
|
def test_hyperopt_show(mocker, capsys, hyperopt_results):
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.optimize.hyperopt.Hyperopt.load_previous_results',
|
'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results',
|
||||||
MagicMock(return_value=hyperopt_results)
|
MagicMock(return_value=hyperopt_results)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ from freqtrade.commands.optimize_commands import setup_optimize_configuration, s
|
|||||||
from freqtrade.data.history import load_data
|
from freqtrade.data.history import load_data
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.optimize.hyperopt import Hyperopt
|
from freqtrade.optimize.hyperopt import Hyperopt
|
||||||
|
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||||
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
|
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
|
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
|
||||||
@ -336,9 +337,9 @@ def test_save_results_saves_epochs(mocker, hyperopt, testdatadir, caplog) -> Non
|
|||||||
|
|
||||||
def test_read_results_returns_epochs(mocker, hyperopt, testdatadir, caplog) -> None:
|
def test_read_results_returns_epochs(mocker, hyperopt, testdatadir, caplog) -> None:
|
||||||
epochs = create_results(mocker, hyperopt, testdatadir)
|
epochs = create_results(mocker, hyperopt, testdatadir)
|
||||||
mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=epochs)
|
mock_load = mocker.patch('freqtrade.optimize.hyperopt_tools.load', return_value=epochs)
|
||||||
results_file = testdatadir / 'optimize' / 'ut_results.pickle'
|
results_file = testdatadir / 'optimize' / 'ut_results.pickle'
|
||||||
hyperopt_epochs = hyperopt._read_results(results_file)
|
hyperopt_epochs = HyperoptTools._read_results(results_file)
|
||||||
assert log_has(f"Reading epochs from '{results_file}'", caplog)
|
assert log_has(f"Reading epochs from '{results_file}'", caplog)
|
||||||
assert hyperopt_epochs == epochs
|
assert hyperopt_epochs == epochs
|
||||||
mock_load.assert_called_once()
|
mock_load.assert_called_once()
|
||||||
@ -346,7 +347,7 @@ def test_read_results_returns_epochs(mocker, hyperopt, testdatadir, caplog) -> N
|
|||||||
|
|
||||||
def test_load_previous_results(mocker, hyperopt, testdatadir, caplog) -> None:
|
def test_load_previous_results(mocker, hyperopt, testdatadir, caplog) -> None:
|
||||||
epochs = create_results(mocker, hyperopt, testdatadir)
|
epochs = create_results(mocker, hyperopt, testdatadir)
|
||||||
mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=epochs)
|
mock_load = mocker.patch('freqtrade.optimize.hyperopt_tools.load', return_value=epochs)
|
||||||
mocker.patch.object(Path, 'is_file', MagicMock(return_value=True))
|
mocker.patch.object(Path, 'is_file', MagicMock(return_value=True))
|
||||||
statmock = MagicMock()
|
statmock = MagicMock()
|
||||||
statmock.st_size = 5
|
statmock.st_size = 5
|
||||||
@ -354,16 +355,16 @@ def test_load_previous_results(mocker, hyperopt, testdatadir, caplog) -> None:
|
|||||||
|
|
||||||
results_file = testdatadir / 'optimize' / 'ut_results.pickle'
|
results_file = testdatadir / 'optimize' / 'ut_results.pickle'
|
||||||
|
|
||||||
hyperopt_epochs = hyperopt.load_previous_results(results_file)
|
hyperopt_epochs = HyperoptTools.load_previous_results(results_file)
|
||||||
|
|
||||||
assert hyperopt_epochs == epochs
|
assert hyperopt_epochs == epochs
|
||||||
mock_load.assert_called_once()
|
mock_load.assert_called_once()
|
||||||
|
|
||||||
del epochs[0]['is_best']
|
del epochs[0]['is_best']
|
||||||
mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=epochs)
|
mock_load = mocker.patch('freqtrade.optimize.hyperopt_tools.load', return_value=epochs)
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
hyperopt.load_previous_results(results_file)
|
HyperoptTools.load_previous_results(results_file)
|
||||||
|
|
||||||
|
|
||||||
def test_roi_table_generation(hyperopt) -> None:
|
def test_roi_table_generation(hyperopt) -> None:
|
||||||
@ -453,7 +454,7 @@ def test_format_results(hyperopt):
|
|||||||
'is_initial_point': True,
|
'is_initial_point': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
result = hyperopt._format_explanation_string(results, 1)
|
result = HyperoptTools._format_explanation_string(results, 1)
|
||||||
assert result.find(' 66.67%')
|
assert result.find(' 66.67%')
|
||||||
assert result.find('Total profit 1.00000000 BTC')
|
assert result.find('Total profit 1.00000000 BTC')
|
||||||
assert result.find('2.0000Σ %')
|
assert result.find('2.0000Σ %')
|
||||||
@ -467,7 +468,7 @@ def test_format_results(hyperopt):
|
|||||||
df = pd.DataFrame.from_records(trades, columns=labels)
|
df = pd.DataFrame.from_records(trades, columns=labels)
|
||||||
results_metrics = hyperopt._calculate_results_metrics(df)
|
results_metrics = hyperopt._calculate_results_metrics(df)
|
||||||
results['total_profit'] = results_metrics['total_profit']
|
results['total_profit'] = results_metrics['total_profit']
|
||||||
result = hyperopt._format_explanation_string(results, 1)
|
result = HyperoptTools._format_explanation_string(results, 1)
|
||||||
assert result.find('Total profit 1.00000000 EUR')
|
assert result.find('Total profit 1.00000000 EUR')
|
||||||
|
|
||||||
|
|
||||||
@ -1076,7 +1077,7 @@ def test_print_epoch_details(capsys):
|
|||||||
'is_best': True
|
'is_best': True
|
||||||
}
|
}
|
||||||
|
|
||||||
Hyperopt.print_epoch_details(test_result, 5, False, no_header=True)
|
HyperoptTools.print_epoch_details(test_result, 5, False, no_header=True)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert '# Trailing stop:' in captured.out
|
assert '# Trailing stop:' in captured.out
|
||||||
# re.match(r"Pairs for .*", captured.out)
|
# re.match(r"Pairs for .*", captured.out)
|
||||||
|
Loading…
Reference in New Issue
Block a user