Fixed implementation of unlock_at and updated unit tests
This commit is contained in:
@@ -46,6 +46,9 @@ class CooldownPeriod(IProtection):
|
|||||||
# Ignore type error as we know we only get closed trades.
|
# Ignore type error as we know we only get closed trades.
|
||||||
trade = sorted(trades, key=lambda t: t.close_date)[-1] # type: ignore
|
trade = sorted(trades, key=lambda t: t.close_date)[-1] # type: ignore
|
||||||
self.log_once(f"Cooldown for {pair} for {self.stop_duration_str}.", logger.info)
|
self.log_once(f"Cooldown for {pair} for {self.stop_duration_str}.", logger.info)
|
||||||
|
|
||||||
|
self.set_unlock_at_as_stop_duration()
|
||||||
|
|
||||||
until = self.calculate_lock_end([trade], self._stop_duration)
|
until = self.calculate_lock_end([trade], self._stop_duration)
|
||||||
|
|
||||||
return ProtectionReturn(
|
return ProtectionReturn(
|
||||||
|
|||||||
@@ -47,18 +47,7 @@ class IProtection(LoggingMixin, ABC):
|
|||||||
else:
|
else:
|
||||||
self._lookback_period = int(protection_config.get("lookback_period", 60))
|
self._lookback_period = int(protection_config.get("lookback_period", 60))
|
||||||
|
|
||||||
if "unlock_at" in protection_config:
|
self.set_unlock_at_as_stop_duration()
|
||||||
now_time = datetime.now(timezone.utc)
|
|
||||||
unlock_at = datetime.strptime(protection_config["unlock_at"], "%H:%M").replace(
|
|
||||||
day=now_time.day, year=now_time.year, month=now_time.month
|
|
||||||
)
|
|
||||||
|
|
||||||
if unlock_at.time() < now_time.time():
|
|
||||||
unlock_at = unlock_at.replace(day=now_time.day + 1)
|
|
||||||
|
|
||||||
unlock_at = unlock_at.replace(tzinfo=timezone.utc)
|
|
||||||
self._stop_duration = self.calculate_timespan(now_time, unlock_at)
|
|
||||||
self.unlock_at = unlock_at
|
|
||||||
|
|
||||||
LoggingMixin.__init__(self, logger)
|
LoggingMixin.__init__(self, logger)
|
||||||
|
|
||||||
@@ -101,6 +90,36 @@ class IProtection(LoggingMixin, ABC):
|
|||||||
return self.unlock_at.strftime("%H:%M")
|
return self.unlock_at.strftime("%H:%M")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def set_unlock_at_as_stop_duration(self) -> None:
|
||||||
|
"""
|
||||||
|
Calculates the stop_duration based on the unlock_at protection config value and sets it.
|
||||||
|
"""
|
||||||
|
if "unlock_at" in self._protection_config:
|
||||||
|
self._stop_duration = self.calculate_unlock_at()
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.warning(
|
||||||
|
"Couldn't update the stop duration, because unlock_at is not set in the "
|
||||||
|
"protection config."
|
||||||
|
)
|
||||||
|
|
||||||
|
def calculate_unlock_at(self) -> int:
|
||||||
|
"""
|
||||||
|
Calculate and update the stop duration based on the unlock at config.
|
||||||
|
"""
|
||||||
|
|
||||||
|
now_time = datetime.now(timezone.utc)
|
||||||
|
unlock_at = datetime.strptime(
|
||||||
|
str(self._protection_config.get("unlock_at_config")), "%H:%M"
|
||||||
|
).replace(day=now_time.day, year=now_time.year, month=now_time.month)
|
||||||
|
|
||||||
|
if unlock_at.time() < now_time.time():
|
||||||
|
unlock_at = unlock_at.replace(day=now_time.day + 1)
|
||||||
|
|
||||||
|
self.unlock_at = unlock_at.replace(tzinfo=timezone.utc)
|
||||||
|
result = IProtection.calculate_timespan(now_time, self.unlock_at)
|
||||||
|
return result
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def short_desc(self) -> str:
|
def short_desc(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ class LowProfitPairs(IProtection):
|
|||||||
f"within {self._lookback_period} minutes.",
|
f"within {self._lookback_period} minutes.",
|
||||||
logger.info,
|
logger.info,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.set_unlock_at_as_stop_duration()
|
||||||
until = self.calculate_lock_end(trades, self._stop_duration)
|
until = self.calculate_lock_end(trades, self._stop_duration)
|
||||||
|
|
||||||
return ProtectionReturn(
|
return ProtectionReturn(
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ class MaxDrawdown(IProtection):
|
|||||||
f" within {self.lookback_period_str}.",
|
f" within {self.lookback_period_str}.",
|
||||||
logger.info,
|
logger.info,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.set_unlock_at_as_stop_duration()
|
||||||
until = self.calculate_lock_end(trades, self._stop_duration)
|
until = self.calculate_lock_end(trades, self._stop_duration)
|
||||||
|
|
||||||
return ProtectionReturn(
|
return ProtectionReturn(
|
||||||
|
|||||||
@@ -81,7 +81,10 @@ class StoplossGuard(IProtection):
|
|||||||
f"stoplosses within {self._lookback_period} minutes.",
|
f"stoplosses within {self._lookback_period} minutes.",
|
||||||
logger.info,
|
logger.info,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.set_unlock_at_as_stop_duration()
|
||||||
until = self.calculate_lock_end(trades, self._stop_duration)
|
until = self.calculate_lock_end(trades, self._stop_duration)
|
||||||
|
|
||||||
return ProtectionReturn(
|
return ProtectionReturn(
|
||||||
lock=True,
|
lock=True,
|
||||||
until=until,
|
until=until,
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ def test_protections_init(default_conf, timeframe, expected_lookback, expected_s
|
|||||||
if isinstance(expected_stop, int):
|
if isinstance(expected_stop, int):
|
||||||
assert man._protection_handlers[0]._stop_duration == expected_stop
|
assert man._protection_handlers[0]._stop_duration == expected_stop
|
||||||
else:
|
else:
|
||||||
assert man._protection_handlers[0].unlock_at.strftime("%H:%M") == expected_stop
|
assert man._protection_handlers[0].unlock_at_str == expected_stop
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("is_short", [False, True])
|
@pytest.mark.parametrize("is_short", [False, True])
|
||||||
@@ -701,19 +701,19 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
|
|||||||
"unlock_at": "01:00",
|
"unlock_at": "01:00",
|
||||||
},
|
},
|
||||||
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
|
"[{'StoplossGuard': 'StoplossGuard - Frequent Stoploss Guard, "
|
||||||
"2 stoplosses with profit < -5.00% within 12 candles. Unlocking trading at 01:00.'}]",
|
"2 stoplosses with profit < -5.00% within 12 candles.'}]",
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{"method": "LowProfitPairs", "lookback_period_candles": 11, "unlock_at": "03:00"},
|
{"method": "LowProfitPairs", "lookback_period_candles": 11, "unlock_at": "03:00"},
|
||||||
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with "
|
"[{'LowProfitPairs': 'LowProfitPairs - Low Profit Protection, locks pairs with "
|
||||||
"profit < 0.0 within 11 candles. Unlocking trading at 03:00.'}]",
|
"profit < 0.0 within 11 candles.'}]",
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{"method": "MaxDrawdown", "lookback_period_candles": 20, "unlock_at": "04:00"},
|
{"method": "MaxDrawdown", "lookback_period_candles": 20, "unlock_at": "04:00"},
|
||||||
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading "
|
"[{'MaxDrawdown': 'MaxDrawdown - Max drawdown protection, stop trading "
|
||||||
"if drawdown is > 0.0 within 20 candles. Unlocking trading at 04:00.'}]",
|
"if drawdown is > 0.0 within 20 candles.'}]",
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user