Merge pull request #2043 from freqtrade/combine/resolvers
Combine/resolvers
This commit is contained in:
commit
41f24898e5
@ -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
|
||||||
|
@ -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 "
|
||||||
|
@ -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
|
||||||
|
@ -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."
|
||||||
|
@ -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 "
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user