myTestFreqAI/freqtrade/plugins/protections/total_loss_protection.py
zhangkun9038@dingtalk.com 43a0070351 自定义两个protections
2025-08-27 14:05:18 +08:00

121 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
from datetime import datetime, timedelta
from typing import Any
from freqtrade.constants import Config, LongShort
from freqtrade.persistence import Trade
from freqtrade.plugins.protections.iprotection import IProtection, ProtectionReturn
logger = logging.getLogger(__name__)
class TotalLossProtection(IProtection):
has_global_stop: bool = True
has_local_stop: bool = False
def __init__(self, config: Config, protection_config: dict[str, Any]) -> None:
super().__init__(config, protection_config)
# 配置参数10根半小时K线的亏损阈值10美元
self._threshold_10_candles = protection_config.get("threshold_10_candles", 10.0)
# 配置参数20根半小时K线的亏损阈值15美元
self._threshold_20_candles = protection_config.get("threshold_20_candles", 15.0)
def short_desc(self) -> str:
"""
Short method description - used for startup-messages
"""
return (
f"{self.name} - Total Loss Protection, stop trading if loss > {self._threshold_10_candles} within last 10 30min candles or > {self._threshold_20_candles} within last 20 30min candles."
)
def _reason(self, lookback_period: int, total_loss: float, threshold: float) -> str:
"""
Lock reason text
"""
return (
f"Total Loss {total_loss:.2f} > {threshold} in {lookback_period} 30min candles, "
f"locking {self.unlock_reason_time_element}."
)
def _total_loss_check(
self, date_now: datetime, side: LongShort
) -> ProtectionReturn | None:
"""
检查最近10根和20根半小时K线内的总亏损
1根半小时K线 = 30分钟
10根半小时K线 = 5小时
20根半小时K线 = 10小时
"""
# 计算10根半小时K线的回溯时间5小时
look_back_until_10_candles = date_now - timedelta(hours=5)
# 计算20根半小时K线的回溯时间10小时
look_back_until_20_candles = date_now - timedelta(hours=10)
# 获取10根K线时间窗口内的所有已关闭交易
trades_10_candles = Trade.get_trades_proxy(
is_open=False,
close_date=look_back_until_10_candles
)
# 获取20根K线时间窗口内的所有已关闭交易
trades_20_candles = Trade.get_trades_proxy(
is_open=False,
close_date=look_back_until_20_candles
)
# 计算10根K线时间窗口内的总亏损
total_loss_10_candles = sum(
min(0, trade.close_profit) # 只计算亏损部分
for trade in trades_10_candles
if trade.close_profit is not None
) * -1 # 转换为正数表示亏损金额
# 计算20根K线时间窗口内的总亏损
total_loss_20_candles = sum(
min(0, trade.close_profit) # 只计算亏损部分
for trade in trades_20_candles
if trade.close_profit is not None
) * -1 # 转换为正数表示亏损金额
# 检查是否触发任一条件
if total_loss_10_candles > self._threshold_10_candles and trades_10_candles:
self.log_once(
f"Trading stopped due to Total Loss Protection: {total_loss_10_candles:.2f} > {self._threshold_10_candles} within last 10 30min candles.",
logger.info,
)
until = self.calculate_lock_end(trades_10_candles)
return ProtectionReturn(
lock=True,
until=until,
reason=self._reason(10, total_loss_10_candles, self._threshold_10_candles),
)
elif total_loss_20_candles > self._threshold_20_candles and trades_20_candles:
self.log_once(
f"Trading stopped due to Total Loss Protection: {total_loss_20_candles:.2f} > {self._threshold_20_candles} within last 20 30min candles.",
logger.info,
)
until = self.calculate_lock_end(trades_20_candles)
return ProtectionReturn(
lock=True,
until=until,
reason=self._reason(20, total_loss_20_candles, self._threshold_20_candles),
)
return None
def global_stop(self, date_now: datetime, side: LongShort) -> ProtectionReturn | None:
"""
停止所有交易对的交易(开仓)
"""
return self._total_loss_check(date_now, side)
def stop_per_pair(
self, pair: str, date_now: datetime, side: LongShort
) -> ProtectionReturn | None:
"""
本保护机制不支持单个交易对的停止
"""
return None