diff --git a/freqtrade/plugins/__init__.py b/freqtrade/plugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/freqtrade/plugins/protectionmanager.py b/freqtrade/plugins/protectionmanager.py new file mode 100644 index 000000000..ff64ca789 --- /dev/null +++ b/freqtrade/plugins/protectionmanager.py @@ -0,0 +1,51 @@ +""" +Protection manager class +""" +import logging +from typing import Dict, List + +from freqtrade.exceptions import OperationalException +from freqtrade.plugins.protections import IProtection +from freqtrade.resolvers import ProtectionResolver + + +logger = logging.getLogger(__name__) + + +class ProtectionManager(): + + def __init__(self, exchange, config: dict) -> None: + self._exchange = exchange + self._config = config + + self._protection_handlers: List[IProtection] = [] + self._tickers_needed = False + for protection_handler_config in self._config.get('protections', None): + if 'method' not in protection_handler_config: + logger.warning(f"No method found in {protection_handler_config}, ignoring.") + continue + protection_handler = ProtectionResolver.load_protection( + protection_handler_config['method'], + exchange=exchange, + protectionmanager=self, + config=config, + protection_config=protection_handler_config, + ) + self._tickers_needed |= protection_handler.needstickers + self._protection_handlers.append(protection_handler) + + if not self._protection_handlers: + raise OperationalException("No protection Handlers defined") + + @property + def name_list(self) -> List[str]: + """ + Get list of loaded Protection Handler names + """ + return [p.name for p in self._protection_handlers] + + def short_desc(self) -> List[Dict]: + """ + List of short_desc for each Pairlist Handler + """ + return [{p.name: p.short_desc()} for p in self._pairlist_handlers] diff --git a/freqtrade/plugins/protections/__init__.py b/freqtrade/plugins/protections/__init__.py new file mode 100644 index 000000000..5ecae7888 --- /dev/null +++ b/freqtrade/plugins/protections/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa: F401 +from freqtrade.plugins.protections.iprotection import IProtection diff --git a/freqtrade/plugins/protections/iprotection.py b/freqtrade/plugins/protections/iprotection.py new file mode 100644 index 000000000..b10856f70 --- /dev/null +++ b/freqtrade/plugins/protections/iprotection.py @@ -0,0 +1,24 @@ + +import logging +from abc import ABC, abstractmethod +from typing import Any, Dict + + +logger = logging.getLogger(__name__) + + +class IProtection(ABC): + + def __init__(self, config: Dict[str, Any]) -> None: + self._config = config + + @property + def name(self) -> str: + return self.__class__.__name__ + + @abstractmethod + def short_desc(self) -> str: + """ + Short method description - used for startup-messages + -> Please overwrite in subclasses + """ diff --git a/freqtrade/resolvers/__init__.py b/freqtrade/resolvers/__init__.py index b42ec4931..ef24bf481 100644 --- a/freqtrade/resolvers/__init__.py +++ b/freqtrade/resolvers/__init__.py @@ -6,6 +6,7 @@ from freqtrade.resolvers.exchange_resolver import ExchangeResolver # Don't import HyperoptResolver to avoid loading the whole Optimize tree # from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver from freqtrade.resolvers.pairlist_resolver import PairListResolver +from freqtrade.resolvers.protection_resolver import ProtectionResolver from freqtrade.resolvers.strategy_resolver import StrategyResolver diff --git a/freqtrade/resolvers/protection_resolver.py b/freqtrade/resolvers/protection_resolver.py new file mode 100644 index 000000000..9a85104c3 --- /dev/null +++ b/freqtrade/resolvers/protection_resolver.py @@ -0,0 +1,44 @@ +# pragma pylint: disable=attribute-defined-outside-init + +""" +This module load custom pairlists +""" +import logging +from pathlib import Path +from typing import Dict + +from freqtrade.plugins.protections import IProtection +from freqtrade.resolvers import IResolver + + +logger = logging.getLogger(__name__) + + +class ProtectionResolver(IResolver): + """ + This class contains all the logic to load custom PairList class + """ + object_type = IProtection + object_type_str = "Protection" + user_subdir = None + initial_search_path = Path(__file__).parent.parent.joinpath('plugins/protections').resolve() + + @staticmethod + def load_protection(protection_name: str, exchange, protectionmanager, + config: Dict, protection_config: Dict) -> IProtection: + """ + Load the protection with protection_name + :param protection_name: Classname of the pairlist + :param exchange: Initialized exchange class + :param protectionmanager: Initialized protection manager + :param config: configuration dictionary + :param protection_config: Configuration dedicated to this pairlist + :return: initialized Protection class + """ + return ProtectionResolver.load_object(protection_name, config, + kwargs={'exchange': exchange, + 'pairlistmanager': protectionmanager, + 'config': config, + 'pairlistconfig': protection_config, + }, + )