From ba92e09b7bab024fb7ea7a12d1b3df22cf33fc6a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 Apr 2022 09:11:50 +0200 Subject: [PATCH] list-strategies should find recursively as well --- config_examples/config_full.example.json | 1 + freqtrade/commands/list_commands.py | 9 +++++---- freqtrade/optimize/hyperopt_tools.py | 3 ++- freqtrade/resolvers/iresolver.py | 11 +++++++++-- freqtrade/resolvers/strategy_resolver.py | 2 +- freqtrade/rpc/api_server/api_v1.py | 3 ++- 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index 7931476b4..8f14e1771 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -179,6 +179,7 @@ "disable_dataframe_checks": false, "strategy": "SampleStrategy", "strategy_path": "user_data/strategies/", + "recursive_strategy_search": false, "dataformat_ohlcv": "json", "dataformat_trades": "jsongz" } diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index 38fb098a0..1833db922 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -41,7 +41,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: print(tabulate(exchanges, headers=['Exchange name', 'Valid', 'reason'])) -def _print_objs_tabular(objs: List, print_colorized: bool) -> None: +def _print_objs_tabular(objs: List, print_colorized: bool, base_dir: Path) -> None: if print_colorized: colorama_init(autoreset=True) red = Fore.RED @@ -55,7 +55,7 @@ def _print_objs_tabular(objs: List, print_colorized: bool) -> None: names = [s['name'] for s in objs] objs_to_print = [{ 'name': s['name'] if s['name'] else "--", - 'location': s['location'].name, + 'location': s['location'].relative_to(base_dir), 'status': (red + "LOAD FAILED" + reset if s['class'] is None else "OK" if names.count(s['name']) == 1 else yellow + "DUPLICATE NAME" + reset) @@ -77,7 +77,8 @@ def start_list_strategies(args: Dict[str, Any]) -> None: config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) directory = Path(config.get('strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) - strategy_objs = StrategyResolver.search_all_objects(directory, not args['print_one_column']) + strategy_objs = StrategyResolver.search_all_objects( + directory, not args['print_one_column'], config.get('recursive_strategy_search', False)) # Sort alphabetically strategy_objs = sorted(strategy_objs, key=lambda x: x['name']) for obj in strategy_objs: @@ -89,7 +90,7 @@ def start_list_strategies(args: Dict[str, Any]) -> None: if args['print_one_column']: print('\n'.join([s['name'] for s in strategy_objs])) else: - _print_objs_tabular(strategy_objs, config.get('print_colorized', False)) + _print_objs_tabular(strategy_objs, config.get('print_colorized', False), directory) def start_list_timeframes(args: Dict[str, Any]) -> None: diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 8c84f772a..e836681c5 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -41,7 +41,8 @@ class HyperoptTools(): """ from freqtrade.resolvers.strategy_resolver import StrategyResolver directory = Path(config.get('strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) - strategy_objs = StrategyResolver.search_all_objects(directory, False) + strategy_objs = StrategyResolver.search_all_objects( + directory, False, config.get('recursive_strategy_search', False)) strategies = [s for s in strategy_objs if s['name'] == strategy_name] if strategies: strategy = strategies[0] diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index d310856d8..74b28dffe 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -182,18 +182,25 @@ class IResolver: ) @classmethod - def search_all_objects(cls, directory: Path, - enum_failed: bool) -> List[Dict[str, Any]]: + def search_all_objects(cls, directory: Path, enum_failed: bool, + recursive: bool = False) -> List[Dict[str, Any]]: """ Searches a directory for valid objects :param directory: Path to search :param enum_failed: If True, will return None for modules which fail. Otherwise, failing modules are skipped. + :param recursive: Recursively walk directory tree searching for strategies :return: List of dicts containing 'name', 'class' and 'location' entries """ logger.debug(f"Searching for {cls.object_type.__name__} '{directory}'") objects = [] for entry in directory.iterdir(): + if ( + recursive and entry.is_dir() + and not entry.name.startswith('__') + and not entry.name.startswith('.') + ): + objects.extend(cls.search_all_objects(entry, enum_failed, recursive=recursive)) # Only consider python files if entry.suffix != '.py': logger.debug('Ignoring %s', entry) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 8a22dbd65..60961b15b 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -167,7 +167,7 @@ class StrategyResolver(IResolver): :param extra_dir: additional directory to search for the given strategy :return: Strategy instance or None """ - if 'recursive_strategy_search' in config and config['recursive_strategy_search']: + if config.get('recursive_strategy_search', False): extra_dirs: List[str] = [ path[0] for path in walk(f"{config['user_data_dir']}/{USERPATH_STRATEGIES}") ] # sub-directories diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 5a34385da..fe6426178 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -243,7 +243,8 @@ def list_strategies(config=Depends(get_config)): directory = Path(config.get( 'strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) from freqtrade.resolvers.strategy_resolver import StrategyResolver - strategies = StrategyResolver.search_all_objects(directory, False) + strategies = StrategyResolver.search_all_objects( + directory, False, config.get('recursive_strategy_search', False)) strategies = sorted(strategies, key=lambda x: x['name']) return {'strategies': [x['name'] for x in strategies]}