Add duration_explanation functions

This commit is contained in:
Matthias 2020-12-07 11:08:54 +01:00
parent c993831a04
commit 0e2a43ab4d
6 changed files with 63 additions and 14 deletions

View File

@ -22,13 +22,13 @@ class CooldownPeriod(IProtection):
""" """
LockReason to use LockReason to use
""" """
return (f'Cooldown period for {self._stop_duration} min.') return (f'Cooldown period for {self.stop_duration_str}.')
def short_desc(self) -> str: def short_desc(self) -> str:
""" """
Short method description - used for startup-messages Short method description - used for startup-messages
""" """
return (f"{self.name} - Cooldown period of {self._stop_duration} min.") return (f"{self.name} - Cooldown period of {self.stop_duration_str}.")
def _cooldown_period(self, pair: str, date_now: datetime, ) -> ProtectionReturn: def _cooldown_period(self, pair: str, date_now: datetime, ) -> ProtectionReturn:
""" """
@ -42,7 +42,7 @@ class CooldownPeriod(IProtection):
] ]
trade = Trade.get_trades(filters).first() trade = Trade.get_trades(filters).first()
if trade: if trade:
self.log_once(f"Cooldown for {pair} for {self._stop_duration}.", logger.info) self.log_once(f"Cooldown for {pair} for {self.stop_duration_str}.", logger.info)
until = self.calculate_lock_end([trade], self._stop_duration) until = self.calculate_lock_end([trade], self._stop_duration)
return True, until, self._reason() return True, until, self._reason()

View File

@ -5,6 +5,7 @@ from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
from freqtrade.exchange import timeframe_to_minutes from freqtrade.exchange import timeframe_to_minutes
from freqtrade.misc import plural
from freqtrade.mixins import LoggingMixin from freqtrade.mixins import LoggingMixin
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
@ -26,12 +27,16 @@ class IProtection(LoggingMixin, ABC):
self._protection_config = protection_config self._protection_config = protection_config
tf_in_min = timeframe_to_minutes(config['timeframe']) tf_in_min = timeframe_to_minutes(config['timeframe'])
if 'stop_duration_candles' in protection_config: if 'stop_duration_candles' in protection_config:
self._stop_duration = (tf_in_min * protection_config.get('stop_duration_candles')) self._stop_duration_candles = protection_config.get('stop_duration_candles', 1)
self._stop_duration = (tf_in_min * self._stop_duration_candles)
else: else:
self._stop_duration_candles = None
self._stop_duration = protection_config.get('stop_duration', 60) self._stop_duration = protection_config.get('stop_duration', 60)
if 'lookback_period_candles' in protection_config: if 'lookback_period_candles' in protection_config:
self._lookback_period = tf_in_min * protection_config.get('lookback_period_candles', 60) self._lookback_period_candles = protection_config.get('lookback_period_candles', 1)
self._lookback_period = tf_in_min * self._lookback_period_candles
else: else:
self._lookback_period_candles = None
self._lookback_period = protection_config.get('lookback_period', 60) self._lookback_period = protection_config.get('lookback_period', 60)
LoggingMixin.__init__(self, logger) LoggingMixin.__init__(self, logger)
@ -40,6 +45,30 @@ class IProtection(LoggingMixin, ABC):
def name(self) -> str: def name(self) -> str:
return self.__class__.__name__ return self.__class__.__name__
@property
def stop_duration_str(self) -> str:
"""
Output configured stop duration in either candles or minutes
"""
if self._stop_duration_candles:
return (f"{self._stop_duration_candles} "
f"{plural(self._stop_duration_candles, 'candle', 'candles')}")
else:
return (f"{self._stop_duration} "
f"{plural(self._stop_duration, 'minute', 'minutes')}")
@property
def lookback_period_str(self) -> str:
"""
Output configured lookback period in either candles or minutes
"""
if self._lookback_period_candles:
return (f"{self._lookback_period_candles} "
f"{plural(self._lookback_period_candles, 'candle', 'candles')}")
else:
return (f"{self._lookback_period} "
f"{plural(self._lookback_period, 'minute', 'minutes')}")
@abstractmethod @abstractmethod
def short_desc(self) -> str: def short_desc(self) -> str:
""" """

View File

@ -26,14 +26,14 @@ class LowProfitPairs(IProtection):
Short method description - used for startup-messages Short method description - used for startup-messages
""" """
return (f"{self.name} - Low Profit Protection, locks pairs with " return (f"{self.name} - Low Profit Protection, locks pairs with "
f"profit < {self._required_profit} within {self._lookback_period} minutes.") f"profit < {self._required_profit} within {self.lookback_period_str}.")
def _reason(self, profit: float) -> str: def _reason(self, profit: float) -> str:
""" """
LockReason to use LockReason to use
""" """
return (f'{profit} < {self._required_profit} in {self._lookback_period} min, ' return (f'{profit} < {self._required_profit} in {self.lookback_period_str}, '
f'locking for {self._stop_duration} min.') f'locking for {self.stop_duration_str}.')
def _low_profit(self, date_now: datetime, pair: str) -> ProtectionReturn: def _low_profit(self, date_now: datetime, pair: str) -> ProtectionReturn:
""" """

View File

@ -30,14 +30,14 @@ class MaxDrawdown(IProtection):
Short method description - used for startup-messages Short method description - used for startup-messages
""" """
return (f"{self.name} - Max drawdown protection, stop trading if drawdown is > " return (f"{self.name} - Max drawdown protection, stop trading if drawdown is > "
f"{self._max_allowed_drawdown} within {self._lookback_period} minutes.") f"{self._max_allowed_drawdown} within {self.lookback_period_str}.")
def _reason(self, drawdown: float) -> str: def _reason(self, drawdown: float) -> str:
""" """
LockReason to use LockReason to use
""" """
return (f'{drawdown} > {self._max_allowed_drawdown} in {self._lookback_period} min, ' return (f'{drawdown} > {self._max_allowed_drawdown} in {self.lookback_period_str}, '
f'locking for {self._stop_duration} min.') f'locking for {self.stop_duration_str}.')
def _max_drawdown(self, date_now: datetime) -> ProtectionReturn: def _max_drawdown(self, date_now: datetime) -> ProtectionReturn:
""" """
@ -62,7 +62,7 @@ class MaxDrawdown(IProtection):
if drawdown > self._max_allowed_drawdown: if drawdown > self._max_allowed_drawdown:
self.log_once( self.log_once(
f"Trading stopped due to Max Drawdown {drawdown:.2f} < {self._max_allowed_drawdown}" f"Trading stopped due to Max Drawdown {drawdown:.2f} < {self._max_allowed_drawdown}"
f" within {self._lookback_period} minutes.", logger.info) f" within {self.lookback_period_str}.", logger.info)
until = self.calculate_lock_end(trades, self._stop_duration) until = self.calculate_lock_end(trades, self._stop_duration)
return True, until, self._reason(drawdown) return True, until, self._reason(drawdown)

View File

@ -29,7 +29,7 @@ class StoplossGuard(IProtection):
Short method description - used for startup-messages Short method description - used for startup-messages
""" """
return (f"{self.name} - Frequent Stoploss Guard, {self._trade_limit} stoplosses " return (f"{self.name} - Frequent Stoploss Guard, {self._trade_limit} stoplosses "
f"within {self._lookback_period} minutes.") f"within {self.lookback_period_str}.")
def _reason(self) -> str: def _reason(self) -> str:
""" """

View File

@ -350,7 +350,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
None None
), ),
({"method": "CooldownPeriod", "stop_duration": 60}, ({"method": "CooldownPeriod", "stop_duration": 60},
"[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 60 min.'}]", "[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 60 minutes.'}]",
None None
), ),
({"method": "LowProfitPairs", "lookback_period": 60, "stop_duration": 60}, ({"method": "LowProfitPairs", "lookback_period": 60, "stop_duration": 60},
@ -363,6 +363,26 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
"within 60 minutes.'}]", "within 60 minutes.'}]",
None None
), ),
({"method": "StoplossGuard", "lookback_period_candles": 12, "trade_limit": 2,
"stop_duration": 60},
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
"2 stoplosses within 12 candles.'}]",
None
),
({"method": "CooldownPeriod", "stop_duration_candles": 5},
"[{'CooldownPeriod': 'CooldownPeriod - Cooldown period of 5 candles.'}]",
None
),
({"method": "LowProfitPairs", "lookback_period_candles": 11, "stop_duration": 60},
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with "
"profit < 0.0 within 11 candles.'}]",
None
),
({"method": "MaxDrawdown", "lookback_period_candles": 20, "stop_duration": 60},
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading if drawdown is > 0.0 "
"within 20 candles.'}]",
None
),
]) ])
def test_protection_manager_desc(mocker, default_conf, protectionconf, def test_protection_manager_desc(mocker, default_conf, protectionconf,
desc_expected, exception_expected): desc_expected, exception_expected):