From f39a534fc039795af1eb45761d998b221e9a1867 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Oct 2020 20:03:56 +0200 Subject: [PATCH] Implement global stop (First try) --- freqtrade/constants.py | 1 + freqtrade/freqtradebot.py | 5 ++++- freqtrade/plugins/__init__.py | 2 ++ freqtrade/plugins/protectionmanager.py | 20 +++++++++++++------ freqtrade/plugins/protections/iprotection.py | 2 +- .../plugins/protections/stoploss_guard.py | 2 +- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 601e525c1..d070386d0 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -27,6 +27,7 @@ AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'AgeFilter', 'PerformanceFilter', 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', 'ShuffleFilter', 'SpreadFilter'] +AVAILABLE_PROTECTIONS = ['StoplossGuard'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5'] DRY_RUN_WALLET = 1000 DATETIME_PRINT_FORMAT = '%Y-%m-%d %H:%M:%S' diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c8d281852..2dbd7f099 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -23,6 +23,7 @@ from freqtrade.exchange import timeframe_to_minutes from freqtrade.misc import safe_value_fallback, safe_value_fallback2 from freqtrade.pairlist.pairlistmanager import PairListManager from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db +from freqtrade.plugins.protectionmanager import ProtectionManager from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.state import State @@ -78,6 +79,8 @@ class FreqtradeBot: self.dataprovider = DataProvider(self.config, self.exchange, self.pairlists) + self.protections = ProtectionManager(self.config) + # Attach Dataprovider to Strategy baseclass IStrategy.dp = self.dataprovider # Attach Wallets to Strategy baseclass @@ -178,7 +181,7 @@ class FreqtradeBot: self.exit_positions(trades) # Then looking for buy opportunities - if self.get_free_open_trades(): + if self.get_free_open_trades() and not self.protections.global_stop(): self.enter_positions() Trade.session.flush() diff --git a/freqtrade/plugins/__init__.py b/freqtrade/plugins/__init__.py index e69de29bb..96943268b 100644 --- a/freqtrade/plugins/__init__.py +++ b/freqtrade/plugins/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa: F401 +# from freqtrade.plugins.protectionmanager import ProtectionManager diff --git a/freqtrade/plugins/protectionmanager.py b/freqtrade/plugins/protectionmanager.py index 5185c93f0..31b0ca300 100644 --- a/freqtrade/plugins/protectionmanager.py +++ b/freqtrade/plugins/protectionmanager.py @@ -7,7 +7,7 @@ from typing import Dict, List from freqtrade.exceptions import OperationalException from freqtrade.plugins.protections import IProtection from freqtrade.resolvers import ProtectionResolver - +from datetime import datetime logger = logging.getLogger(__name__) @@ -18,8 +18,7 @@ class ProtectionManager(): self._config = config self._protection_handlers: List[IProtection] = [] - self._tickers_needed = False - for protection_handler_config in self._config.get('protections', None): + for protection_handler_config in self._config.get('protections', []): if 'method' not in protection_handler_config: logger.warning(f"No method found in {protection_handler_config}, ignoring.") continue @@ -28,11 +27,10 @@ class ProtectionManager(): 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") + logger.info("No protection Handlers defined.") @property def name_list(self) -> List[str]: @@ -45,4 +43,14 @@ class ProtectionManager(): """ List of short_desc for each Pairlist Handler """ - return [{p.name: p.short_desc()} for p in self._pairlist_handlers] + return [{p.name: p.short_desc()} for p in self._protection_handlers] + + def global_stop(self) -> bool: + now = datetime.utcnow() + for protection_handler in self._protection_handlers: + result = protection_handler.global_stop(now) + + # Early stopping - first positive result stops the application + if result: + return True + return False diff --git a/freqtrade/plugins/protections/iprotection.py b/freqtrade/plugins/protections/iprotection.py index 75d1fb3ad..25bcee923 100644 --- a/freqtrade/plugins/protections/iprotection.py +++ b/freqtrade/plugins/protections/iprotection.py @@ -26,7 +26,7 @@ class IProtection(ABC): """ @abstractmethod - def stop_trade_enters_global(self, date_now: datetime) -> bool: + def global_stop(self, date_now: datetime) -> bool: """ Stops trading (position entering) for all pairs This must evaluate to true for the whole period of the "cooldown period". diff --git a/freqtrade/plugins/protections/stoploss_guard.py b/freqtrade/plugins/protections/stoploss_guard.py index 3418dd1da..c6cddb01e 100644 --- a/freqtrade/plugins/protections/stoploss_guard.py +++ b/freqtrade/plugins/protections/stoploss_guard.py @@ -47,7 +47,7 @@ class StoplossGuard(IProtection): return False - def stop_trade_enters_global(self, date_now: datetime) -> bool: + def global_stop(self, date_now: datetime) -> bool: """ Stops trading (position entering) for all pairs This must evaluate to true for the whole period of the "cooldown period".