diff --git a/.travis.yml b/.travis.yml index 33a45b280..b44fef7a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,11 +32,11 @@ jobs: name: pytest - script: - cp config.json.example config.json - - python freqtrade --datadir freqtrade/tests/testdata backtesting + - freqtrade --datadir freqtrade/tests/testdata backtesting name: backtest - script: - cp config.json.example config.json - - python freqtrade --datadir freqtrade/tests/testdata hyperopt -e 5 + - freqtrade --datadir freqtrade/tests/testdata hyperopt -e 5 name: hyperopt - script: flake8 freqtrade scripts name: flake8 diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 42e5ff31c..74412e738 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -23,12 +23,11 @@ class HyperOptResolver(IResolver): __slots__ = ['hyperopt'] - def __init__(self, config: Optional[Dict] = None) -> None: + def __init__(self, config: Dict) -> None: """ Load the custom class from config parameter - :param config: configuration dictionary or None + :param config: configuration dictionary """ - config = config or {} # Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt hyperopt_name = config.get('hyperopt') or DEFAULT_HYPEROPT @@ -63,17 +62,10 @@ class HyperOptResolver(IResolver): # Add extra hyperopt directory on top of search paths abs_paths.insert(0, Path(extra_dir)) - for _path in abs_paths: - try: - (hyperopt, module_path) = self._search_object(directory=_path, - object_type=IHyperOpt, - object_name=hyperopt_name) - if hyperopt: - logger.info(f"Using resolved hyperopt {hyperopt_name} from '{module_path}'...") - return hyperopt - except FileNotFoundError: - logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd())) - + hyperopt = self._load_object(paths=abs_paths, object_type=IHyperOpt, + object_name=hyperopt_name) + if hyperopt: + return hyperopt raise OperationalException( f"Impossible to load Hyperopt '{hyperopt_name}'. This class does not exist " "or contains Python code errors." @@ -125,17 +117,10 @@ class HyperOptLossResolver(IResolver): # Add extra hyperopt directory on top of search paths abs_paths.insert(0, Path(extra_dir)) - for _path in abs_paths: - try: - (hyperoptloss, module_path) = self._search_object(directory=_path, - object_type=IHyperOptLoss, - object_name=hyper_loss_name) - if hyperoptloss: - logger.info( - f"Using resolved hyperopt {hyper_loss_name} from '{module_path}'...") - return hyperoptloss - except FileNotFoundError: - logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd())) + hyperoptloss = self._load_object(paths=abs_paths, object_type=IHyperOptLoss, + object_name=hyper_loss_name) + if hyperoptloss: + return hyperoptloss raise OperationalException( f"Impossible to load HyperoptLoss '{hyper_loss_name}'. This class does not exist " diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index 1065abba7..9d0c97917 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, Optional, Tuple, Type, Union +from typing import Any, List, Optional, Tuple, Type, Union logger = logging.getLogger(__name__) @@ -64,3 +64,26 @@ class IResolver(object): if obj: return (obj(**kwargs), module_path) return (None, None) + + @staticmethod + def _load_object(paths: List[Path], object_type, object_name: str, + kwargs: dict = {}) -> Optional[Any]: + """ + Try to load object from path list. + """ + + for _path in paths: + try: + (module, module_path) = IResolver._search_object(directory=_path, + object_type=object_type, + object_name=object_name, + kwargs=kwargs) + if module: + logger.info( + f"Using resolved {object_type.__name__.lower()[1:]} {object_name} " + f"from '{module_path}'...") + return module + except FileNotFoundError: + logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd())) + + return None diff --git a/freqtrade/resolvers/pairlist_resolver.py b/freqtrade/resolvers/pairlist_resolver.py index 651a7d33f..3d95c0295 100644 --- a/freqtrade/resolvers/pairlist_resolver.py +++ b/freqtrade/resolvers/pairlist_resolver.py @@ -43,18 +43,10 @@ class PairListResolver(IResolver): current_path, ] - for _path in abs_paths: - try: - (pairlist, module_path) = self._search_object(directory=_path, - object_type=IPairList, - object_name=pairlist_name, - kwargs=kwargs) - if pairlist: - logger.info(f"Using resolved pairlist {pairlist_name} from '{module_path}'...") - return pairlist - except FileNotFoundError: - logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd())) - + pairlist = self._load_object(paths=abs_paths, object_type=IPairList, + object_name=pairlist_name, kwargs=kwargs) + if pairlist: + return pairlist raise OperationalException( f"Impossible to load Pairlist '{pairlist_name}'. This class does not exist " "or contains Python code errors." diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 4a5604db8..aa73327ff 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -147,26 +147,19 @@ class StrategyResolver(IResolver): # register temp path with the bot abs_paths.insert(0, temp.resolve()) - for _path in abs_paths: + strategy = self._load_object(paths=abs_paths, object_type=IStrategy, + object_name=strategy_name, kwargs={'config': config}) + if strategy: + strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args) + strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) + strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args) + try: - (strategy, module_path) = self._search_object(directory=_path, - object_type=IStrategy, - object_name=strategy_name, - kwargs={'config': config}) - if strategy: - logger.info(f"Using resolved strategy {strategy_name} from '{module_path}'...") - strategy._populate_fun_len = len( - getfullargspec(strategy.populate_indicators).args) - strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) - strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args) - try: - return import_strategy(strategy, config=config) - except TypeError as e: - logger.warning( - f"Impossible to load strategy '{strategy_name}' from {module_path}. " - f"Error: {e}") - except FileNotFoundError: - logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd())) + return import_strategy(strategy, config=config) + except TypeError as e: + logger.warning( + f"Impossible to load strategy '{strategy_name}'. " + f"Error: {e}") raise OperationalException( f"Impossible to load Strategy '{strategy_name}'. This class does not exist " diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 9a2c950e5..609cc58ff 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -1,5 +1,6 @@ # pragma pylint: disable=missing-docstring, protected-access, C0103 import logging +import tempfile import warnings from base64 import urlsafe_b64encode from os import path @@ -44,7 +45,7 @@ def test_import_strategy(caplog): def test_search_strategy(): default_config = {} - default_location = Path(__file__).parent.parent.joinpath('strategy').resolve() + default_location = Path(__file__).parent.parent.parent.joinpath('strategy').resolve() s, _ = StrategyResolver._search_object( directory=default_location, @@ -68,11 +69,15 @@ def test_load_strategy(result): assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) -def test_load_strategy_base64(result): - with open("freqtrade/tests/strategy/test_strategy.py", "rb") as file: +def test_load_strategy_base64(result, caplog): + with open("user_data/strategies/test_strategy.py", "rb") as file: encoded_string = urlsafe_b64encode(file.read()).decode("utf-8") resolver = StrategyResolver({'strategy': 'TestStrategy:{}'.format(encoded_string)}) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) + # Make sure strategy was loaded from base64 (using temp directory)!! + assert log_has_re(r"Using resolved strategy TestStrategy from '" + + tempfile.gettempdir() + r"/.*/TestStrategy\.py'\.\.\.", + caplog.record_tuples) def test_load_strategy_invalid_directory(result, caplog):