From 2ab989e274c5bfb278e8b6397380b1714d806a80 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Dec 2019 15:28:35 +0100 Subject: [PATCH] Cleanup some code and add option --- freqtrade/configuration/arguments.py | 2 +- freqtrade/resolvers/iresolver.py | 35 +++++++++++++++++++++++----- freqtrade/utils.py | 14 +++++++++-- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 5f7bc74f1..b2197619d 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -30,7 +30,7 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] -ARGS_LIST_STRATEGIES = ["strategy_path"] +ARGS_LIST_STRATEGIES = ["strategy_path", "print_one_column"] ARGS_LIST_EXCHANGES = ["print_one_column", "list_exchanges_all"] diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index bbdc8ca91..01ecbcb84 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -7,7 +7,7 @@ import importlib.util import inspect import logging from pathlib import Path -from typing import Any, Generator, List, Optional, Tuple, Type, Union +from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union from freqtrade import OperationalException @@ -41,7 +41,7 @@ class IResolver: @classmethod def _get_valid_object(cls, module_path: Path, - object_name: str) -> Generator[Any, None, None]: + object_name: Optional[str]) -> Generator[Any, None, None]: """ Generator returning objects with matching object_type and object_name in the path given. :param module_path: absolute path to the module @@ -51,7 +51,7 @@ class IResolver: # Generate spec based on absolute path # Pass object_name as first argument to have logging print a reasonable name. - spec = importlib.util.spec_from_file_location(object_name, str(module_path)) + spec = importlib.util.spec_from_file_location(object_name or "", str(module_path)) module = importlib.util.module_from_spec(spec) try: spec.loader.exec_module(module) # type: ignore # importlib does not use typehints @@ -61,7 +61,7 @@ class IResolver: valid_objects_gen = ( obj for name, obj in inspect.getmembers(module, inspect.isclass) - if object_name == name and cls.object_type in obj.__bases__ + if (object_name is None or object_name == name) and cls.object_type in obj.__bases__ ) return valid_objects_gen @@ -74,8 +74,7 @@ class IResolver: :param object_name: ClassName of the object to load :return: object class """ - logger.debug("Searching for %s %s in '%s'", - cls.object_type.__name__, object_name, directory) + logger.debug(f"Searching for {cls.object_type.__name__} {object_name} in '{directory}'") for entry in directory.iterdir(): # Only consider python files if not str(entry).endswith('.py'): @@ -134,3 +133,27 @@ class IResolver: f"Impossible to load {cls.object_type_str} '{object_name}'. This class does not exist " "or contains Python code errors." ) + + @classmethod + def search_all_objects(cls, directory: Path) -> List[Dict[str, Any]]: + """ + Searches a directory for valid objects + :param directory: Path to search + :return: List of dicts containing 'name', 'class' and 'location' entires + """ + logger.debug(f"Searching for {cls.object_type.__name__} '{directory}'") + objects = [] + for entry in directory.iterdir(): + # Only consider python files + if not str(entry).endswith('.py'): + logger.debug('Ignoring %s', entry) + continue + module_path = entry.resolve() + logger.info(f"Path {module_path}") + for obj in cls._get_valid_object(module_path, object_name=None): + objects.append( + {'name': obj.__name__, + 'class': obj, + 'location': entry, + }) + return objects diff --git a/freqtrade/utils.py b/freqtrade/utils.py index c5fc47a74..06a62172a 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -23,7 +23,7 @@ from freqtrade.data.history import (convert_trades_to_ohlcv, from freqtrade.exchange import (available_exchanges, ccxt_exchanges, market_is_active, symbol_is_pair) from freqtrade.misc import plural, render_template -from freqtrade.resolvers import ExchangeResolver +from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.state import RunMode logger = logging.getLogger(__name__) @@ -229,7 +229,17 @@ def start_list_strategies(args: Dict[str, Any]) -> None: Print Strategies available in a folder """ config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) - print(config) + + directory = Path(config.get('strategy_path', config['user_data_dir'] / USERPATH_STRATEGY)) + strategies = StrategyResolver.search_all_objects(directory) + # Sort alphabetically + strategies = sorted(strategies, key=lambda x: x['name']) + strats_to_print = [{'name': s['name'], 'location': s['location']} for s in strategies] + + if args['print_one_column']: + print('\n'.join([s['name'] for s in strategies])) + else: + print(tabulate(strats_to_print, headers='keys', tablefmt='pipe')) def start_list_timeframes(args: Dict[str, Any]) -> None: