Refactor: update IProtection interface to support starting_balance

This commit is contained in:
ABS
2026-02-16 20:31:43 +08:00
parent 9e2eca3b8b
commit 5c9455c32a
6 changed files with 31 additions and 14 deletions
+4 -2
View File
@@ -2421,7 +2421,9 @@ class FreqtradeBot(LoggingMixin):
def handle_protections(self, pair: str, side: LongShort) -> None: def handle_protections(self, pair: str, side: LongShort) -> None:
# Lock pair for one candle to prevent immediate re-entries # Lock pair for one candle to prevent immediate re-entries
self.strategy.lock_pair(pair, datetime.now(UTC), reason="Auto lock", side=side) self.strategy.lock_pair(pair, datetime.now(UTC), reason="Auto lock", side=side)
prot_trig = self.protections.stop_per_pair(pair, side=side) starting_balance = self.wallets.get_starting_balance()
prot_trig = self.protections.stop_per_pair(
pair, side=side, starting_balance=starting_balance)
if prot_trig: if prot_trig:
msg: RPCProtectionMsg = { msg: RPCProtectionMsg = {
"type": RPCMessageType.PROTECTION_TRIGGER, "type": RPCMessageType.PROTECTION_TRIGGER,
@@ -2430,7 +2432,7 @@ class FreqtradeBot(LoggingMixin):
} }
self.rpc.send_msg(msg) self.rpc.send_msg(msg)
prot_trig_glb = self.protections.global_stop(side=side) prot_trig_glb = self.protections.global_stop(side=side, starting_balance=starting_balance)
if prot_trig_glb: if prot_trig_glb:
msg = { msg = {
"type": RPCMessageType.PROTECTION_TRIGGER_GLOBAL, "type": RPCMessageType.PROTECTION_TRIGGER_GLOBAL,
+11 -4
View File
@@ -47,13 +47,17 @@ class ProtectionManager:
""" """
return [{p.name: p.short_desc()} for p in self._protection_handlers] return [{p.name: p.short_desc()} for p in self._protection_handlers]
def global_stop(self, now: datetime | None = None, side: LongShort = "long") -> PairLock | None: def global_stop(
self, now: datetime | None = None, side: LongShort = "long", starting_balance: float = 0.0
) -> PairLock | None:
if not now: if not now:
now = datetime.now(UTC) now = datetime.now(UTC)
result = None result = None
for protection_handler in self._protection_handlers: for protection_handler in self._protection_handlers:
if protection_handler.has_global_stop: if protection_handler.has_global_stop:
lock = protection_handler.global_stop(date_now=now, side=side) lock = protection_handler.global_stop(
date_now=now, side=side, starting_balance=starting_balance
)
if lock and lock.until: if lock and lock.until:
if not PairLocks.is_global_lock(lock.until, side=lock.lock_side): if not PairLocks.is_global_lock(lock.until, side=lock.lock_side):
result = PairLocks.lock_pair( result = PairLocks.lock_pair(
@@ -62,14 +66,17 @@ class ProtectionManager:
return result return result
def stop_per_pair( def stop_per_pair(
self, pair, now: datetime | None = None, side: LongShort = "long" self, pair, now: datetime | None = None, side: LongShort = "long",
starting_balance: float = 0.0
) -> PairLock | None: ) -> PairLock | None:
if not now: if not now:
now = datetime.now(UTC) now = datetime.now(UTC)
result = None result = None
for protection_handler in self._protection_handlers: for protection_handler in self._protection_handlers:
if protection_handler.has_local_stop: if protection_handler.has_local_stop:
lock = protection_handler.stop_per_pair(pair=pair, date_now=now, side=side) lock = protection_handler.stop_per_pair(
pair=pair, date_now=now, side=side, starting_balance=starting_balance
)
if lock and lock.until: if lock and lock.until:
if not PairLocks.is_pair_locked(pair, lock.until, lock.lock_side): if not PairLocks.is_pair_locked(pair, lock.until, lock.lock_side):
result = PairLocks.lock_pair( result = PairLocks.lock_pair(
@@ -52,7 +52,9 @@ class CooldownPeriod(IProtection):
return None return None
def global_stop(self, date_now: datetime, side: LongShort) -> ProtectionReturn | None: def global_stop(
self, date_now: datetime, side: LongShort, starting_balance: float = 0.0
) -> ProtectionReturn | None:
""" """
Stops trading (position entering) for all pairs Stops trading (position entering) for all pairs
This must evaluate to true for the whole period of the "cooldown period". This must evaluate to true for the whole period of the "cooldown period".
@@ -63,7 +65,7 @@ class CooldownPeriod(IProtection):
return None return None
def stop_per_pair( def stop_per_pair(
self, pair: str, date_now: datetime, side: LongShort self, pair: str, date_now: datetime, side: LongShort, starting_balance: float = 0.0
) -> ProtectionReturn | None: ) -> ProtectionReturn | None:
""" """
Stops trading (position entering) for this pair Stops trading (position entering) for this pair
+4 -2
View File
@@ -102,7 +102,9 @@ class IProtection(LoggingMixin, ABC):
""" """
@abstractmethod @abstractmethod
def global_stop(self, date_now: datetime, side: LongShort) -> ProtectionReturn | None: def global_stop(
self, date_now: datetime, side: LongShort, starting_balance: float = 0.0
) -> ProtectionReturn | None:
""" """
Stops trading (position entering) for all pairs Stops trading (position entering) for all pairs
This must evaluate to true for the whole period of the "cooldown period". This must evaluate to true for the whole period of the "cooldown period".
@@ -110,7 +112,7 @@ class IProtection(LoggingMixin, ABC):
@abstractmethod @abstractmethod
def stop_per_pair( def stop_per_pair(
self, pair: str, date_now: datetime, side: LongShort self, pair: str, date_now: datetime, side: LongShort, starting_balance: float = 0.0
) -> ProtectionReturn | None: ) -> ProtectionReturn | None:
""" """
Stops trading (position entering) for this pair Stops trading (position entering) for this pair
@@ -81,7 +81,9 @@ class LowProfitPairs(IProtection):
return None return None
def global_stop(self, date_now: datetime, side: LongShort) -> ProtectionReturn | None: def global_stop(
self, date_now: datetime, side: LongShort, starting_balance: float = 0.0
) -> ProtectionReturn | None:
""" """
Stops trading (position entering) for all pairs Stops trading (position entering) for all pairs
This must evaluate to true for the whole period of the "cooldown period". This must evaluate to true for the whole period of the "cooldown period".
@@ -91,7 +93,7 @@ class LowProfitPairs(IProtection):
return None return None
def stop_per_pair( def stop_per_pair(
self, pair: str, date_now: datetime, side: LongShort self, pair: str, date_now: datetime, side: LongShort, starting_balance: float = 0.0
) -> ProtectionReturn | None: ) -> ProtectionReturn | None:
""" """
Stops trading (position entering) for this pair Stops trading (position entering) for this pair
@@ -86,7 +86,9 @@ class StoplossGuard(IProtection):
lock_side=(side if self._only_per_side else "*"), lock_side=(side if self._only_per_side else "*"),
) )
def global_stop(self, date_now: datetime, side: LongShort) -> ProtectionReturn | None: def global_stop(
self, date_now: datetime, side: LongShort, starting_balance: float = 0.0
) -> ProtectionReturn | None:
""" """
Stops trading (position entering) for all pairs Stops trading (position entering) for all pairs
This must evaluate to true for the whole period of the "cooldown period". This must evaluate to true for the whole period of the "cooldown period".
@@ -98,7 +100,7 @@ class StoplossGuard(IProtection):
return self._stoploss_guard(date_now, None, side) return self._stoploss_guard(date_now, None, side)
def stop_per_pair( def stop_per_pair(
self, pair: str, date_now: datetime, side: LongShort self, pair: str, date_now: datetime, side: LongShort, starting_balance: float = 0.0
) -> ProtectionReturn | None: ) -> ProtectionReturn | None:
""" """
Stops trading (position entering) for this pair Stops trading (position entering) for this pair