diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index f9289a54..683e9217 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -62,6 +62,106 @@ class FreqaiPrimer(IStrategy): logger.info(message) + def calculate_comprehensive_volatility_signal(self, dataframe: DataFrame, metadata: dict) -> tuple[float, str]: + """ + 计算综合波动率信号和趋势方向 + 返回: (波动率强度, 趋势方向) + 波动率强度: 0.0-1.0, 数值越高表示波动越大 + 趋势方向: 'up'(上涨), 'down'(下跌), 'neutral'(中性) + """ + if len(dataframe) < 50: + return 0.5, 'neutral' # 数据不足时返回中性值 + + # 获取最新数据 + current_data = dataframe.iloc[-1] + recent_data = dataframe.tail(20) # 最近20根K线 + + # 1. 基于ATR的波动率计算 + atr = current_data.get('atr', 0) + if atr > 0: + atr_normalized = min(atr / current_data['close'] / 0.02, 1.0) # 归一化到0-1 + else: + atr_normalized = 0.3 # 默认中等波动 + + # 2. 基于收盘价标准差的波动率 + price_volatility = recent_data['close'].pct_change().abs().std() + if pd.notna(price_volatility): + price_volatility_normalized = min(price_volatility / 0.03, 1.0) # 归一化到0-1 + else: + price_volatility_normalized = 0.3 + + # 3. 基于布林带宽度的波动率 + bb_width = 0 + if 'bb_upper_3m' in dataframe.columns and 'bb_lower_3m' in dataframe.columns: + bb_width = (current_data['bb_upper_3m'] - current_data['bb_lower_3m']) / current_data['close'] + bb_width_normalized = min(bb_width / 0.1, 1.0) # 归一化到0-1 + else: + bb_width_normalized = 0.3 + + # 4. 综合波动率强度 (平均值) + volatility_strength = (atr_normalized + price_volatility_normalized + bb_width_normalized) / 3.0 + + # 5. 趋势方向判断 + trend_direction = 'neutral' + + # 使用多个时间框架和技术指标进行趋势判断 + if 'close' in dataframe.columns and len(dataframe) >= 50: + # 短期趋势:基于EMA交叉 + if 'ema_50_3m' in dataframe.columns and 'ema_200_3m' in dataframe.columns: + current_close = current_data['close'] + ema_50 = current_data['ema_50_3m'] + ema_200 = current_data['ema_200_3m'] + + # EMA趋势 + if current_close > ema_50 > ema_200: + trend_direction = 'up' + elif current_close < ema_50 < ema_200: + trend_direction = 'down' + + # 补充趋势判断:基于RSI和MACD + if trend_direction == 'neutral': + # RSI趋势判断 + if 'rsi_3m' in dataframe.columns: + rsi = current_data['rsi_3m'] + if pd.notna(rsi): + if rsi > 60: + trend_direction = 'up' + elif rsi < 40: + trend_direction = 'down' + + # MACD趋势判断 + if trend_direction == 'neutral' and 'macd_3m' in dataframe.columns and 'macd_signal_3m' in dataframe.columns: + macd = current_data['macd_3m'] + macd_signal = current_data['macd_signal_3m'] + if pd.notna(macd) and pd.notna(macd_signal): + if macd > macd_signal: + trend_direction = 'up' + else: + trend_direction = 'down' + + # 6. 结合15分钟数据进行趋势确认 + try: + df_15m = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='15m') + if len(df_15m) >= 50: + current_15m = df_15m.iloc[-1] + + # 15分钟EMA趋势 + if 'ema_50_15m' in df_15m.columns and 'ema_200_15m' in df_15m.columns: + ema_50_15m = current_15m['ema_50_15m'] + ema_200_15m = current_15m['ema_200_15m'] + close_15m = current_15m['close'] + + if close_15m > ema_50_15m > ema_200_15m: + trend_direction = 'up' + elif close_15m < ema_50_15m < ema_200_15m: + trend_direction = 'down' + except Exception: + # 如果无法获取15分钟数据,则使用已有判断 + pass + + return min(volatility_strength, 1.0), trend_direction + + # 只用于adjust_trade_position方法的波动系数获取 def get_volatility_coefficient(self, pair: str) -> float: """ @@ -918,8 +1018,44 @@ class FreqaiPrimer(IStrategy): # 确保概率在 [0, 1] 范围内(分类器输出可能有浮点误差) entry_prob = max(0.0, min(1.0, entry_prob)) entry_threshold = self.ml_entry_signal_threshold.value + + # 新增:读取 AI 预测的未来波动率信号(入场决策版本) + future_vol_signal = None + if '&s-future_volatility' in df.columns: + future_vol_signal = float(last_row['&s-future_volatility']) + elif '&-future_volatility' in df.columns: + future_vol_signal = float(last_row['&-future_volatility']) + + # 波动率AI入场决策逻辑 + if future_vol_signal is not None: + # 情况A:AI 预测强趋势(高波动 > 0.65),且ML入场信号强 → 更倾向于入场 + if future_vol_signal > 0.65 and entry_prob >= entry_threshold: + self.strategy_log( + f"[波动率 AI] [{pair}] AI 预测强趋势(高波动 {future_vol_signal:.2f}),增强入场信心 | " + f"ML概率: {entry_prob:.2f}, 阈值: {entry_threshold:.2f}" + ) + # 可以考虑稍微降低入场阈值或增加入场权重 + # 情况B:AI 预测震荡市(低波动 < 0.35)→ 谨慎入场,可能错过趋势机会但降低风险 + elif future_vol_signal < 0.35: + self.strategy_log( + f"[波动率 AI] [{pair}] AI 预测震荡市(低波动 {future_vol_signal:.2f}),谨慎入场 | " + f"ML概率: {entry_prob:.2f}, 阈值: {entry_threshold:.2f}" + ) + # 在震荡市中,可以适当提高入场阈值,要求更强的信号才入场 + adjusted_threshold = entry_threshold * 1.1 # 提高10%的要求 + if entry_prob < adjusted_threshold: + self.strategy_log( + f"[波动率 AI] [{pair}] 震荡市中入场信号不足: {entry_prob:.2f} < 调整后阈值 {adjusted_threshold:.2f},拒绝入场" + ) + allow_trade = False + else: + self.strategy_log( + f"[波动率 AI] [{pair}] 震荡市中入场信号充足: {entry_prob:.2f} >= 调整后阈值 {adjusted_threshold:.2f},允许入场" + ) + # 介于 0.35-0.65 之间:中性区间,按原ML审核官逻辑处理 + # 记录entry_signal值用于调试 - self.strategy_log(f"[{pair}] ML 审核官检查: entry_signal={entry_prob:.2f}, 阈值={entry_threshold:.2f}, 市场状态={market_state}") + self.strategy_log(f"[{pair}] ML 审核官检查: entry_signal={entry_prob:.2f}, 阈值={entry_threshold:.2f}, 市场状态={market_state}, 波动率AI: {future_vol_signal if future_vol_signal is not None else 'N/A'}") if entry_prob < entry_threshold: self.strategy_log(f"[{pair}] ML 审核官拒绝入场: entry_signal 概率 {entry_prob:.2f} < 阈值 {entry_threshold:.2f}(上涨概率低,不宜入场)") allow_trade = False @@ -1039,8 +1175,40 @@ class FreqaiPrimer(IStrategy): return True # 介于 0.35-0.65 之间:中性区间,不做强制处理,继续走原有 ML 审核官逻辑 + # 使用综合技术指标计算波动率强度和趋势方向 + volatility_strength, trend_direction = self.calculate_comprehensive_volatility_signal(df, {'pair': pair}) + + # 基于趋势方向调整阈值 + # 如果趋势向上但当前盈利,可能需要更宽松的退出条件以抓住更多收益 + # 如果趋势向下且当前盈利,可能需要更严格的退出条件以保护利润 + trend_adjustment = 0.0 + if trend_direction == 'up' and current_profit > 0: + # 上升趋势中盈利,稍微放宽退出条件,让利润继续增长 + trend_adjustment = -0.05 # 降低阈值,更容易退出(保护利润) + elif trend_direction == 'down' and current_profit > 0: + # 下降趋势中盈利,收紧退出条件,尽快锁定利润 + trend_adjustment = 0.05 # 提高阈值,更难退出(保护利润) + elif trend_direction == 'up' and current_profit < 0: + # 上升趋势中亏损,稍微放宽退出条件,可能及时止损 + trend_adjustment = -0.03 + elif trend_direction == 'down' and current_profit < 0: + # 下降趋势中亏损,收紧退出条件,等待反弹 + trend_adjustment = 0.03 + + # 应用趋势调整后的阈值 + trend_adjusted_threshold = dynamic_threshold + trend_adjustment + # 确保阈值在合理范围内 + trend_adjusted_threshold = max(0.05, min(0.95, trend_adjusted_threshold)) + + # 记录趋势分析信息 + self.strategy_log( + f"[趋势分析] [{pair}] 波动强度: {volatility_strength:.2f}, " + f"趋势方向: {trend_direction}, 趋势调整: {trend_adjustment:.2f}, " + f"调整后阈值: {trend_adjusted_threshold:.2f}, 原始阈值: {dynamic_threshold:.2f}" + ) + # 设定下限,避免阈值过低 - dynamic_threshold = max(0.05, dynamic_threshold) + dynamic_threshold = max(0.05, trend_adjusted_threshold) if exit_prob < dynamic_threshold: self.strategy_log(