Store epochs as json per line
This commit is contained in:
parent
7398ea88e0
commit
06bf1aa274
@ -5,15 +5,16 @@ This module contains the hyperopt logic
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from operator import itemgetter
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
import progressbar
|
import progressbar
|
||||||
|
import rapidjson
|
||||||
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
|
||||||
@ -86,7 +87,7 @@ class Hyperopt:
|
|||||||
time_now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
time_now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
strategy = str(self.config['strategy'])
|
strategy = str(self.config['strategy'])
|
||||||
self.results_file: Path = (self.config['user_data_dir'] / 'hyperopt_results' /
|
self.results_file: Path = (self.config['user_data_dir'] / 'hyperopt_results' /
|
||||||
f'strategy_{strategy}_hyperopt_results_{time_now}.pickle')
|
f'strategy_{strategy}_hyperopt_results_{time_now}.fthypt')
|
||||||
self.data_pickle_file = (self.config['user_data_dir'] /
|
self.data_pickle_file = (self.config['user_data_dir'] /
|
||||||
'hyperopt_results' / 'hyperopt_tickerdata.pkl')
|
'hyperopt_results' / 'hyperopt_tickerdata.pkl')
|
||||||
self.total_epochs = config.get('epochs', 0)
|
self.total_epochs = config.get('epochs', 0)
|
||||||
@ -96,9 +97,7 @@ class Hyperopt:
|
|||||||
self.clean_hyperopt()
|
self.clean_hyperopt()
|
||||||
|
|
||||||
self.num_epochs_saved = 0
|
self.num_epochs_saved = 0
|
||||||
|
self.current_best_epoch: Optional[Dict[str, Any]] = None
|
||||||
# Previous evaluations
|
|
||||||
self.epochs: List = []
|
|
||||||
|
|
||||||
# Populate functions here (hasattr is slow so should not be run during "regular" operations)
|
# Populate functions here (hasattr is slow so should not be run during "regular" operations)
|
||||||
if hasattr(self.custom_hyperopt, 'populate_indicators'):
|
if hasattr(self.custom_hyperopt, 'populate_indicators'):
|
||||||
@ -156,21 +155,24 @@ class Hyperopt:
|
|||||||
# and the values are taken from the list of parameters.
|
# and the values are taken from the list of parameters.
|
||||||
return {d.name: v for d, v in zip(dimensions, raw_params)}
|
return {d.name: v for d, v in zip(dimensions, raw_params)}
|
||||||
|
|
||||||
def _save_results(self) -> None:
|
def _save_result(self, epoch: Dict) -> None:
|
||||||
"""
|
"""
|
||||||
Save hyperopt results to file
|
Save hyperopt results to file
|
||||||
|
Store one line per epoch.
|
||||||
|
While not a valid json object - this allows appending easily.
|
||||||
|
:param epoch: result dictionary for this epoch.
|
||||||
"""
|
"""
|
||||||
num_epochs = len(self.epochs)
|
with self.results_file.open('a') as f:
|
||||||
if num_epochs > self.num_epochs_saved:
|
rapidjson.dump(epoch, f, default=str, number_mode=rapidjson.NM_NATIVE)
|
||||||
logger.debug(f"Saving {num_epochs} {plural(num_epochs, 'epoch')}.")
|
f.write(os.linesep)
|
||||||
dump(self.epochs, self.results_file)
|
|
||||||
self.num_epochs_saved = num_epochs
|
self.num_epochs_saved += 1
|
||||||
logger.debug(f"{self.num_epochs_saved} {plural(self.num_epochs_saved, 'epoch')} "
|
logger.debug(f"{self.num_epochs_saved} {plural(self.num_epochs_saved, 'epoch')} "
|
||||||
f"saved to '{self.results_file}'.")
|
f"saved to '{self.results_file}'.")
|
||||||
# Store hyperopt filename
|
# Store hyperopt filename
|
||||||
latest_filename = Path.joinpath(self.results_file.parent, LAST_BT_RESULT_FN)
|
latest_filename = Path.joinpath(self.results_file.parent, LAST_BT_RESULT_FN)
|
||||||
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)
|
||||||
|
|
||||||
def _get_params_details(self, params: Dict) -> Dict:
|
def _get_params_details(self, params: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
@ -442,25 +444,21 @@ class Hyperopt:
|
|||||||
|
|
||||||
if is_best:
|
if is_best:
|
||||||
self.current_best_loss = val['loss']
|
self.current_best_loss = val['loss']
|
||||||
self.epochs.append(val)
|
self.current_best_epoch = val
|
||||||
|
|
||||||
# Save results after each best epoch and every 100 epochs
|
self._save_result(val)
|
||||||
if is_best or current % 100 == 0:
|
|
||||||
self._save_results()
|
|
||||||
|
|
||||||
pbar.update(current)
|
pbar.update(current)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('User interrupted..')
|
print('User interrupted..')
|
||||||
|
|
||||||
self._save_results()
|
|
||||||
logger.info(f"{self.num_epochs_saved} {plural(self.num_epochs_saved, 'epoch')} "
|
logger.info(f"{self.num_epochs_saved} {plural(self.num_epochs_saved, 'epoch')} "
|
||||||
f"saved to '{self.results_file}'.")
|
f"saved to '{self.results_file}'.")
|
||||||
|
|
||||||
if self.epochs:
|
if self.current_best_epoch:
|
||||||
sorted_epochs = sorted(self.epochs, key=itemgetter('loss'))
|
HyperoptTools.print_epoch_details(self.current_best_epoch, self.total_epochs,
|
||||||
best_epoch = sorted_epochs[0]
|
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.
|
||||||
|
@ -386,7 +386,8 @@ def test_roi_table_generation(hyperopt) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
|
def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
|
||||||
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
|
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
|
||||||
|
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
||||||
|
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
||||||
@ -425,9 +426,9 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
|
|||||||
|
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
|
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
|
||||||
assert dumper.called
|
# Should be called for historical candle data
|
||||||
# Should be called twice, once for historical candle data, once to save evaluations
|
assert dumper.call_count == 1
|
||||||
assert dumper.call_count == 2
|
assert dumper2.call_count == 1
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
||||||
assert hasattr(hyperopt, "max_open_trades")
|
assert hasattr(hyperopt, "max_open_trades")
|
||||||
@ -714,7 +715,8 @@ def test_clean_hyperopt(mocker, hyperopt_conf, caplog):
|
|||||||
|
|
||||||
|
|
||||||
def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
|
def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
|
||||||
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
|
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
|
||||||
|
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
||||||
|
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
||||||
@ -765,13 +767,14 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
|
|||||||
':{},"stoploss":null,"trailing_stop":null}'
|
':{},"stoploss":null,"trailing_stop":null}'
|
||||||
)
|
)
|
||||||
assert result_str in out # noqa: E501
|
assert result_str in out # noqa: E501
|
||||||
assert dumper.called
|
# Should be called for historical candle data
|
||||||
# Should be called twice, once for historical candle data, once to save evaluations
|
assert dumper.call_count == 1
|
||||||
assert dumper.call_count == 2
|
assert dumper2.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
||||||
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
|
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
|
||||||
|
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
||||||
MagicMock(return_value=(MagicMock(), None)))
|
MagicMock(return_value=(MagicMock(), None)))
|
||||||
@ -813,13 +816,14 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
|||||||
|
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}' in out # noqa: E501
|
assert '{"params":{"mfi-value":null,"sell-mfi-value":null},"minimal_roi":{},"stoploss":null}' in out # noqa: E501
|
||||||
assert dumper.called
|
# Should be called for historical candle data
|
||||||
# Should be called twice, once for historical candle data, once to save evaluations
|
assert dumper.call_count == 1
|
||||||
assert dumper.call_count == 2
|
assert dumper2.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
||||||
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
|
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
|
||||||
|
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
||||||
MagicMock(return_value=(MagicMock(), None)))
|
MagicMock(return_value=(MagicMock(), None)))
|
||||||
@ -860,13 +864,14 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
|||||||
|
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert '{"minimal_roi":{},"stoploss":null}' in out
|
assert '{"minimal_roi":{},"stoploss":null}' in out
|
||||||
assert dumper.called
|
|
||||||
# Should be called twice, once for historical candle data, once to save evaluations
|
assert dumper.call_count == 1
|
||||||
assert dumper.call_count == 2
|
assert dumper2.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
||||||
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
|
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
|
||||||
|
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
||||||
MagicMock(return_value=(MagicMock(), None)))
|
MagicMock(return_value=(MagicMock(), None)))
|
||||||
@ -908,9 +913,9 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
|
|||||||
|
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
|
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
|
||||||
assert dumper.called
|
assert dumper.call_count == 1
|
||||||
# Should be called twice, once for historical candle data, once to save evaluations
|
assert dumper2.call_count == 1
|
||||||
assert dumper.call_count == 2
|
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
||||||
assert hasattr(hyperopt, "max_open_trades")
|
assert hasattr(hyperopt, "max_open_trades")
|
||||||
@ -946,7 +951,8 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
||||||
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
|
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
|
||||||
|
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
||||||
MagicMock(return_value=(MagicMock(), None)))
|
MagicMock(return_value=(MagicMock(), None)))
|
||||||
@ -989,8 +995,8 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
|
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
|
||||||
assert dumper.called
|
assert dumper.called
|
||||||
# Should be called twice, once for historical candle data, once to save evaluations
|
assert dumper.call_count == 1
|
||||||
assert dumper.call_count == 2
|
assert dumper2.call_count == 1
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
||||||
assert hasattr(hyperopt, "max_open_trades")
|
assert hasattr(hyperopt, "max_open_trades")
|
||||||
@ -999,7 +1005,8 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
||||||
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
|
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump')
|
||||||
|
dumper2 = mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._save_result')
|
||||||
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
|
||||||
MagicMock(return_value=(MagicMock(), None)))
|
MagicMock(return_value=(MagicMock(), None)))
|
||||||
@ -1042,8 +1049,8 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
|
assert 'Best result:\n\n* 1/1: foo result Objective: 1.00000\n' in out
|
||||||
assert dumper.called
|
assert dumper.called
|
||||||
# Should be called twice, once for historical candle data, once to save evaluations
|
assert dumper.call_count == 1
|
||||||
assert dumper.call_count == 2
|
assert dumper2.call_count == 1
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_sell")
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_buy")
|
||||||
assert hasattr(hyperopt, "max_open_trades")
|
assert hasattr(hyperopt, "max_open_trades")
|
||||||
|
Loading…
Reference in New Issue
Block a user