Combine load_object methods into one

This commit is contained in:
Matthias 2019-12-24 13:54:46 +01:00
parent eb1040ddb7
commit 25e6d6a7bf
5 changed files with 68 additions and 100 deletions

View File

@ -5,7 +5,7 @@ This module load custom hyperopt
""" """
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Optional, Dict from typing import Dict
from freqtrade import OperationalException from freqtrade import OperationalException
from freqtrade.constants import DEFAULT_HYPEROPT_LOSS, USERPATH_HYPEROPTS from freqtrade.constants import DEFAULT_HYPEROPT_LOSS, USERPATH_HYPEROPTS
@ -21,6 +21,9 @@ class HyperOptResolver(IResolver):
This class contains all the logic to load custom hyperopt class This class contains all the logic to load custom hyperopt class
""" """
object_type = IHyperOpt object_type = IHyperOpt
object_type_str = "Hyperopt"
user_subdir = USERPATH_HYPEROPTS
initial_search_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
@staticmethod @staticmethod
def load_hyperopt(config: Dict) -> IHyperOpt: def load_hyperopt(config: Dict) -> IHyperOpt:
@ -34,8 +37,9 @@ class HyperOptResolver(IResolver):
hyperopt_name = config['hyperopt'] hyperopt_name = config['hyperopt']
hyperopt = HyperOptResolver._load_hyperopt(hyperopt_name, config, hyperopt = HyperOptResolver.load_object(hyperopt_name, config,
extra_dir=config.get('hyperopt_path')) kwargs={'config': config},
extra_dir=config.get('hyperopt_path'))
if not hasattr(hyperopt, 'populate_indicators'): if not hasattr(hyperopt, 'populate_indicators'):
logger.warning("Hyperopt class does not provide populate_indicators() method. " logger.warning("Hyperopt class does not provide populate_indicators() method. "
@ -48,38 +52,15 @@ class HyperOptResolver(IResolver):
"Using populate_sell_trend from the strategy.") "Using populate_sell_trend from the strategy.")
return hyperopt return hyperopt
@staticmethod
def _load_hyperopt(
hyperopt_name: str, config: Dict, extra_dir: Optional[str] = None) -> IHyperOpt:
"""
Search and loads the specified hyperopt.
:param hyperopt_name: name of the module to import
:param config: configuration dictionary
:param extra_dir: additional directory to search for the given hyperopt
:return: HyperOpt instance or None
"""
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
abs_paths = HyperOptResolver.build_search_paths(config, current_path=current_path,
user_subdir=USERPATH_HYPEROPTS,
extra_dir=extra_dir)
hyperopt = HyperOptResolver._load_object(paths=abs_paths,
object_name=hyperopt_name,
kwargs={'config': config})
if hyperopt:
return hyperopt
raise OperationalException(
f"Impossible to load Hyperopt '{hyperopt_name}'. This class does not exist "
"or contains Python code errors."
)
class HyperOptLossResolver(IResolver): class HyperOptLossResolver(IResolver):
""" """
This class contains all the logic to load custom hyperopt loss class This class contains all the logic to load custom hyperopt loss class
""" """
object_type = IHyperOptLoss object_type = IHyperOptLoss
object_type_str = "HyperoptLoss"
user_subdir = USERPATH_HYPEROPTS
initial_search_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
@staticmethod @staticmethod
def load_hyperoptloss(config: Dict) -> IHyperOptLoss: def load_hyperoptloss(config: Dict) -> IHyperOptLoss:
@ -92,8 +73,9 @@ class HyperOptLossResolver(IResolver):
# default hyperopt loss # default hyperopt loss
hyperoptloss_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS hyperoptloss_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS
hyperoptloss = HyperOptLossResolver._load_hyperoptloss( hyperoptloss = HyperOptLossResolver.load_object(hyperoptloss_name,
hyperoptloss_name, config, extra_dir=config.get('hyperopt_path')) config, kwargs={},
extra_dir=config.get('hyperopt_path'))
# Assign ticker_interval to be used in hyperopt # Assign ticker_interval to be used in hyperopt
hyperoptloss.__class__.ticker_interval = str(config['ticker_interval']) hyperoptloss.__class__.ticker_interval = str(config['ticker_interval'])
@ -103,29 +85,3 @@ class HyperOptLossResolver(IResolver):
f"Found HyperoptLoss class {hyperoptloss_name} does not " f"Found HyperoptLoss class {hyperoptloss_name} does not "
"implement `hyperopt_loss_function`.") "implement `hyperopt_loss_function`.")
return hyperoptloss return hyperoptloss
@staticmethod
def _load_hyperoptloss(hyper_loss_name: str, config: Dict,
extra_dir: Optional[str] = None) -> IHyperOptLoss:
"""
Search and loads the specified hyperopt loss class.
:param hyper_loss_name: name of the module to import
:param config: configuration dictionary
:param extra_dir: additional directory to search for the given hyperopt
:return: HyperOptLoss instance or None
"""
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
abs_paths = HyperOptLossResolver.build_search_paths(config, current_path=current_path,
user_subdir=USERPATH_HYPEROPTS,
extra_dir=extra_dir)
hyperoptloss = HyperOptLossResolver._load_object(paths=abs_paths,
object_name=hyper_loss_name)
if hyperoptloss:
return hyperoptloss
raise OperationalException(
f"Impossible to load HyperoptLoss '{hyper_loss_name}'. This class does not exist "
"or contains Python code errors."
)

View File

@ -9,6 +9,8 @@ import logging
from pathlib import Path from pathlib import Path
from typing import Any, Generator, List, Optional, Tuple, Type, Union from typing import Any, Generator, List, Optional, Tuple, Type, Union
from freqtrade import OperationalException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -18,12 +20,15 @@ class IResolver:
""" """
# Childclasses need to override this # Childclasses need to override this
object_type: Type[Any] object_type: Type[Any]
object_type_str: str
user_subdir: Optional[str] = None
initial_search_path: Path
@staticmethod @classmethod
def build_search_paths(config, current_path: Path, user_subdir: Optional[str] = None, def build_search_paths(cls, config, user_subdir: Optional[str] = None,
extra_dir: Optional[str] = None) -> List[Path]: extra_dir: Optional[str] = None) -> List[Path]:
abs_paths: List[Path] = [current_path] abs_paths: List[Path] = [cls.initial_search_path]
if user_subdir: if user_subdir:
abs_paths.insert(0, config['user_data_dir'].joinpath(user_subdir)) abs_paths.insert(0, config['user_data_dir'].joinpath(user_subdir))
@ -105,3 +110,28 @@ class IResolver:
logger.warning('Path "%s" does not exist.', _path.resolve()) logger.warning('Path "%s" does not exist.', _path.resolve())
return None return None
@classmethod
def load_object(cls, object_name: str, config: dict, kwargs: dict,
extra_dir: Optional[str] = None) -> Any:
"""
Search and loads the specified object as configured in hte child class.
:param objectname: name of the module to import
:param config: configuration dictionary
:param extra_dir: additional directory to search for the given pairlist
:raises: OperationalException if the class is invalid or does not exist.
:return: Object instance or None
"""
abs_paths = cls.build_search_paths(config,
user_subdir=cls.user_subdir,
extra_dir=extra_dir)
pairlist = cls._load_object(paths=abs_paths, object_name=object_name,
kwargs=kwargs)
if pairlist:
return pairlist
raise OperationalException(
f"Impossible to load {cls.object_type_str} '{object_name}'. This class does not exist "
"or contains Python code errors."
)

View File

@ -6,7 +6,6 @@ This module load custom pairlists
import logging import logging
from pathlib import Path from pathlib import Path
from freqtrade import OperationalException
from freqtrade.pairlist.IPairList import IPairList from freqtrade.pairlist.IPairList import IPairList
from freqtrade.resolvers import IResolver from freqtrade.resolvers import IResolver
@ -18,6 +17,9 @@ class PairListResolver(IResolver):
This class contains all the logic to load custom PairList class This class contains all the logic to load custom PairList class
""" """
object_type = IPairList object_type = IPairList
object_type_str = "Pairlist"
user_subdir = None
initial_search_path = Path(__file__).parent.parent.joinpath('pairlist').resolve()
@staticmethod @staticmethod
def load_pairlist(pairlist_name: str, exchange, pairlistmanager, def load_pairlist(pairlist_name: str, exchange, pairlistmanager,
@ -32,34 +34,10 @@ class PairListResolver(IResolver):
:param pairlist_pos: Position of the pairlist in the list of pairlists :param pairlist_pos: Position of the pairlist in the list of pairlists
:return: initialized Pairlist class :return: initialized Pairlist class
""" """
return PairListResolver.load_object(pairlist_name, config,
return PairListResolver._load_pairlist(pairlist_name, config, kwargs={'exchange': exchange,
kwargs={'exchange': exchange, 'pairlistmanager': pairlistmanager,
'pairlistmanager': pairlistmanager, 'config': config,
'config': config, 'pairlistconfig': pairlistconfig,
'pairlistconfig': pairlistconfig, 'pairlist_pos': pairlist_pos},
'pairlist_pos': pairlist_pos}) )
@staticmethod
def _load_pairlist(pairlist_name: str, config: dict, kwargs: dict) -> IPairList:
"""
Search and loads the specified pairlist.
:param pairlist_name: name of the module to import
:param config: configuration dictionary
:param extra_dir: additional directory to search for the given pairlist
:return: PairList instance or None
"""
current_path = Path(__file__).parent.parent.joinpath('pairlist').resolve()
abs_paths = IResolver.build_search_paths(config, current_path=current_path,
user_subdir=None, extra_dir=None)
pairlist = PairListResolver._load_object(paths=abs_paths,
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."
)

View File

@ -11,7 +11,9 @@ from inspect import getfullargspec
from pathlib import Path from pathlib import Path
from typing import Dict, Optional from typing import Dict, Optional
from freqtrade import constants, OperationalException from freqtrade import OperationalException
from freqtrade.constants import (REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES,
USERPATH_STRATEGY)
from freqtrade.resolvers import IResolver from freqtrade.resolvers import IResolver
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
@ -23,6 +25,9 @@ class StrategyResolver(IResolver):
This class contains the logic to load custom strategy class This class contains the logic to load custom strategy class
""" """
object_type = IStrategy object_type = IStrategy
object_type_str = "Strategy"
user_subdir = USERPATH_STRATEGY
initial_search_path = Path(__file__).parent.parent.joinpath('strategy').resolve()
@staticmethod @staticmethod
def load_strategy(config: Optional[Dict] = None) -> IStrategy: def load_strategy(config: Optional[Dict] = None) -> IStrategy:
@ -115,11 +120,11 @@ class StrategyResolver(IResolver):
@staticmethod @staticmethod
def _strategy_sanity_validations(strategy): def _strategy_sanity_validations(strategy):
if not all(k in strategy.order_types for k in constants.REQUIRED_ORDERTYPES): if not all(k in strategy.order_types for k in REQUIRED_ORDERTYPES):
raise ImportError(f"Impossible to load Strategy '{strategy.__class__.__name__}'. " raise ImportError(f"Impossible to load Strategy '{strategy.__class__.__name__}'. "
f"Order-types mapping is incomplete.") f"Order-types mapping is incomplete.")
if not all(k in strategy.order_time_in_force for k in constants.REQUIRED_ORDERTIF): if not all(k in strategy.order_time_in_force for k in REQUIRED_ORDERTIF):
raise ImportError(f"Impossible to load Strategy '{strategy.__class__.__name__}'. " raise ImportError(f"Impossible to load Strategy '{strategy.__class__.__name__}'. "
f"Order-time-in-force mapping is incomplete.") f"Order-time-in-force mapping is incomplete.")
@ -133,10 +138,9 @@ class StrategyResolver(IResolver):
:param extra_dir: additional directory to search for the given strategy :param extra_dir: additional directory to search for the given strategy
:return: Strategy instance or None :return: Strategy instance or None
""" """
current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()
abs_paths = StrategyResolver.build_search_paths(config, current_path=current_path, abs_paths = StrategyResolver.build_search_paths(config,
user_subdir=constants.USERPATH_STRATEGY, user_subdir=USERPATH_STRATEGY,
extra_dir=extra_dir) extra_dir=extra_dir)
if ":" in strategy_name: if ":" in strategy_name:

View File

@ -159,7 +159,7 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
delattr(hyperopt, 'populate_buy_trend') delattr(hyperopt, 'populate_buy_trend')
delattr(hyperopt, 'populate_sell_trend') delattr(hyperopt, 'populate_sell_trend')
mocker.patch( mocker.patch(
'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt', 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object',
MagicMock(return_value=hyperopt(default_conf)) MagicMock(return_value=hyperopt(default_conf))
) )
default_conf.update({'hyperopt': 'DefaultHyperOpt'}) default_conf.update({'hyperopt': 'DefaultHyperOpt'})
@ -195,7 +195,7 @@ def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None:
hl = DefaultHyperOptLoss hl = DefaultHyperOptLoss
mocker.patch( mocker.patch(
'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver._load_hyperoptloss', 'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver.load_object',
MagicMock(return_value=hl) MagicMock(return_value=hl)
) )
x = HyperOptLossResolver.load_hyperoptloss(default_conf) x = HyperOptLossResolver.load_hyperoptloss(default_conf)