diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index 0f93d3dc..23da82b2 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -991,6 +991,12 @@ class FreqaiPrimer(IStrategy): dataframe['stochrsi_k_3m'] = stochrsi_3m[f'STOCHRSIk_{rsi_length_value}_{rsi_length_value}_3_3'] dataframe['stochrsi_d_3m'] = stochrsi_3m[f'STOCHRSId_{rsi_length_value}_{rsi_length_value}_3_3'] + # 新增 KDJ 指标(3m) + kdj_3m = ta.stoch(high=dataframe['high'], low=dataframe['low'], close=dataframe['close'], k=9, d=3, smooth_k=3) + dataframe['kdj_k_3m'] = kdj_3m['STOCHk_9_3_3'] + dataframe['kdj_d_3m'] = kdj_3m['STOCHd_9_3_3'] + dataframe['kdj_j_3m'] = 3 * dataframe['kdj_k_3m'] - 2 * dataframe['kdj_d_3m'] + # 新增 MACD 指标 macd_3m = ta.macd(dataframe['close'], fast=12, slow=26, signal=9) dataframe['macd_3m'] = macd_3m['MACD_12_26_9'] @@ -1085,6 +1091,12 @@ class FreqaiPrimer(IStrategy): df_1h['stochrsi_k_1h'] = stochrsi_1h[f'STOCHRSIk_{rsi_length_value}_{rsi_length_value}_3_3'] df_1h['stochrsi_d_1h'] = stochrsi_1h[f'STOCHRSId_{rsi_length_value}_{rsi_length_value}_3_3'] + # 新增 KDJ 指标(1h) + kdj_1h = ta.stoch(high=df_1h['high'], low=df_1h['low'], close=df_1h['close'], k=9, d=3, smooth_k=3) + df_1h['kdj_k_1h'] = kdj_1h['STOCHk_9_3_3'] + df_1h['kdj_d_1h'] = kdj_1h['STOCHd_9_3_3'] + df_1h['kdj_j_1h'] = 3 * df_1h['kdj_k_1h'] - 2 * df_1h['kdj_d_1h'] + # 新增 MACD 指标 macd_1h = ta.macd(df_1h['close'], fast=12, slow=26, signal=9) df_1h['macd_1h'] = macd_1h['MACD_12_26_9'] @@ -1193,6 +1205,7 @@ class FreqaiPrimer(IStrategy): df_1h = df_1h[[ 'date', 'rsi_1h', 'trend_1h', 'ema_50_1h', 'ema_200_1h', 'bb_lower_1h', 'bb_upper_1h', 'stochrsi_k_1h', 'stochrsi_d_1h', + 'kdj_k_1h', 'kdj_d_1h', 'kdj_j_1h', # KDJ 指标 'macd_1h', 'macd_signal_1h', 'ema_5_1h', 'ema_20_1h', 'ema5_cross_above_ema20', # === 1h 趋势特征 === 'adx_1h', 'plus_di_1h', 'minus_di_1h', # ADX 趋势强度 @@ -1317,6 +1330,12 @@ class FreqaiPrimer(IStrategy): # 条件4: RSI 进入超买区域(使用可优化的超买阈值) rsi_overbought = dataframe['rsi_1h'] > self.rsi_overbought.value + # 条件5: KDJ 高位死叉(3m KDJ 辅助出场) + kdj_3m_exit = pd.Series(False, index=dataframe.index) + 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_exit = kdj_3m_death_cross + # === 新增:1h 趋势反转检测 === # 计算趋势反转得分(0-100,得分越高越可能反转) if 'ema_bear_align_1h' in dataframe.columns and 'adx_1h' in dataframe.columns: @@ -1381,12 +1400,13 @@ class FreqaiPrimer(IStrategy): self.strategy_log(f"[{metadata['pair']}] 警告:缺少趋势特征,趋势出场禁用") # === 最终出场条件 === - # 方案1:传统条件 OR 趋势反转 + # 方案1:传统条件 OR 趋势反转 OR KDJ 高位死叉 final_condition = ( breakout_condition | volume_spike | macd_downward | rsi_overbought | + kdj_3m_exit | trend_protected_exit ) @@ -1471,6 +1491,23 @@ class FreqaiPrimer(IStrategy): tech_score += vol_score tech_components += 1 + # 1.6 KDJ 得分(3m + 1h 组合) + # 3m KDJ:低位金叉加分 + if 'kdj_k_3m' in dataframe.columns and 'kdj_d_3m' in dataframe.columns: + kdj_3m_golden = (dataframe['kdj_k_3m'] < 40) & (dataframe['kdj_k_3m'] > dataframe['kdj_d_3m']) & (dataframe['kdj_k_3m'].shift(1) <= dataframe['kdj_d_3m'].shift(1)) + kdj_3m_j_recovery = (dataframe['kdj_j_3m'] > 0) & (dataframe['kdj_j_3m'].shift(1) <= 0) + kdj_3m_score = ((kdj_3m_golden | kdj_3m_j_recovery).astype(float) * 100) + tech_score += kdj_3m_score + tech_components += 1 + + # 1h KDJ:禁止极端高位(扣分机制) + if 'kdj_k_1h' in dataframe.columns and 'kdj_j_1h' in dataframe.columns: + kdj_1h_block = (dataframe['kdj_k_1h'] > 85) & (dataframe['kdj_j_1h'] < dataframe['kdj_j_1h'].shift(1)) + # 高位见顶时扣 50 分(通过乘以 -0.5 实现) + kdj_1h_score = (~kdj_1h_block).astype(float) * 50 + 50 # 正常状态 100 分,见顶状态 50 分 + tech_score += kdj_1h_score + tech_components += 1 + # 计算技术指标平均得分 if tech_components > 0: tech_score = tech_score / tech_components