Merge pull request #2043 from freqtrade/combine/resolvers

Combine/resolvers
This commit is contained in:
Matthias 2019-07-22 06:19:31 +02:00 committed by GitHub
commit 41f24898e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 62 deletions

View File

@ -32,11 +32,11 @@ jobs:
name: pytest name: pytest
- script: - script:
- cp config.json.example config.json - cp config.json.example config.json
- python freqtrade --datadir freqtrade/tests/testdata backtesting - freqtrade --datadir freqtrade/tests/testdata backtesting
name: backtest name: backtest
- script: - script:
- cp config.json.example config.json - 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 name: hyperopt
- script: flake8 freqtrade scripts - script: flake8 freqtrade scripts
name: flake8 name: flake8

View File

@ -23,12 +23,11 @@ class HyperOptResolver(IResolver):
__slots__ = ['hyperopt'] __slots__ = ['hyperopt']
def __init__(self, config: Optional[Dict] = None) -> None: def __init__(self, config: Dict) -> None:
""" """
Load the custom class from config parameter 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 # Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt
hyperopt_name = config.get('hyperopt') or 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 # Add extra hyperopt directory on top of search paths
abs_paths.insert(0, Path(extra_dir)) abs_paths.insert(0, Path(extra_dir))
for _path in abs_paths: hyperopt = self._load_object(paths=abs_paths, object_type=IHyperOpt,
try: object_name=hyperopt_name)
(hyperopt, module_path) = self._search_object(directory=_path, if hyperopt:
object_type=IHyperOpt, return hyperopt
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()))
raise OperationalException( raise OperationalException(
f"Impossible to load Hyperopt '{hyperopt_name}'. This class does not exist " f"Impossible to load Hyperopt '{hyperopt_name}'. This class does not exist "
"or contains Python code errors." "or contains Python code errors."
@ -125,17 +117,10 @@ class HyperOptLossResolver(IResolver):
# Add extra hyperopt directory on top of search paths # Add extra hyperopt directory on top of search paths
abs_paths.insert(0, Path(extra_dir)) abs_paths.insert(0, Path(extra_dir))
for _path in abs_paths: hyperoptloss = self._load_object(paths=abs_paths, object_type=IHyperOptLoss,
try: object_name=hyper_loss_name)
(hyperoptloss, module_path) = self._search_object(directory=_path, if hyperoptloss:
object_type=IHyperOptLoss, return hyperoptloss
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()))
raise OperationalException( raise OperationalException(
f"Impossible to load HyperoptLoss '{hyper_loss_name}'. This class does not exist " f"Impossible to load HyperoptLoss '{hyper_loss_name}'. This class does not exist "

View File

@ -7,7 +7,7 @@ import importlib.util
import inspect import inspect
import logging import logging
from pathlib import Path 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__) logger = logging.getLogger(__name__)
@ -64,3 +64,26 @@ class IResolver(object):
if obj: if obj:
return (obj(**kwargs), module_path) return (obj(**kwargs), module_path)
return (None, None) 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

View File

@ -43,18 +43,10 @@ class PairListResolver(IResolver):
current_path, current_path,
] ]
for _path in abs_paths: pairlist = self._load_object(paths=abs_paths, object_type=IPairList,
try: object_name=pairlist_name, kwargs=kwargs)
(pairlist, module_path) = self._search_object(directory=_path, if pairlist:
object_type=IPairList, return pairlist
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()))
raise OperationalException( raise OperationalException(
f"Impossible to load Pairlist '{pairlist_name}'. This class does not exist " f"Impossible to load Pairlist '{pairlist_name}'. This class does not exist "
"or contains Python code errors." "or contains Python code errors."

View File

@ -147,26 +147,19 @@ class StrategyResolver(IResolver):
# register temp path with the bot # register temp path with the bot
abs_paths.insert(0, temp.resolve()) 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: try:
(strategy, module_path) = self._search_object(directory=_path, return import_strategy(strategy, config=config)
object_type=IStrategy, except TypeError as e:
object_name=strategy_name, logger.warning(
kwargs={'config': config}) f"Impossible to load strategy '{strategy_name}'. "
if strategy: f"Error: {e}")
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()))
raise OperationalException( raise OperationalException(
f"Impossible to load Strategy '{strategy_name}'. This class does not exist " f"Impossible to load Strategy '{strategy_name}'. This class does not exist "

View File

@ -1,5 +1,6 @@
# pragma pylint: disable=missing-docstring, protected-access, C0103 # pragma pylint: disable=missing-docstring, protected-access, C0103
import logging import logging
import tempfile
import warnings import warnings
from base64 import urlsafe_b64encode from base64 import urlsafe_b64encode
from os import path from os import path
@ -44,7 +45,7 @@ def test_import_strategy(caplog):
def test_search_strategy(): def test_search_strategy():
default_config = {} 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( s, _ = StrategyResolver._search_object(
directory=default_location, directory=default_location,
@ -68,11 +69,15 @@ def test_load_strategy(result):
assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'})
def test_load_strategy_base64(result): def test_load_strategy_base64(result, caplog):
with open("freqtrade/tests/strategy/test_strategy.py", "rb") as file: with open("user_data/strategies/test_strategy.py", "rb") as file:
encoded_string = urlsafe_b64encode(file.read()).decode("utf-8") encoded_string = urlsafe_b64encode(file.read()).decode("utf-8")
resolver = StrategyResolver({'strategy': 'TestStrategy:{}'.format(encoded_string)}) resolver = StrategyResolver({'strategy': 'TestStrategy:{}'.format(encoded_string)})
assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) 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): def test_load_strategy_invalid_directory(result, caplog):