def test_params_print(capsys): params = { "buy": { "buy_rsi": 30 }, "sell": { "sell_rsi": 70 }, } non_optimized = { "buy": { "buy_adx": 44 }, "sell": { "sell_adx": 65 }, "stoploss": { "stoploss": -0.05, }, "roi": { "0": 0.05, "20": 0.01, }, "trailing": { "trailing_stop": False, "trailing_stop_positive": 0.05, "trailing_stop_positive_offset": 0.1, "trailing_only_offset_is_reached": True }, } HyperoptTools._params_pretty_print(params, 'buy', 'No header', non_optimized) captured = capsys.readouterr() assert re.search("# No header", captured.out) assert re.search('"buy_rsi": 30,\n', captured.out) assert re.search('"buy_adx": 44, # value loaded.*\n', captured.out) assert not re.search("sell", captured.out) HyperoptTools._params_pretty_print(params, 'sell', 'Sell Header', non_optimized) captured = capsys.readouterr() assert re.search("# Sell Header", captured.out) assert re.search('"sell_rsi": 70,\n', captured.out) assert re.search('"sell_adx": 65, # value loaded.*\n', captured.out) HyperoptTools._params_pretty_print(params, 'roi', 'ROI Table:', non_optimized) captured = capsys.readouterr() assert re.search("# ROI Table: # value loaded.*\n", captured.out) assert re.search('minimal_roi = {\n', captured.out) assert re.search('"20": 0.01\n', captured.out) HyperoptTools._params_pretty_print(params, 'trailing', 'Trailing stop:', non_optimized) captured = capsys.readouterr() assert re.search("# Trailing stop:", captured.out) assert re.search('trailing_stop = False # value loaded.*\n', captured.out) assert re.search('trailing_stop_positive = 0.05 # value loaded.*\n', captured.out) assert re.search('trailing_stop_positive_offset = 0.1 # value loaded.*\n', captured.out) assert re.search('trailing_only_offset_is_reached = True # value loaded.*\n', captured.out)
def _load_params(self, params: Dict, space: str, hyperopt: bool = False) -> None: """ Set optimizable parameter values. :param params: Dictionary with new parameter values. """ if not params: logger.info(f"No params for {space} found, using default values.") param_container: List[BaseParameter] = getattr(self, f"ft_{space}_params") for attr_name, attr in self.detect_parameters(space): attr.name = attr_name attr.in_space = hyperopt and HyperoptTools.has_space( self.config, space) if not attr.category: attr.category = space param_container.append(attr) if params and attr_name in params: if attr.load: attr.value = params[attr_name] logger.info( f'Strategy Parameter: {attr_name} = {attr.value}') else: logger.warning( f'Parameter "{attr_name}" exists, but is disabled. ' f'Default value "{attr.value}" used.') else: logger.info( f'Strategy Parameter(default): {attr_name} = {attr.value}')
def __init__(self, config: Dict[str, Any]) -> None: self.buy_space: List[Dimension] = [] self.sell_space: List[Dimension] = [] self.protection_space: List[Dimension] = [] self.roi_space: List[Dimension] = [] self.stoploss_space: List[Dimension] = [] self.trailing_space: List[Dimension] = [] self.dimensions: List[Dimension] = [] self.config = config self.backtesting = Backtesting(self.config) self.pairlist = self.backtesting.pairlists.whitelist if not self.config.get('hyperopt'): self.custom_hyperopt = HyperOptAuto(self.config) else: raise OperationalException( "Using separate Hyperopt files has been removed in 2021.9. Please convert " "your existing Hyperopt file to the new Hyperoptable strategy interface") self.backtesting._set_strategy(self.backtesting.strategylist[0]) self.custom_hyperopt.strategy = self.backtesting.strategy self.custom_hyperoptloss = HyperOptLossResolver.load_hyperoptloss(self.config) self.calculate_loss = self.custom_hyperoptloss.hyperopt_loss_function time_now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") strategy = str(self.config['strategy']) self.results_file: Path = (self.config['user_data_dir'] / 'hyperopt_results' / f'strategy_{strategy}_{time_now}.fthypt') self.data_pickle_file = (self.config['user_data_dir'] / 'hyperopt_results' / 'hyperopt_tickerdata.pkl') self.total_epochs = config.get('epochs', 0) self.current_best_loss = 100 self.clean_hyperopt() self.num_epochs_saved = 0 self.current_best_epoch: Optional[Dict[str, Any]] = None # Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set if self.config.get('use_max_market_positions', True): self.max_open_trades = self.config['max_open_trades'] else: logger.debug('Ignoring max_open_trades (--disable-max-market-positions was used) ...') self.max_open_trades = 0 self.position_stacking = self.config.get('position_stacking', False) if HyperoptTools.has_space(self.config, 'sell'): # Make sure use_sell_signal is enabled if 'ask_strategy' not in self.config: self.config['ask_strategy'] = {} self.config['ask_strategy']['use_sell_signal'] = True self.print_all = self.config.get('print_all', False) self.hyperopt_table_header = 0 self.print_colorized = self.config.get('print_colorized', False) self.print_json = self.config.get('print_json', False)
def test_read_results_returns_epochs(mocker, hyperopt, testdatadir, caplog) -> None: epochs = create_results(mocker, hyperopt, testdatadir) mock_load = mocker.patch('freqtrade.optimize.hyperopt_tools.load', return_value=epochs) results_file = testdatadir / 'optimize' / 'ut_results.pickle' hyperopt_epochs = HyperoptTools._read_results(results_file) assert log_has(f"Reading epochs from '{results_file}'", caplog) assert hyperopt_epochs == epochs mock_load.assert_called_once()
def test_load_previous_results(testdatadir, caplog) -> None: results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle' hyperopt_epochs = HyperoptTools.load_previous_results(results_file) assert len(hyperopt_epochs) == 5 assert log_has_re(r"Reading pickled epochs from .*", caplog) caplog.clear() # Modern version results_file = testdatadir / 'strategy_SampleStrategy.fthypt' hyperopt_epochs = HyperoptTools.load_previous_results(results_file) assert len(hyperopt_epochs) == 5 assert log_has_re(r"Reading epochs from .*", caplog)
def _get_no_optimize_details(self) -> Dict[str, Any]: """ Get non-optimized parameters """ result: Dict[str, Any] = {} strategy = self.backtesting.strategy if not HyperoptTools.has_space(self.config, 'roi'): result['roi'] = {str(k): v for k, v in strategy.minimal_roi.items()} if not HyperoptTools.has_space(self.config, 'stoploss'): result['stoploss'] = {'stoploss': strategy.stoploss} if not HyperoptTools.has_space(self.config, 'trailing'): result['trailing'] = { 'trailing_stop': strategy.trailing_stop, 'trailing_stop_positive': strategy.trailing_stop_positive, 'trailing_stop_positive_offset': strategy.trailing_stop_positive_offset, 'trailing_only_offset_is_reached': strategy.trailing_only_offset_is_reached, } return result
def test_try_export_params(default_conf, tmpdir, caplog, mocker): default_conf['disableparamexport'] = False export_mock = mocker.patch("freqtrade.optimize.hyperopt_tools.HyperoptTools.export_params") filename = Path(tmpdir) / f"{CURRENT_TEST_STRATEGY}.json" assert not filename.is_file() params = { "params_details": { "buy": { "buy_rsi": 30 }, "sell": { "sell_rsi": 70 }, "roi": { "0": 0.528, "346": 0.08499, "507": 0.049, "1595": 0 } }, "params_not_optimized": { "stoploss": -0.05, "trailing": { "trailing_stop": False, "trailing_stop_positive": 0.05, "trailing_stop_positive_offset": 0.1, "trailing_only_offset_is_reached": True }, }, FTHYPT_FILEVERSION: 2, } HyperoptTools.try_export_params(default_conf, "StrategyTestVXXX", params) assert log_has("Strategy not found, not exporting parameter file.", caplog) assert export_mock.call_count == 0 caplog.clear() HyperoptTools.try_export_params(default_conf, CURRENT_TEST_STRATEGY, params) assert export_mock.call_count == 1 assert export_mock.call_args_list[0][0][1] == CURRENT_TEST_STRATEGY assert export_mock.call_args_list[0][0][2].name == 'strategy_test_v3.json'
def test_export_params(tmpdir): filename = Path(tmpdir) / f"{CURRENT_TEST_STRATEGY}.json" assert not filename.is_file() params = { "params_details": { "buy": { "buy_rsi": 30 }, "sell": { "sell_rsi": 70 }, "roi": { "0": 0.528, "346": 0.08499, "507": 0.049, "1595": 0 } }, "params_not_optimized": { "stoploss": -0.05, "trailing": { "trailing_stop": False, "trailing_stop_positive": 0.05, "trailing_stop_positive_offset": 0.1, "trailing_only_offset_is_reached": True }, } } HyperoptTools.export_params(params, CURRENT_TEST_STRATEGY, filename) assert filename.is_file() with filename.open('r') as f: content = rapidjson.load(f) assert content['strategy_name'] == CURRENT_TEST_STRATEGY assert 'params' in content assert "buy" in content["params"] assert "sell" in content["params"] assert "roi" in content["params"] assert "stoploss" in content["params"] assert "trailing" in content["params"]
def test_load_previous_results(mocker, hyperopt, testdatadir, caplog) -> None: epochs = create_results(mocker, hyperopt, testdatadir) mock_load = mocker.patch('joblib.load', return_value=epochs) mocker.patch.object(Path, 'is_file', MagicMock(return_value=True)) statmock = MagicMock() statmock.st_size = 5 # mocker.patch.object(Path, 'stat', MagicMock(return_value=statmock)) results_file = testdatadir / 'optimize' / 'ut_results.pickle' hyperopt_epochs = HyperoptTools.load_previous_results(results_file) assert hyperopt_epochs == epochs mock_load.assert_called_once() del epochs[0]['is_best'] mock_load = mocker.patch('joblib.load', return_value=epochs) with pytest.raises(OperationalException): HyperoptTools.load_previous_results(results_file)
def test__pprint_dict(): params = { 'buy_std': 1.2, 'buy_rsi': 31, 'buy_enable': True, 'buy_what': 'asdf' } non_params = {'buy_notoptimied': 55} x = HyperoptTools._pprint_dict(params, non_params) assert x == """{
def test_format_results(hyperopt): # Test with BTC as stake_currency trades = [ ('ETH/BTC', 2, 2, 123), ('LTC/BTC', 1, 1, 123), ('XPR/BTC', -1, -2, -246) ] labels = ['currency', 'profit_ratio', 'profit_abs', 'trade_duration'] df = pd.DataFrame.from_records(trades, columns=labels) results_metrics = hyperopt._calculate_results_metrics(df) results_explanation = hyperopt._format_results_explanation_string(results_metrics) total_profit = results_metrics['total_profit'] results = { 'loss': 0.0, 'params_dict': None, 'params_details': None, 'results_metrics': results_metrics, 'results_explanation': results_explanation, 'total_profit': total_profit, 'current_epoch': 1, 'is_initial_point': True, } result = HyperoptTools._format_explanation_string(results, 1) assert result.find(' 66.67%') assert result.find('Total profit 1.00000000 BTC') assert result.find('2.0000Σ %') # Test with EUR as stake_currency trades = [ ('ETH/EUR', 2, 2, 123), ('LTC/EUR', 1, 1, 123), ('XPR/EUR', -1, -2, -246) ] df = pd.DataFrame.from_records(trades, columns=labels) results_metrics = hyperopt._calculate_results_metrics(df) results['total_profit'] = results_metrics['total_profit'] result = HyperoptTools._format_explanation_string(results, 1) assert result.find('Total profit 1.00000000 EUR')
def init_spaces(self): """ Assign the dimensions in the hyperoptimization space. """ if HyperoptTools.has_space(self.config, 'protection'): # Protections can only be optimized when using the Parameter interface logger.debug("Hyperopt has 'protection' space") # Enable Protections if protection space is selected. self.config['enable_protections'] = True self.protection_space = self.custom_hyperopt.protection_space() if HyperoptTools.has_space(self.config, 'buy'): logger.debug("Hyperopt has 'buy' space") self.buy_space = self.custom_hyperopt.buy_indicator_space() if HyperoptTools.has_space(self.config, 'sell'): logger.debug("Hyperopt has 'sell' space") self.sell_space = self.custom_hyperopt.sell_indicator_space() if HyperoptTools.has_space(self.config, 'roi'): logger.debug("Hyperopt has 'roi' space") self.roi_space = self.custom_hyperopt.roi_space() if HyperoptTools.has_space(self.config, 'stoploss'): logger.debug("Hyperopt has 'stoploss' space") self.stoploss_space = self.custom_hyperopt.stoploss_space() if HyperoptTools.has_space(self.config, 'trailing'): logger.debug("Hyperopt has 'trailing' space") self.trailing_space = self.custom_hyperopt.trailing_space() self.dimensions = (self.buy_space + self.sell_space + self.protection_space + self.roi_space + self.stoploss_space + self.trailing_space)
def start_hyperopt_list(args: Dict[str, Any]) -> None: """ List hyperopt epochs previously evaluated """ from freqtrade.optimize.hyperopt_tools import HyperoptTools config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) print_colorized = config.get('print_colorized', False) print_json = config.get('print_json', False) export_csv = config.get('export_csv', None) no_details = config.get('hyperopt_list_no_details', False) no_header = False filteroptions = { 'only_best': config.get('hyperopt_list_best', False), 'only_profitable': config.get('hyperopt_list_profitable', False), 'filter_min_trades': config.get('hyperopt_list_min_trades', 0), 'filter_max_trades': config.get('hyperopt_list_max_trades', 0), '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), 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None), 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None), '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), } results_file = get_latest_hyperopt_file( config['user_data_dir'] / 'hyperopt_results', config.get('hyperoptexportfilename')) # Previous evaluations epochs = HyperoptTools.load_previous_results(results_file) total_epochs = len(epochs) epochs = hyperopt_filter_epochs(epochs, filteroptions) if print_colorized: colorama_init(autoreset=True) if not export_csv: try: print(HyperoptTools.get_result_table(config, epochs, total_epochs, not filteroptions['only_best'], print_colorized, 0)) except KeyboardInterrupt: print('User interrupted..') if epochs and not no_details: sorted_epochs = sorted(epochs, key=itemgetter('loss')) results = sorted_epochs[0] HyperoptTools.show_epoch_details(results, total_epochs, print_json, no_header) if epochs and export_csv: HyperoptTools.export_csv_file( config, epochs, total_epochs, not filteroptions['only_best'], export_csv )
def test_print_epoch_details(capsys): test_result = { 'params_details': { 'trailing': { 'trailing_stop': True, 'trailing_stop_positive': 0.02, 'trailing_stop_positive_offset': 0.04, 'trailing_only_offset_is_reached': True }, 'roi': { 0: 0.18, 90: 0.14, 225: 0.05, 430: 0 }, }, 'results_explanation': 'foo result', 'is_initial_point': False, 'total_profit': 0, 'current_epoch': 2, # This starts from 1 (in a human-friendly manner) 'is_best': True } HyperoptTools.print_epoch_details(test_result, 5, False, no_header=True) captured = capsys.readouterr() assert '# Trailing stop:' in captured.out # re.match(r"Pairs for .*", captured.out) assert re.search(r'^\s+trailing_stop = True$', captured.out, re.MULTILINE) assert re.search(r'^\s+trailing_stop_positive = 0.02$', captured.out, re.MULTILINE) assert re.search(r'^\s+trailing_stop_positive_offset = 0.04$', captured.out, re.MULTILINE) assert re.search(r'^\s+trailing_only_offset_is_reached = True$', captured.out, re.MULTILINE) assert '# ROI table:' in captured.out assert re.search(r'^\s+minimal_roi = \{$', captured.out, re.MULTILINE) assert re.search(r'^\s+\"90\"\:\s0.14,\s*$', captured.out, re.MULTILINE)
def test_save_results_saves_epochs(hyperopt, tmpdir, caplog) -> None: hyperopt.results_file = Path(tmpdir / 'ut_results.fthypt') hyperopt_epochs = HyperoptTools.load_filtered_results( hyperopt.results_file, {}) assert log_has_re("Hyperopt file .* not found.", caplog) assert hyperopt_epochs == ([], 0) # Test writing to temp dir and reading again epochs = create_results() caplog.set_level(logging.DEBUG) for epoch in epochs: hyperopt._save_result(epoch) assert log_has(f"1 epoch saved to '{hyperopt.results_file}'.", caplog) hyperopt._save_result(epochs[0]) assert log_has(f"2 epochs saved to '{hyperopt.results_file}'.", caplog) hyperopt_epochs = HyperoptTools.load_filtered_results( hyperopt.results_file, {}) assert len(hyperopt_epochs) == 2 assert hyperopt_epochs[1] == 2 assert len(hyperopt_epochs[0]) == 2 result_gen = HyperoptTools._read_results(hyperopt.results_file, 1) epoch = next(result_gen) assert len(epoch) == 1 assert epoch[0] == epochs[0] epoch = next(result_gen) assert len(epoch) == 1 epoch = next(result_gen) assert len(epoch) == 0 with pytest.raises(StopIteration): next(result_gen)
def print_results(self, results) -> None: """ Log results if it is better than any previous evaluation TODO: this should be moved to HyperoptTools too """ is_best = results['is_best'] if self.print_all or is_best: print( HyperoptTools.get_result_table(self.config, results, self.total_epochs, self.print_all, self.print_colorized, self.hyperopt_table_header)) self.hyperopt_table_header = 2
def test_save_results_saves_epochs(hyperopt, tmpdir, caplog) -> None: # Test writing to temp dir and reading again epochs = create_results() hyperopt.results_file = Path(tmpdir / 'ut_results.fthypt') caplog.set_level(logging.DEBUG) for epoch in epochs: hyperopt._save_result(epoch) assert log_has(f"1 epoch saved to '{hyperopt.results_file}'.", caplog) hyperopt._save_result(epochs[0]) assert log_has(f"2 epochs saved to '{hyperopt.results_file}'.", caplog) hyperopt_epochs = HyperoptTools.load_previous_results(hyperopt.results_file) assert len(hyperopt_epochs) == 2
def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict: """ Used Optimize function. Called once per epoch to optimize whatever is configured. Keep this function as optimized as possible! """ backtest_start_time = datetime.now(timezone.utc) params_dict = self._get_params_dict(self.dimensions, raw_params) # Apply parameters if HyperoptTools.has_space(self.config, 'buy'): self.assign_params(params_dict, 'buy') if HyperoptTools.has_space(self.config, 'sell'): self.assign_params(params_dict, 'sell') if HyperoptTools.has_space(self.config, 'protection'): self.assign_params(params_dict, 'protection') if HyperoptTools.has_space(self.config, 'roi'): self.backtesting.strategy.minimal_roi = ( # type: ignore self.custom_hyperopt.generate_roi_table(params_dict)) if HyperoptTools.has_space(self.config, 'stoploss'): self.backtesting.strategy.stoploss = params_dict['stoploss'] if HyperoptTools.has_space(self.config, 'trailing'): d = self.custom_hyperopt.generate_trailing_params(params_dict) self.backtesting.strategy.trailing_stop = d['trailing_stop'] self.backtesting.strategy.trailing_stop_positive = d['trailing_stop_positive'] self.backtesting.strategy.trailing_stop_positive_offset = \ d['trailing_stop_positive_offset'] self.backtesting.strategy.trailing_only_offset_is_reached = \ d['trailing_only_offset_is_reached'] with self.data_pickle_file.open('rb') as f: processed = load(f, mmap_mode='r') bt_results = self.backtesting.backtest( processed=processed, start_date=self.min_date, end_date=self.max_date, max_open_trades=self.max_open_trades, position_stacking=self.position_stacking, enable_protections=self.config.get('enable_protections', False), ) backtest_end_time = datetime.now(timezone.utc) bt_results.update({ 'backtest_start_time': int(backtest_start_time.timestamp()), 'backtest_end_time': int(backtest_end_time.timestamp()), }) return self._get_results_dict(bt_results, self.min_date, self.max_date, params_dict, processed=processed)
def _get_results_dict(self, backtesting_results, min_date, max_date, params_dict, processed: Dict[str, DataFrame]) -> Dict[str, Any]: params_details = self._get_params_details(params_dict) strat_stats = generate_strategy_stats( processed, self.backtesting.strategy.get_strategy_name(), backtesting_results, min_date, max_date, market_change=0) results_explanation = HyperoptTools.format_results_explanation_string( strat_stats, self.config['stake_currency']) not_optimized = self.backtesting.strategy.get_no_optimize_params() not_optimized = deep_merge_dicts(not_optimized, self._get_no_optimize_details()) trade_count = strat_stats['total_trades'] total_profit = strat_stats['profit_total'] # If this evaluation contains too short amount of trades to be # interesting -- consider it as 'bad' (assigned max. loss value) # in order to cast this hyperspace point away from optimization # path. We do not want to optimize 'hodl' strategies. loss: float = MAX_LOSS if trade_count >= self.config['hyperopt_min_trades']: loss = self.calculate_loss(results=backtesting_results['results'], trade_count=trade_count, min_date=min_date, max_date=max_date, config=self.config, processed=processed, backtest_stats=strat_stats) return { 'loss': loss, 'params_dict': params_dict, 'params_details': params_details, 'params_not_optimized': not_optimized, 'results_metrics': strat_stats, 'results_explanation': results_explanation, 'total_profit': total_profit, }
def _get_params_details(self, params: Dict) -> Dict: """ Return the params for each space """ result: Dict = {} if HyperoptTools.has_space(self.config, 'buy'): result['buy'] = {p.name: params.get(p.name) for p in self.buy_space} if HyperoptTools.has_space(self.config, 'sell'): result['sell'] = {p.name: params.get(p.name) for p in self.sell_space} if HyperoptTools.has_space(self.config, 'protection'): result['protection'] = {p.name: params.get(p.name) for p in self.protection_space} if HyperoptTools.has_space(self.config, 'roi'): result['roi'] = {str(k): v for k, v in self.custom_hyperopt.generate_roi_table(params).items()} if HyperoptTools.has_space(self.config, 'stoploss'): result['stoploss'] = {p.name: params.get(p.name) for p in self.stoploss_space} if HyperoptTools.has_space(self.config, 'trailing'): result['trailing'] = self.custom_hyperopt.generate_trailing_params(params) return result
def start(self) -> None: self.random_state = self._set_random_state( self.config.get('hyperopt_random_state', None)) logger.info(f"Using optimizer random state: {self.random_state}") self.hyperopt_table_header = -1 # Initialize spaces ... self.init_spaces() self.prepare_hyperopt_data() # We don't need exchange instance anymore while running hyperopt self.backtesting.exchange.close() self.backtesting.exchange._api = None # type: ignore self.backtesting.exchange._api_async = None # type: ignore # self.backtesting.exchange = None # type: ignore self.backtesting.pairlists = None # type: ignore cpus = cpu_count() logger.info(f"Found {cpus} CPU cores. Let's make them scream!") config_jobs = self.config.get('hyperopt_jobs', -1) logger.info(f'Number of parallel jobs set as: {config_jobs}') self.opt = self.get_optimizer(self.dimensions, config_jobs) if self.print_colorized: colorama_init(autoreset=True) try: with Parallel(n_jobs=config_jobs) as parallel: jobs = parallel._effective_n_jobs() logger.info( f'Effective number of parallel workers used: {jobs}') # Define progressbar if self.print_colorized: widgets = [ ' [Epoch ', progressbar.Counter(), ' of ', str(self.total_epochs), ' (', progressbar.Percentage(), ')] ', progressbar.Bar(marker=progressbar.AnimatedMarker( fill='\N{FULL BLOCK}', fill_wrap=Fore.GREEN + '{}' + Fore.RESET, marker_wrap=Style.BRIGHT + '{}' + Style.RESET_ALL, )), ' [', progressbar.ETA(), ', ', progressbar.Timer(), ']', ] else: widgets = [ ' [Epoch ', progressbar.Counter(), ' of ', str(self.total_epochs), ' (', progressbar.Percentage(), ')] ', progressbar.Bar(marker=progressbar.AnimatedMarker( fill='\N{FULL BLOCK}', )), ' [', progressbar.ETA(), ', ', progressbar.Timer(), ']', ] with progressbar.ProgressBar(max_value=self.total_epochs, redirect_stdout=False, redirect_stderr=False, widgets=widgets) as pbar: EVALS = ceil(self.total_epochs / jobs) for i in range(EVALS): # Correct the number of epochs to be processed for the last # iteration (should not exceed self.total_epochs in total) n_rest = (i + 1) * jobs - self.total_epochs current_jobs = jobs - n_rest if n_rest > 0 else jobs asked = self.opt.ask(n_points=current_jobs) f_val = self.run_optimizer_parallel(parallel, asked, i) self.opt.tell(asked, [v['loss'] for v in f_val]) # Calculate progressbar outputs for j, val in enumerate(f_val): # Use human-friendly indexes here (starting from 1) current = i * jobs + j + 1 val['current_epoch'] = current val['is_initial_point'] = current <= INITIAL_POINTS logger.debug(f"Optimizer epoch evaluated: {val}") is_best = HyperoptTools.is_best_loss( val, self.current_best_loss) # This value is assigned here and not in the optimization method # to keep proper order in the list of results. That's because # evaluations can take different time. Here they are aligned in the # order they will be shown to the user. val['is_best'] = is_best self.print_results(val) if is_best: self.current_best_loss = val['loss'] self.current_best_epoch = val self._save_result(val) pbar.update(current) except KeyboardInterrupt: print('User interrupted..') logger.info( f"{self.num_epochs_saved} {plural(self.num_epochs_saved, 'epoch')} " f"saved to '{self.results_file}'.") if self.current_best_epoch: HyperoptTools.try_export_params( self.config, self.backtesting.strategy.get_strategy_name(), self.current_best_epoch) HyperoptTools.show_epoch_details(self.current_best_epoch, self.total_epochs, self.print_json) else: # This is printed when Ctrl+C is pressed quickly, before first epochs have # a chance to be evaluated. print("No epochs evaluated yet, no best result.")
def start_hyperopt_show(args: Dict[str, Any]) -> None: """ Show details of a hyperopt epoch previously evaluated """ from freqtrade.optimize.hyperopt_tools import HyperoptTools config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) print_json = config.get('print_json', False) no_header = config.get('hyperopt_show_no_header', False) results_file = get_latest_hyperopt_file( config['user_data_dir'] / 'hyperopt_results', config.get('hyperoptexportfilename')) n = config.get('hyperopt_show_index', -1) filteroptions = { 'only_best': config.get('hyperopt_list_best', False), 'only_profitable': config.get('hyperopt_list_profitable', False), 'filter_min_trades': config.get('hyperopt_list_min_trades', 0), 'filter_max_trades': config.get('hyperopt_list_max_trades', 0), '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), 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None), 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None), '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) } # Previous evaluations epochs = HyperoptTools.load_previous_results(results_file) total_epochs = len(epochs) epochs = hyperopt_filter_epochs(epochs, filteroptions) filtered_epochs = len(epochs) if n > filtered_epochs: raise OperationalException( f"The index of the epoch to show should be less than {filtered_epochs + 1}." ) if n < -filtered_epochs: raise OperationalException( f"The index of the epoch to show should be greater than {-filtered_epochs - 1}." ) # Translate epoch index from human-readable format to pythonic if n > 0: n -= 1 if epochs: val = epochs[n] metrics = val['results_metrics'] if 'strategy_name' in metrics: strategy_name = metrics['strategy_name'] show_backtest_result(strategy_name, metrics, metrics['stake_currency']) HyperoptTools.try_export_params(config, strategy_name, val) HyperoptTools.show_epoch_details(val, total_epochs, print_json, no_header, header_str="Epoch details")
def start(self) -> None: self.random_state = self._set_random_state( self.config.get('hyperopt_random_state', None)) logger.info(f"Using optimizer random state: {self.random_state}") self.hyperopt_table_header = -1 data, timerange = self.backtesting.load_bt_data() logger.info("Dataload complete. Calculating indicators") preprocessed = self.backtesting.strategy.ohlcvdata_to_dataframe(data) # Trim startup period from analyzed dataframe for pair, df in preprocessed.items(): preprocessed[pair] = trim_dataframe( df, timerange, startup_candles=self.backtesting.required_startup) min_date, max_date = get_timerange(preprocessed) logger.info( f'Hyperopting with data from {min_date.strftime(DATETIME_PRINT_FORMAT)} ' f'up to {max_date.strftime(DATETIME_PRINT_FORMAT)} ' f'({(max_date - min_date).days} days)..') dump(preprocessed, self.data_pickle_file) # We don't need exchange instance anymore while running hyperopt self.backtesting.exchange.close() self.backtesting.exchange._api = None # type: ignore self.backtesting.exchange._api_async = None # type: ignore # self.backtesting.exchange = None # type: ignore self.backtesting.pairlists = None # type: ignore self.backtesting.strategy.dp = None # type: ignore IStrategy.dp = None # type: ignore cpus = cpu_count() logger.info(f"Found {cpus} CPU cores. Let's make them scream!") config_jobs = self.config.get('hyperopt_jobs', -1) logger.info(f'Number of parallel jobs set as: {config_jobs}') self.dimensions: List[Dimension] = self.hyperopt_space() self.opt = self.get_optimizer(self.dimensions, config_jobs) if self.print_colorized: colorama_init(autoreset=True) try: with Parallel(n_jobs=config_jobs) as parallel: jobs = parallel._effective_n_jobs() logger.info( f'Effective number of parallel workers used: {jobs}') # Define progressbar if self.print_colorized: widgets = [ ' [Epoch ', progressbar.Counter(), ' of ', str(self.total_epochs), ' (', progressbar.Percentage(), ')] ', progressbar.Bar(marker=progressbar.AnimatedMarker( fill='\N{FULL BLOCK}', fill_wrap=Fore.GREEN + '{}' + Fore.RESET, marker_wrap=Style.BRIGHT + '{}' + Style.RESET_ALL, )), ' [', progressbar.ETA(), ', ', progressbar.Timer(), ']', ] else: widgets = [ ' [Epoch ', progressbar.Counter(), ' of ', str(self.total_epochs), ' (', progressbar.Percentage(), ')] ', progressbar.Bar(marker=progressbar.AnimatedMarker( fill='\N{FULL BLOCK}', )), ' [', progressbar.ETA(), ', ', progressbar.Timer(), ']', ] with progressbar.ProgressBar(max_value=self.total_epochs, redirect_stdout=False, redirect_stderr=False, widgets=widgets) as pbar: EVALS = ceil(self.total_epochs / jobs) for i in range(EVALS): # Correct the number of epochs to be processed for the last # iteration (should not exceed self.total_epochs in total) n_rest = (i + 1) * jobs - self.total_epochs current_jobs = jobs - n_rest if n_rest > 0 else jobs asked = self.opt.ask(n_points=current_jobs) f_val = self.run_optimizer_parallel(parallel, asked, i) self.opt.tell(asked, [v['loss'] for v in f_val]) # Calculate progressbar outputs for j, val in enumerate(f_val): # Use human-friendly indexes here (starting from 1) current = i * jobs + j + 1 val['current_epoch'] = current val['is_initial_point'] = current <= INITIAL_POINTS logger.debug(f"Optimizer epoch evaluated: {val}") is_best = HyperoptTools.is_best_loss( val, self.current_best_loss) # This value is assigned here and not in the optimization method # to keep proper order in the list of results. That's because # evaluations can take different time. Here they are aligned in the # order they will be shown to the user. val['is_best'] = is_best self.print_results(val) if is_best: self.current_best_loss = val['loss'] self.epochs.append(val) # Save results after each best epoch and every 100 epochs if is_best or current % 100 == 0: self._save_results() pbar.update(current) except KeyboardInterrupt: print('User interrupted..') self._save_results() logger.info( f"{self.num_epochs_saved} {plural(self.num_epochs_saved, 'epoch')} " f"saved to '{self.results_file}'.") if self.epochs: sorted_epochs = sorted(self.epochs, key=itemgetter('loss')) best_epoch = sorted_epochs[0] HyperoptTools.print_epoch_details(best_epoch, self.total_epochs, self.print_json) else: # This is printed when Ctrl+C is pressed quickly, before first epochs have # a chance to be evaluated. print("No epochs evaluated yet, no best result.")
def test_has_space(hyperopt_conf, spaces, expected_results): for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']: hyperopt_conf.update({'spaces': spaces}) assert HyperoptTools.has_space(hyperopt_conf, s) == expected_results[s]
def test_hyperopt_format_results(hyperopt): bt_result = { 'results': pd.DataFrame({ "pair": ["UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC"], "profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780], "profit_abs": [0.000003, 0.000011, 0.000014, 0.000003], "open_date": [ Arrow(2017, 11, 14, 19, 32, 00).datetime, Arrow(2017, 11, 14, 21, 36, 00).datetime, Arrow(2017, 11, 14, 22, 12, 00).datetime, Arrow(2017, 11, 14, 22, 44, 00).datetime ], "close_date": [ Arrow(2017, 11, 14, 21, 35, 00).datetime, Arrow(2017, 11, 14, 22, 10, 00).datetime, Arrow(2017, 11, 14, 22, 43, 00).datetime, Arrow(2017, 11, 14, 22, 58, 00).datetime ], "open_rate": [0.002543, 0.003003, 0.003089, 0.003214], "close_rate": [0.002546, 0.003014, 0.003103, 0.003217], "trade_duration": [123, 34, 31, 14], "is_open": [False, False, False, True], "stake_amount": [0.01, 0.01, 0.01, 0.01], "sell_reason": [ SellType.ROI, SellType.STOP_LOSS, SellType.ROI, SellType.FORCE_SELL ] }), 'config': hyperopt.config, 'locks': [], 'final_balance': 0.02, 'backtest_start_time': 1619718665, 'backtest_end_time': 1619718665, } results_metrics = generate_strategy_stats({'XRP/BTC': None}, '', bt_result, Arrow(2017, 11, 14, 19, 32, 00), Arrow(2017, 12, 14, 19, 32, 00), market_change=0) results_explanation = HyperoptTools.format_results_explanation_string( results_metrics, 'BTC') total_profit = results_metrics['profit_total_abs'] results = { 'loss': 0.0, 'params_dict': None, 'params_details': None, 'results_metrics': results_metrics, 'results_explanation': results_explanation, 'total_profit': total_profit, 'current_epoch': 1, 'is_initial_point': True, } result = HyperoptTools._format_explanation_string(results, 1) assert ' 0.71%' in result assert 'Total profit 0.00003100 BTC' in result assert '0:50:00 min' in result
def test_load_previous_results2(mocker, testdatadir, caplog) -> None: mocker.patch('freqtrade.optimize.hyperopt_tools.HyperoptTools._read_results_pickle', return_value=[{'asdf': '222'}]) results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle' with pytest.raises(OperationalException, match=r"The file .* incompatible.*"): HyperoptTools.load_previous_results(results_file)
def __init__(self, config: Dict[str, Any]) -> None: self.buy_space: List[Dimension] = [] self.sell_space: List[Dimension] = [] self.roi_space: List[Dimension] = [] self.stoploss_space: List[Dimension] = [] self.trailing_space: List[Dimension] = [] self.dimensions: List[Dimension] = [] self.config = config self.backtesting = Backtesting(self.config) if not self.config.get('hyperopt'): self.custom_hyperopt = HyperOptAuto(self.config) else: self.custom_hyperopt = HyperOptResolver.load_hyperopt(self.config) self.custom_hyperopt.strategy = self.backtesting.strategy self.custom_hyperoptloss = HyperOptLossResolver.load_hyperoptloss( self.config) self.calculate_loss = self.custom_hyperoptloss.hyperopt_loss_function time_now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") strategy = str(self.config['strategy']) self.results_file: Path = ( self.config['user_data_dir'] / 'hyperopt_results' / f'strategy_{strategy}_hyperopt_results_{time_now}.pickle') self.data_pickle_file = (self.config['user_data_dir'] / 'hyperopt_results' / 'hyperopt_tickerdata.pkl') self.total_epochs = config.get('epochs', 0) self.current_best_loss = 100 self.clean_hyperopt() self.num_epochs_saved = 0 # Previous evaluations self.epochs: List = [] # Populate functions here (hasattr is slow so should not be run during "regular" operations) if hasattr(self.custom_hyperopt, 'populate_indicators'): self.backtesting.strategy.advise_indicators = ( # type: ignore self.custom_hyperopt.populate_indicators) # type: ignore if hasattr(self.custom_hyperopt, 'populate_buy_trend'): self.backtesting.strategy.advise_buy = ( # type: ignore self.custom_hyperopt.populate_buy_trend) # type: ignore if hasattr(self.custom_hyperopt, 'populate_sell_trend'): self.backtesting.strategy.advise_sell = ( # type: ignore self.custom_hyperopt.populate_sell_trend) # type: ignore # Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set if self.config.get('use_max_market_positions', True): self.max_open_trades = self.config['max_open_trades'] else: logger.debug( 'Ignoring max_open_trades (--disable-max-market-positions was used) ...' ) self.max_open_trades = 0 self.position_stacking = self.config.get('position_stacking', False) if HyperoptTools.has_space(self.config, 'sell'): # Make sure use_sell_signal is enabled if 'ask_strategy' not in self.config: self.config['ask_strategy'] = {} self.config['ask_strategy']['use_sell_signal'] = True self.print_all = self.config.get('print_all', False) self.hyperopt_table_header = 0 self.print_colorized = self.config.get('print_colorized', False) self.print_json = self.config.get('print_json', False)
def test_load_previous_results2(mocker, testdatadir, caplog) -> None: results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle' with pytest.raises(OperationalException, match=r"Legacy hyperopt results are no longer supported.*"): HyperoptTools.load_filtered_results(results_file, {})