diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index 6b4b8433..918084e8 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -697,45 +697,74 @@ class FreqaiPrimer(IStrategy): return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - # 出场信号基于趋势和量价关系 - # 条件1: 价格突破布林带上轨(使用可优化的偏差参数) - breakout_condition = dataframe['close'] >= dataframe['bb_upper_1h'] * self.exit_bb_upper_deviation.value + # 出场信号基于趋势和量价关系 - 收紧条件减少过早出场 ⭐优化 - # 条件2: 成交量显著放大(使用可优化的成交量乘数) - volume_spike = dataframe['volume'] > dataframe['volume_ma'] * self.exit_volume_multiplier.value + # 条件1: 价格显著突破布林带上轨(收紧:需要更明显的突破) + breakout_condition = dataframe['close'] >= dataframe['bb_upper_1h'] * 1.02 # 收紧:需要超出BB上轨2% - # 条件3: MACD 下降趋势 - macd_downward = dataframe['macd_1h'] < dataframe['macd_signal_1h'] + # 条件2: 成交量剧烈放大(收紧:需要更大的放量) + volume_spike = dataframe['volume'] > dataframe['volume_ma'] * 2.5 # 收紧:1.5倍 → 2.5倍 - # 条件4: RSI 进入超买区域(使用可优化的超买阈值) - rsi_overbought = dataframe['rsi_1h'] > self.rsi_overbought.value + # 条件3: MACD 明确下降(收紧:需要更强的下跌确认) + macd_downward = ( + (dataframe['macd_1h'] < dataframe['macd_signal_1h']) & # MACD < 信号线 + (dataframe['macd_hist_1h'] < 0) & # 新增:柱状图必须为负 + (dataframe['macd_hist_1h'] < dataframe['macd_hist_1h'].shift(1)) # 且递减 + ) - # ========== KDJ 出场辅助判断 ========== - # 3m KDJ 高位死叉:K > 80 且 K 下穿 D(部分止盈信号) + # 条件4: RSI 极度超买(收紧:提高阈值) + rsi_overbought = dataframe['rsi_1h'] > 75 # 收紧:70 → 75 + + # ========== KDJ 出场辅助判断(收紧条件)========== + # 3m KDJ 极度高位死叉:K > 85 且 K 下穿 D(收紧:提高阈值) kdj_3m_exit = False if 'kdj_k_3m' in dataframe.columns and 'kdj_d_3m' in dataframe.columns: - kdj_3m_death_cross = (dataframe['kdj_k_3m'] > 80) & (dataframe['kdj_k_3m'] < dataframe['kdj_d_3m']) & (dataframe['kdj_k_3m'].shift(1) >= dataframe['kdj_d_3m'].shift(1)) + kdj_3m_death_cross = ( + (dataframe['kdj_k_3m'] > 85) & # 收紧:80 → 85(更极端的高位) + (dataframe['kdj_k_3m'] < dataframe['kdj_d_3m']) & + (dataframe['kdj_k_3m'].shift(1) >= dataframe['kdj_d_3m'].shift(1)) + ) kdj_3m_exit = kdj_3m_death_cross - # ========== SAR 出场辅助判断(趋势反转)========== + # ========== SAR 出场辅助判断(保持不变)========== + # SAR 反转是强信号,保持原逻辑 sar_exit = False if 'sar_3m' in dataframe.columns: - # SAR 反转:价格跌破 SAR 止损位(从上方穿到下方) + # SAR 反转:价格跌破 SAR 止损位 sar_reversal = (dataframe['close'] < dataframe['sar_3m']) & (dataframe['close'].shift(1) >= dataframe['sar_3m'].shift(1)) sar_exit = sar_reversal - # ========== Ichimoku 出场辅助判断(云突破、TK死叉)========== + # ========== Ichimoku 出场辅助判断(仅保留云突破)========== + # 收紧:只用云突破,不用TK死叉(TK死叉太敏感) ichimoku_exit = False - if 'ichimoku_price_vs_cloud_3m' in dataframe.columns and 'ichimoku_tk_cross_3m' in dataframe.columns: - # 条件1:价格跌破云(从云上跌到云下或云中) - price_below_cloud = (dataframe['ichimoku_price_vs_cloud_3m'] <= 0) & (dataframe['ichimoku_price_vs_cloud_3m'].shift(1) == 1) - # 条件j:TK死叉(转换线下穿基准线) - tk_death_cross = (dataframe['ichimoku_tk_cross_3m'] == -1) & (dataframe['ichimoku_tk_cross_3m'].shift(1) == 1) - # 任一条件满足即触发 - ichimoku_exit = price_below_cloud | tk_death_cross + if 'ichimoku_price_vs_cloud_3m' in dataframe.columns: + # 仅条件1:价格跌破云(从云上跌到云下) + price_below_cloud = ( + (dataframe['ichimoku_price_vs_cloud_3m'] == -1) & # 当前在云下 + (dataframe['ichimoku_price_vs_cloud_3m'].shift(1) == 1) # 前一根在云上 + ) + ichimoku_exit = price_below_cloud - # 合并所有条件(SAR、Ichimoku 作为可选信号,不强制) - final_condition = breakout_condition | volume_spike | macd_downward | rsi_overbought | kdj_3m_exit | sar_exit | ichimoku_exit + # ========== 合并出场条件(收紧逻辑:需要多重确认)⭐优化 ========== + # 原逻辑:任一条件满足就出场(太敏感) + # 新逻辑:需要满足以下任一组合 + + # 组合1:强制出场信号(单一强信号即可) + strong_exit = sar_exit | ichimoku_exit # SAR反转或跌破云 + + # 组合2:多重技术指标确认(需要至少2个条件) + tech_indicators = [ + breakout_condition, # 突破BB上轨 + volume_spike, # 放量 + macd_downward, # MACD下降 + rsi_overbought, # RSI超买 + kdj_3m_exit # KDJ死叉 + ] + tech_count = sum([cond.astype(int) for cond in tech_indicators]) + multi_tech_exit = tech_count >= 2 # 需要至少2个技术指标同时触发 + + # 最终出场条件:强制信号 或 多重技术确认 + final_condition = strong_exit | multi_tech_exit # 设置出场信号 dataframe.loc[final_condition, 'exit_long'] = 1 @@ -873,41 +902,42 @@ class FreqaiPrimer(IStrategy): # === 底部识别(5种方法)=== - # 方法1:传统双底识别(插针型) + # 方法1:传统双底识别(插针型)- 放宽阈值 recent_low_20 = dataframe['low'].rolling(window=20, min_periods=1).min() recent_high_20 = dataframe['high'].rolling(window=20, min_periods=1).max() price_vs_recent_low = (dataframe['close'] - recent_low_20) / recent_low_20 price_vs_recent_high = (recent_high_20 - dataframe['close']) / recent_high_20 - near_low_traditional = price_vs_recent_low <= 0.03 # 距离低点 ≤ 3% + near_low_traditional = price_vs_recent_low <= 0.05 # 放宽:3% → 5%(增加入场机会) away_from_high_traditional = price_vs_recent_high >= 0.02 # 距离高点 ≥ 2% - # 方法2:动量衰减识别(缓慢下跌底部) + # 方法2:动量衰减识别(缓慢下跌底部)- 放宽条件 price_change_5 = dataframe['close'].pct_change(5) price_change_10 = dataframe['close'].pct_change(10) momentum_slowing_down = ( (price_change_5 < 0) & # 仍在下跌 (price_change_5 > price_change_10 / 2) & # 但跌幅变小 - (price_change_10 < -0.02) # 且之前有明显下跌 + (price_change_10 < -0.015) # 放宽:-2% → -1.5%(更早识别) ) - # 方法3:RSI超卖识别 + # 方法3:RSI超卖识别 - 放宽阈值 rsi_oversold = pd.Series(False, index=dataframe.index) if 'rsi_1h' in dataframe.columns: - rsi_oversold = dataframe['rsi_1h'] < 35 + rsi_oversold = dataframe['rsi_1h'] < 40 # 放宽:35 → 40(更早识别超卖) - # 方法4:布林带下轨突破识别 + # 方法4:布林带下轨突破识别 - 放宽范围 bb_oversold = pd.Series(False, index=dataframe.index) if 'bb_lower_1h' in dataframe.columns: bb_lower = dataframe['bb_lower_1h'] - bb_oversold = (dataframe['close'] <= bb_lower * 1.02) & (dataframe['close'] >= bb_lower * 0.98) + # 放宽:±2% → ±5%(覆盖更多底部区域) + bb_oversold = (dataframe['close'] <= bb_lower * 1.05) & (dataframe['close'] >= bb_lower * 0.95) - # 方法5:成交量萎缩识别(抛压减弱) + # 方法5:成交量萎缩识别(抛压减弱)- 放宽条件 volume_ma_20 = dataframe['volume'].rolling(window=20, min_periods=1).mean() volume_shrinking = ( - (dataframe['volume'] < volume_ma_20 * 0.7) & + (dataframe['volume'] < volume_ma_20 * 0.8) & # 放宽:70% → 80%(更早识别) (price_change_5 < 0) ) @@ -949,20 +979,26 @@ class FreqaiPrimer(IStrategy): (dataframe['macd_hist_3m'].shift(1) <= 0) # 前一根为负或零 ) - # === 方法7:3m StochRSI 超卖识别(比RSI更敏感)⭐新增 === + # === 方法7:3m StochRSI 超卖识别(比RSI更敏感)⭐新增 - 放宽阈值 === stochrsi_oversold_3m = pd.Series(False, index=dataframe.index) if 'stochrsi_k_3m' in dataframe.columns: - stochrsi_oversold_3m = dataframe['stochrsi_k_3m'] < 20 # 深度超卖 + stochrsi_oversold_3m = dataframe['stochrsi_k_3m'] < 30 # 放宽:20 → 30(更早识别) - # === 底部组合(允许入场)=== + # === 底部组合(允许入场)- 增加灵活组合 === bottom_combo1 = near_low_traditional & away_from_high_traditional # 传统双底 bottom_combo2 = momentum_slowing_down & rsi_oversold # 缓慢下跌见底 bottom_combo3 = bb_oversold & volume_shrinking # 深度回调见底 bottom_combo4 = rsi_oversold & away_from_high_traditional # 超卖回调 - bottom_combo5 = momentum_slowing_down & macd_turning_3m # 动量衰减+MACD反转 ⭐新增 - bottom_combo6 = stochrsi_oversold_3m & near_low_traditional # StochRSI超卖+接近低点 ⭐新增 + bottom_combo5 = momentum_slowing_down & macd_turning_3m # 动量衰减+MACD反转 + bottom_combo6 = stochrsi_oversold_3m & near_low_traditional # StochRSI超卖+接近低点 - is_bottom = bottom_combo1 | bottom_combo2 | bottom_combo3 | bottom_combo4 | bottom_combo5 | bottom_combo6 + # === 新增:单一强信号也允许入场(提高入场频率)⭐ === + bottom_combo7 = near_low_traditional & rsi_oversold # 接近低点+RSI超卖(放宽组合) + bottom_combo8 = bb_oversold & rsi_oversold # 布林带超卖+RSI超卖(双重确认) + bottom_combo9 = stochrsi_oversold_3m & momentum_slowing_down # StochRSI+动量衰减 + + is_bottom = (bottom_combo1 | bottom_combo2 | bottom_combo3 | bottom_combo4 | + bottom_combo5 | bottom_combo6 | bottom_combo7 | bottom_combo8 | bottom_combo9) # === 顶部组合(禁止入场)⭐新增 === top_combo1 = near_high_traditional # 接近历史高点(追高危险) @@ -1199,7 +1235,7 @@ class FreqaiPrimer(IStrategy): self.strategy_log(f"[{pair}] 🚫 拉升后不稳固区域,取消入场") allow_trade = False - # 检查3:防止追高 - 简单规则:3m EMA20 之上拒绝入场 + # 检查3:防止追高 - 放宽规则:价格显著高于 EMA20 才拒绝(放宽条件) if allow_trade: try: df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) @@ -1210,12 +1246,12 @@ class FreqaiPrimer(IStrategy): # 检查 3m EMA20 ema20_3m = float(last_row.get('ema_20_3m', current_close)) - # 价格在 EMA20 之上 → 拒绝入场(追高) - if current_close > ema20_3m: - price_vs_ema20 = (current_close - ema20_3m) / ema20_3m + # 放宽:价格高于 EMA20 超过 2% 才拒绝(之前是只要高于就拒绝) + price_vs_ema20 = (current_close - ema20_3m) / ema20_3m + if price_vs_ema20 > 0.02: # 新增:超过2%才算追高 self.strategy_log( - f"[{pair}] 🚫 追高过滤: 价格 {current_close:.6f} 高于3m EMA20 {ema20_3m:.6f} " - f"({price_vs_ema20:+.2%}),取消入场" + f"[{pair}] 🚫 追高过滤: 价格 {current_close:.6f} 显著高于3m EMA20 {ema20_3m:.6f} " + f"({price_vs_ema20:+.2%} > +2%),取消入场" ) allow_trade = False