收集所有被拒绝的条件,最后统一显示

This commit is contained in:
zhangkun9038@dingtalk.com 2026-02-06 22:56:15 +08:00
parent 687cc59b47
commit b0798e20ba

View File

@ -901,123 +901,146 @@ class FreqaiPrimer(IStrategy):
"""
self.strategy_log(f"[{pair}] confirm_trade_entry 被调用 - 价格: {rate:.8f}, 时间: {current_time}")
# 默认允许交易
allow_trade = True
# 记录所有拒绝条件的列表
rejected_conditions = []
# 仅对多头交易进行检查
if side == 'long':
# 冷启动保护程序启动后前20分钟内不允许入场
cold_start_rejected = False
if hasattr(self, "_strategy_start_time"):
# 在非实盘环境(回测/模拟)下禁用冷启动保护
if not self.config.get("dry_run", False):
warmup_minutes = 20
elapsed_minutes = (current_time - self._strategy_start_time).total_seconds() / 60.0
if elapsed_minutes < warmup_minutes:
self.strategy_log(
f"[{pair}] 策略启动未满 {warmup_minutes} 分钟(已运行 {elapsed_minutes:.1f} 分钟),跳过本次入场信号"
)
return False
rejected_conditions.append(f"冷启动保护: 策略启动未满 {warmup_minutes} 分钟(已运行 {elapsed_minutes:.1f} 分钟)")
self.strategy_log(f"[{pair}] {rejected_conditions[-1]}")
cold_start_rejected = True
else:
# 在实盘模式下,记录冷启动保护已禁用
self.strategy_log(f"[{pair}] 实盘模式,冷启动保护已禁用")
# 检查1入场间隔控制使用hyperopt参数
if pair in self._last_entry_time:
interval_rejected = False
if pair in self._last_entry_time and not cold_start_rejected:
last_entry = self._last_entry_time[pair]
time_diff = (current_time - last_entry).total_seconds() * 0.0166666667 # 转换为分钟(使用乘法避免除法)
if time_diff < self.entry_interval_minutes.value:
self.strategy_log(f"[{pair}] 入场间隔不足: 距离上次入场 {time_diff:.1f}分钟 < {self.entry_interval_minutes.value}分钟,取消本次入场")
return False
rejected_conditions.append(f"入场间隔不足: 距离上次入场 {time_diff:.1f}分钟 < {self.entry_interval_minutes.value}分钟")
self.strategy_log(f"[{pair}] {rejected_conditions[-1]}")
interval_rejected = True
# 检查2检查是否处于剧烈拉升的不稳固区域
is_unstable_region = self.detect_h1_rapid_rise(pair)
if is_unstable_region:
self.strategy_log(f"[{pair}] 检测到剧烈拉升,取消入场交易")
return False
unstable_rejected = False
if not cold_start_rejected and not interval_rejected:
is_unstable_region = self.detect_h1_rapid_rise(pair)
if is_unstable_region:
rejected_conditions.append("剧烈拉升检测: 检测到1小时图剧烈拉升")
self.strategy_log(f"[{pair}] {rejected_conditions[-1]}")
unstable_rejected = True
# 检查3ML 审核官FreqAI 过滤低质量入场)+ 入场诊断统计
# 逻辑:用 entry_signal 概率来判断——若"容易上涨概率"低,则拒绝入场
ml_rejected = False
try:
df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(df) > 0:
last_row = df.iloc[-1]
entry_prob = None
# 优先使用 FreqAI 的 entry_signal 预测列
if '&s-entry_signal' in df.columns:
entry_prob = float(last_row['&s-entry_signal'])
elif '&-entry_signal_prob' in df.columns:
entry_prob = float(last_row['&-entry_signal_prob'])
elif '&-s-entry_signal_prob' in df.columns:
entry_prob = float(last_row['&-s-entry_signal_prob'])
elif '&-entry_signal' in df.columns:
val = last_row['&-entry_signal']
if isinstance(val, (int, float)):
entry_prob = float(val)
if not cold_start_rejected and not interval_rejected and not unstable_rejected:
df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(df) > 0:
last_row = df.iloc[-1]
entry_prob = None
# 优先使用 FreqAI 的 entry_signal 预测列
if '&s-entry_signal' in df.columns:
entry_prob = float(last_row['&s-entry_signal'])
elif '&-entry_signal_prob' in df.columns:
entry_prob = float(last_row['&-entry_signal_prob'])
elif '&-s-entry_signal_prob' in df.columns:
entry_prob = float(last_row['&-s-entry_signal_prob'])
elif '&-entry_signal' in df.columns:
val = last_row['&-entry_signal']
if isinstance(val, (int, float)):
entry_prob = float(val)
else:
# 文本标签时,简单映射为 0/1
entry_prob = 1.0 if str(val).lower() in ['entry', 'buy', '1'] else 0.0
# ========== 新增:入场诊断统计 ==========
# 统计当前入场点的关键指标,用于分析"买在高位"问题
current_close = float(last_row['close'])
# 1. 价格与短期高点的关系
recent_high_5 = float(df['high'].iloc[-5:].max()) if len(df) >= 5 else current_close
price_vs_recent_high = (current_close - recent_high_5) / recent_high_5 if recent_high_5 > 0 else 0
# 2. 价格与 EMA5 的关系
ema5_1h = float(last_row.get('ema_5_1h', current_close))
price_vs_ema5 = (current_close - ema5_1h) / ema5_1h if ema5_1h > 0 else 0
# 3. 价格与布林带的位置
bb_upper = float(last_row.get('bb_upper_1h', current_close))
bb_lower = float(last_row.get('bb_lower_1h', current_close))
bb_position = (current_close - bb_lower) / (bb_upper - bb_lower) if (bb_upper - bb_lower) > 0 else 0.5
# 4. RSI 状态
rsi_1h = float(last_row.get('rsi_1h', 50))
# 5. MACD 状态
macd_1h = float(last_row.get('macd_1h', 0))
macd_signal_1h = float(last_row.get('macd_signal_1h', 0))
macd_cross = 'up' if macd_1h > macd_signal_1h else 'down'
# 6. 市场状态
market_state = str(last_row.get('market_state', 'unknown'))
# 输出诊断日志
if entry_prob is not None:
ml_prob_str = f"{entry_prob:.2f}"
else:
# 文本标签时,简单映射为 0/1
entry_prob = 1.0 if str(val).lower() in ['entry', 'buy', '1'] else 0.0
# ========== 新增:入场诊断统计 ==========
# 统计当前入场点的关键指标,用于分析"买在高位"问题
current_close = float(last_row['close'])
# 1. 价格与短期高点的关系
recent_high_5 = float(df['high'].iloc[-5:].max()) if len(df) >= 5 else current_close
price_vs_recent_high = (current_close - recent_high_5) / recent_high_5 if recent_high_5 > 0 else 0
# 2. 价格与 EMA5 的关系
ema5_1h = float(last_row.get('ema_5_1h', current_close))
price_vs_ema5 = (current_close - ema5_1h) / ema5_1h if ema5_1h > 0 else 0
# 3. 价格与布林带的位置
bb_upper = float(last_row.get('bb_upper_1h', current_close))
bb_lower = float(last_row.get('bb_lower_1h', current_close))
bb_position = (current_close - bb_lower) / (bb_upper - bb_lower) if (bb_upper - bb_lower) > 0 else 0.5
# 4. RSI 状态
rsi_1h = float(last_row.get('rsi_1h', 50))
# 5. MACD 状态
macd_1h = float(last_row.get('macd_1h', 0))
macd_signal_1h = float(last_row.get('macd_signal_1h', 0))
macd_cross = 'up' if macd_1h > macd_signal_1h else 'down'
# 6. 市场状态
market_state = str(last_row.get('market_state', 'unknown'))
# 输出诊断日志
if entry_prob is not None:
ml_prob_str = f"{entry_prob:.2f}"
ml_prob_str = "N/A"
self.strategy_log(
f"[入场诊断] {pair} | "
f"价格: {current_close:.6f} | "
f"vs 5K高点: {price_vs_recent_high:+.2%} | "
f"vs EMA5: {price_vs_ema5:+.2%} | "
f"布林位置: {bb_position:.2f} | "
f"RSI: {rsi_1h:.1f} | "
f"MACD: {macd_cross} | "
f"市场: {market_state} | "
f"ML入场概率: {ml_prob_str}"
)
# ========== 诊断统计结束 ==========
if entry_prob is not None:
# 确保概率在 [0, 1] 范围内(分类器输出可能有浮点误差)
entry_prob = max(0.0, min(1.0, entry_prob))
entry_threshold = self.ml_entry_signal_threshold.value
if entry_prob < entry_threshold:
rejected_conditions.append(f"ML审核官: entry_signal 概率 {entry_prob:.2f} < 阈值 {entry_threshold:.2f}")
self.strategy_log(f"[{pair}] {rejected_conditions[-1]}")
ml_rejected = True
else:
self.strategy_log(f"[{pair}] ML 审核官允许入场: entry_signal 概率 {entry_prob:.2f} >= 阈值 {entry_threshold:.2f}")
else:
ml_prob_str = "N/A"
self.strategy_log(
f"[入场诊断] {pair} | "
f"价格: {current_close:.6f} | "
f"vs 5K高点: {price_vs_recent_high:+.2%} | "
f"vs EMA5: {price_vs_ema5:+.2%} | "
f"布林位置: {bb_position:.2f} | "
f"RSI: {rsi_1h:.1f} | "
f"MACD: {macd_cross} | "
f"市场: {market_state} | "
f"ML入场概率: {ml_prob_str}"
)
# ========== 诊断统计结束 ==========
if entry_prob is not None:
# 确保概率在 [0, 1] 范围内(分类器输出可能有浮点误差)
entry_prob = max(0.0, min(1.0, entry_prob))
entry_threshold = self.ml_entry_signal_threshold.value
if entry_prob < entry_threshold:
self.strategy_log(f"[{pair}] ML 审核官拒绝入场: entry_signal 概率 {entry_prob:.2f} < 阈值 {entry_threshold:.2f}(上涨概率低,不宜入场)")
return False
else:
self.strategy_log(f"[{pair}] ML 审核官允许入场: entry_signal 概率 {entry_prob:.2f} >= 阈值 {entry_threshold:.2f}")
self.strategy_log(f"[{pair}] 无数据分析数据跳过ML审核")
else:
self.strategy_log(f"[{pair}] 无数据分析数据跳过ML审核")
self.strategy_log(f"[{pair}] 因前置条件拒绝跳过ML审核")
except Exception as e:
logger.warning(f"[{pair}] ML 审核官检查失败,忽略 ML 过滤: {e}")
return True
rejected_conditions.append(f"ML审核官异常: {str(e)}")
self.strategy_log(f"[{pair}] {rejected_conditions[-1]}")
else:
# 如果不是多头交易,也记录下来
self.strategy_log(f"[{pair}] 非多头交易,跳过入场检查")
# 如果没有任何拒绝条件,表示允许入场
if not rejected_conditions:
self.strategy_log(f"[{pair}] 所有条件通过,允许入场")
return True
else:
# 显示所有被拒绝的条件
self.strategy_log(f"[{pair}] 入场被拒绝,原因列表: {', '.join(rejected_conditions)}")
return False
def confirm_trade_exit(
self,