feat(blacklist): Enhance blacklist logic and UI representation

Updated the trading system to differentiate between soft and hard blacklists based on recent performance metrics. The frontend now displays the blacklist status with clear visual indicators. This change aims to improve risk management and user awareness of trading conditions.
This commit is contained in:
薇薇安 2026-02-27 20:08:07 +08:00
parent d5ef525224
commit 78007040f1
3 changed files with 44 additions and 10 deletions

View File

@ -1756,7 +1756,7 @@ const GlobalConfig = () => {
</div>
</div>
<div>
<div style={{ fontSize: '12px', fontWeight: 600, marginBottom: '4px', color: '#721c24' }}>黑名单降权考核期中</div>
<div style={{ fontSize: '12px', fontWeight: 600, marginBottom: '4px', color: '#721c24' }}>黑名单/考察期动态调整</div>
<div style={{ maxHeight: '220px', overflowY: 'auto', border: '1px solid #f5c6cb', borderRadius: '4px' }}>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '12px' }}>
<thead>
@ -1765,6 +1765,7 @@ const GlobalConfig = () => {
<th style={{ padding: '4px 6px', textAlign: 'right' }}>笔数</th>
<th style={{ padding: '4px 6px', textAlign: 'right' }}>净盈亏</th>
<th style={{ padding: '4px 6px', textAlign: 'right' }}>胜率%</th>
<th style={{ padding: '4px 6px', textAlign: 'center' }}>模式</th>
</tr>
</thead>
<tbody>
@ -1776,6 +1777,17 @@ const GlobalConfig = () => {
{Number(item.net_pnl).toFixed(2)}
</td>
<td style={{ padding: '3px 6px', borderTop: '1px solid #f5c6cb', textAlign: 'right' }}>{Number(item.win_rate_pct).toFixed(1)}</td>
<td style={{ padding: '3px 6px', borderTop: '1px solid #f5c6cb', textAlign: 'center' }}>
{String(item.mode || 'reduced') === 'hard' ? (
<span style={{ display: 'inline-block', padding: '1px 6px', borderRadius: '10px', fontSize: '11px', background: '#dc3545', color: '#fff' }}>
</span>
) : (
<span style={{ display: 'inline-block', padding: '1px 6px', borderRadius: '10px', fontSize: '11px', background: '#fff3cd', color: '#856404', border: '1px solid #ffeeba' }}>
</span>
)}
</td>
</tr>
))}
</tbody>

View File

@ -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=硬黑名单(自动不再开仓)
}
)
# 白名单:近期净盈且胜率尚可

View File

@ -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中性时反向”以降低风险。