diff --git a/frontend/src/components/GlobalConfig.jsx b/frontend/src/components/GlobalConfig.jsx index 44b3050..5cc4605 100644 --- a/frontend/src/components/GlobalConfig.jsx +++ b/frontend/src/components/GlobalConfig.jsx @@ -1756,7 +1756,7 @@ const GlobalConfig = () => {
| 笔数 | 净盈亏 | 胜率% | +模式 | @@ -1776,6 +1777,17 @@ const GlobalConfig = () => { {Number(item.net_pnl).toFixed(2)}{Number(item.win_rate_pct).toFixed(1)} | ++ {String(item.mode || 'reduced') === 'hard' ? ( + + 硬 + + ) : ( + + 软 + + )} + | ))} diff --git a/scripts/aggregate_trade_stats.py b/scripts/aggregate_trade_stats.py index 5e341b9..7d504cb 100644 --- a/scripts/aggregate_trade_stats.py +++ b/scripts/aggregate_trade_stats.py @@ -42,8 +42,11 @@ def main(): # 规则参数(后续可改成全局配置,这里先写死一版直观规则): min_trades_for_symbol = 5 # 至少 N 笔才纳入评估,避免样本太少 min_trades_for_hour = 10 # 小时至少 N 笔才纳入评估 - blacklist_pnl_threshold = 0.0 # 净盈亏 < 0 视为表现不佳 - good_hour_pnl_threshold = 0.0 # 净盈亏 > 0 视为表现较好 + blacklist_pnl_threshold = 0.0 # 净盈亏 < 0 视为表现不佳(软黑名单) + hard_blacklist_pnl_threshold = -3.0 # 净亏绝对值较大时考虑升级为硬黑名单 + hard_blacklist_min_trades = 10 # 硬黑名单至少需要的样本数 + hard_blacklist_max_win_rate = 10.0 # 胜率≤10% 且净亏较大时视为严重拖累 + good_hour_pnl_threshold = 0.0 # 净盈亏 > 0 视为表现较好 symbol_blacklist = [] symbol_whitelist = [] @@ -61,15 +64,22 @@ def main(): total = max(1, win + loss) win_rate = 100.0 * win / total - # 软黑名单:近期净亏 + 有一定笔数 -> 降权(不一刀切禁止) + # 黑名单:近期净亏 + 有一定笔数 -> 降权或升级为硬黑名单 if net_pnl < blacklist_pnl_threshold: + mode = "reduced" + if ( + net_pnl <= hard_blacklist_pnl_threshold + and tc >= hard_blacklist_min_trades + and win_rate <= hard_blacklist_max_win_rate + ): + mode = "hard" symbol_blacklist.append( { "symbol": sym, "trade_count": tc, "net_pnl": round(net_pnl, 4), "win_rate_pct": round(win_rate, 1), - "mode": "reduced", # 软黑名单:仅降权+提门槛 + "mode": mode, # reduced=软黑名单(降权+提门槛);hard=硬黑名单(自动不再开仓) } ) # 白名单:近期净盈且胜率尚可 diff --git a/trading_system/strategy.py b/trading_system/strategy.py index 2bf14c3..eea4670 100644 --- a/trading_system/strategy.py +++ b/trading_system/strategy.py @@ -690,8 +690,11 @@ class TradingStrategy: # 判断是否应该交易(信号强度 >= 有效 MIN_SIGNAL_STRENGTH 才交易;低波动期自动提高门槛) base_min_signal_strength = config.get_effective_config('MIN_SIGNAL_STRENGTH', 7) - # 基于全局 7 天统计的 symbol / 小时过滤:差标的、差时段提高信号门槛 + # 基于全局 7 天统计的 symbol / 小时过滤: + # - 硬黑名单:直接禁止自动交易(仅允许平仓) + # - 软黑名单 & 差时段:提高信号门槛 extra_signal_boost = 0 + blocked_by_hard_blacklist = False try: from datetime import datetime, timezone, timedelta from config_manager import GlobalStrategyConfigManager @@ -700,11 +703,17 @@ class TradingStrategy: stats_symbol = mgr.get("STATS_SYMBOL_FILTERS", {}) or {} stats_hour = mgr.get("STATS_HOUR_FILTERS", {}) or {} - # symbol 软黑名单:命中则在基础门槛上 +1 + # symbol 黑名单:硬黑名单直接拒绝;软黑名单在基础门槛上 +1 bl = stats_symbol.get("blacklist") or [] if symbol and isinstance(bl, list): - if any((item.get("symbol") or "").upper() == symbol.upper() for item in bl): - extra_signal_boost += 1 + for item in bl: + if (item.get("symbol") or "").upper() == symbol.upper(): + mode = (item.get("mode") or "reduced").lower() + if mode == "hard": + blocked_by_hard_blacklist = True + else: + extra_signal_boost += 1 + break # 按小时过滤:bad 小时再 +1 try: @@ -721,6 +730,7 @@ class TradingStrategy: except Exception: base_min_signal_strength = config.get_effective_config('MIN_SIGNAL_STRENGTH', 7) extra_signal_boost = 0 + blocked_by_hard_blacklist = False min_signal_strength = max(0, min(10, int(base_min_signal_strength) + int(extra_signal_boost))) # 强度上限归一到 0-10,避免出现 12/10 这种误导显示 @@ -740,7 +750,9 @@ class TradingStrategy: signal_strength = min(scan_strength, 5) direction = scan_dir reasons.append("采用扫描器综合信号(弱)") - should_trade = signal_strength >= min_signal_strength and direction is not None + should_trade = (not blocked_by_hard_blacklist) and signal_strength >= min_signal_strength and direction is not None + if blocked_by_hard_blacklist: + reasons.append("❌ 命中硬黑名单(7天持续净亏),自动交易禁用,仅允许手动/解封后再交易") # ===== RSI / 24h 涨跌幅过滤:做多不追高、做空不杀跌;可选 RSI 极端反向(超买反空/超卖反多)===== # 反向属于“逆短期超买超卖”的均值回归,在强趋势里逆势风险大;可选“仅4H中性时反向”以降低风险。
|---|