From 8f1f416a52b2381d6c2958a905eb4716dae1d037 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 30 Jul 2019 11:47:28 +0300 Subject: [PATCH 1/2] hyperopt cleanup and output improvements --- freqtrade/optimize/hyperopt.py | 65 ++++++++++++++++------------------ 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 759ceffbe..842a111ca 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -51,7 +51,7 @@ class Hyperopt(Backtesting): self.custom_hyperoptloss = HyperOptLossResolver(self.config).hyperoptloss self.calculate_loss = self.custom_hyperoptloss.hyperopt_loss_function - self.total_tries = config.get('epochs', 0) + self.total_epochs = config.get('epochs', 0) self.current_best_loss = 100 if not self.config.get('hyperopt_continue'): @@ -124,13 +124,12 @@ class Hyperopt(Backtesting): """ results = sorted(self.trials, key=itemgetter('loss')) best_result = results[0] - logger.info( - 'Best result:\n%s\nwith values:\n', - best_result['result'] - ) + + log_str = self.format_results_logstring(best_result) + print(f"\nBest result:\n{log_str}\nwith values:") pprint(best_result['params'], indent=4) if 'roi_t1' in best_result['params']: - logger.info('ROI table:') + print("ROI table:") pprint(self.custom_hyperopt.generate_roi_table(best_result['params']), indent=4) def log_results(self, results) -> None: @@ -139,22 +138,26 @@ class Hyperopt(Backtesting): """ print_all = self.config.get('print_all', False) if print_all or results['loss'] < self.current_best_loss: - # Output human-friendly index here (starting from 1) - current = results['current_tries'] + 1 - total = results['total_tries'] - res = results['result'] - loss = results['loss'] - self.current_best_loss = results['loss'] - log_msg = f'{current:5d}/{total}: {res} Objective: {loss:.5f}' - log_msg = f'*{log_msg}' if results['initial_point'] else f' {log_msg}' + log_str = self.format_results_logstring(results) if print_all: - print(log_msg) + print(log_str) else: - print('\n' + log_msg) + print('\n' + log_str) else: print('.', end='') sys.stdout.flush() + def format_results_logstring(self, results) -> str: + # Output human-friendly index here (starting from 1) + current = results['current_epoch'] + 1 + total = self.total_epochs + res = results['results_explanation'] + loss = results['loss'] + self.current_best_loss = results['loss'] + log_str = f'{current:5d}/{total}: {res} Objective: {loss:.5f}' + log_str = f'*{log_str}' if results['is_initial_point'] else f' {log_str}' + return log_str + def has_space(self, space: str) -> bool: """ Tell if a space value is contained in the configuration @@ -214,7 +217,7 @@ class Hyperopt(Backtesting): 'end_date': max_date, } ) - result_explanation = self.format_results(results) + results_explanation = self.format_results(results) trade_count = len(results.index) @@ -226,7 +229,7 @@ class Hyperopt(Backtesting): return { 'loss': MAX_LOSS, 'params': params, - 'result': result_explanation, + 'results_explanation': results_explanation, } loss = self.calculate_loss(results=results, trade_count=trade_count, @@ -235,12 +238,12 @@ class Hyperopt(Backtesting): return { 'loss': loss, 'params': params, - 'result': result_explanation, + 'results_explanation': results_explanation, } def format_results(self, results: DataFrame) -> str: """ - Return the format result in a string + Return the formatted results explanation in a string """ trades = len(results.index) avg_profit = results.profit_percent.mean() * 100.0 @@ -323,25 +326,19 @@ class Hyperopt(Backtesting): with Parallel(n_jobs=config_jobs) as parallel: jobs = parallel._effective_n_jobs() logger.info(f'Effective number of parallel workers used: {jobs}') - EVALS = max(self.total_tries // jobs, 1) + EVALS = max(self.total_epochs // jobs, 1) for i in range(EVALS): asked = opt.ask(n_points=jobs) f_val = self.run_optimizer_parallel(parallel, asked) - opt.tell(asked, [i['loss'] for i in f_val]) - - self.trials += f_val + opt.tell(asked, [v['loss'] for v in f_val]) for j in range(jobs): current = i * jobs + j - self.log_results({ - 'loss': f_val[j]['loss'], - 'current_tries': current, - 'initial_point': current < INITIAL_POINTS, - 'total_tries': self.total_tries, - 'result': f_val[j]['result'], - }) - logger.debug(f"Optimizer params: {f_val[j]['params']}") - for j in range(jobs): - logger.debug(f"Optimizer state: Xi: {opt.Xi[-j-1]}, yi: {opt.yi[-j-1]}") + val = f_val[j] + val['current_epoch'] = current + val['is_initial_point'] = current < INITIAL_POINTS + self.log_results(val) + self.trials.append(val) + logger.debug(f"Optimizer epoch evaluated: {val}") except KeyboardInterrupt: print('User interrupted..') From b976f24672fd8f0118d94a5890925fc404998abc Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 30 Jul 2019 11:47:46 +0300 Subject: [PATCH 2/2] tests adjusted --- freqtrade/tests/optimize/test_hyperopt.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 065b4c41b..5d6128e7c 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -370,13 +370,13 @@ def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results) def test_log_results_if_loss_improves(hyperopt, capsys) -> None: hyperopt.current_best_loss = 2 + hyperopt.total_epochs = 2 hyperopt.log_results( { 'loss': 1, - 'current_tries': 1, - 'total_tries': 2, - 'result': 'foo.', - 'initial_point': False + 'current_epoch': 1, + 'results_explanation': 'foo.', + 'is_initial_point': False } ) out, err = capsys.readouterr() @@ -433,7 +433,7 @@ def test_roi_table_generation(hyperopt) -> None: assert hyperopt.custom_hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0} -def test_start_calls_optimizer(mocker, default_conf, caplog) -> None: +def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None: dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock()) mocker.patch( @@ -443,7 +443,7 @@ def test_start_calls_optimizer(mocker, default_conf, caplog) -> None: parallel = mocker.patch( 'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel', - MagicMock(return_value=[{'loss': 1, 'result': 'foo result', 'params': {}}]) + MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}]) ) patch_exchange(mocker) @@ -457,8 +457,11 @@ def test_start_calls_optimizer(mocker, default_conf, caplog) -> None: hyperopt.strategy.tickerdata_to_dataframe = MagicMock() hyperopt.start() + parallel.assert_called_once() - assert log_has('Best result:\nfoo result\nwith values:\n', caplog.record_tuples) + + out, err = capsys.readouterr() + assert 'Best result:\n* 1/1: foo result Objective: 1.00000\nwith values:\n' in out assert dumper.called # Should be called twice, once for tickerdata, once to save evaluations assert dumper.call_count == 2 @@ -598,8 +601,8 @@ def test_generate_optimizer(mocker, default_conf) -> None: } response_expected = { 'loss': 1.9840569076926293, - 'result': ' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC ' - '( 2.31Σ%). Avg duration 100.0 mins.', + 'results_explanation': ' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC ' + '( 2.31Σ%). Avg duration 100.0 mins.', 'params': optimizer_param }