diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index fa4bc1066..167847a0d 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -54,15 +54,21 @@ def _print_objs_tabular(objs: List, print_colorized: bool) -> None: reset = '' names = [s['name'] for s in objs] - objss_to_print = [{ + objs_to_print = [{ 'name': s['name'] if s['name'] else "--", 'location': s['location'].name, 'status': (red + "LOAD FAILED" + reset if s['class'] is None else "OK" if names.count(s['name']) == 1 else yellow + "DUPLICATE NAME" + reset) } for s in objs] - - print(tabulate(objss_to_print, headers='keys', tablefmt='psql', stralign='right')) + for idx, s in enumerate(objs): + if 'hyperoptable' in s: + objs_to_print[idx].update({ + 'hyperoptable': "Yes" if s['hyperoptable']['count'] > 0 else "No", + 'buy-Params': len(s['hyperoptable'].get('buy', [])), + 'sell-Params': len(s['hyperoptable'].get('sell', [])), + }) + print(tabulate(objs_to_print, headers='keys', tablefmt='psql', stralign='right')) def start_list_strategies(args: Dict[str, Any]) -> None: @@ -75,6 +81,11 @@ def start_list_strategies(args: Dict[str, Any]) -> None: strategy_objs = StrategyResolver.search_all_objects(directory, not args['print_one_column']) # Sort alphabetically strategy_objs = sorted(strategy_objs, key=lambda x: x['name']) + for obj in strategy_objs: + if obj['class']: + obj['hyperoptable'] = obj['class'].detect_all_parameters() + else: + obj['hyperoptable'] = {'count': 0} if args['print_one_column']: print('\n'.join([s['name'] for s in strategy_objs])) diff --git a/freqtrade/strategy/hyper.py b/freqtrade/strategy/hyper.py index 7dee47d87..a320f2bb8 100644 --- a/freqtrade/strategy/hyper.py +++ b/freqtrade/strategy/hyper.py @@ -273,11 +273,12 @@ class HyperStrategyMixin(object): for par in params: yield par.name, par - def _detect_parameters(self, category: str) -> Iterator[Tuple[str, BaseParameter]]: + @classmethod + def detect_parameters(cls, category: str) -> Iterator[Tuple[str, BaseParameter]]: """ Detect all parameters for 'category' """ - for attr_name in dir(self): + for attr_name in dir(cls): if not attr_name.startswith('__'): # Ignore internals, not strictly necessary. - attr = getattr(self, attr_name) + attr = getattr(cls, attr_name) if issubclass(attr.__class__, BaseParameter): if (attr_name.startswith(category + '_') and attr.category is not None and attr.category != category): @@ -287,6 +288,19 @@ class HyperStrategyMixin(object): (attr_name.startswith(category + '_') and attr.category is None)): yield attr_name, attr + @classmethod + def detect_all_parameters(cls) -> Dict: + """ Detect all parameters and return them as a list""" + params: Dict = { + 'buy': list(cls.detect_parameters('buy')), + 'sell': list(cls.detect_parameters('sell')), + } + params.update({ + 'count': len(params['buy'] + params['sell']) + }) + + return params + def _load_hyper_params(self, hyperopt: bool = False) -> None: """ Load Hyperoptable parameters @@ -303,7 +317,7 @@ class HyperStrategyMixin(object): 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): + 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: diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index ded396779..df487986f 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -667,8 +667,13 @@ def test_auto_hyperopt_interface(default_conf): # Parameter is disabled - so value from sell_param dict will NOT be used. assert strategy.sell_minusdi.value == 0.5 + all_params = strategy.detect_all_parameters() + assert isinstance(all_params, dict) + assert len(all_params['buy']) == 2 + assert len(all_params['sell']) == 2 + assert all_params['count'] == 4 - strategy.sell_rsi = IntParameter([0, 10], default=5, space='buy') + strategy.__class__.sell_rsi = IntParameter([0, 10], default=5, space='buy') with pytest.raises(OperationalException, match=r"Inconclusive parameter.*"): - [x for x in strategy._detect_parameters('sell')] + [x for x in strategy.detect_parameters('sell')]