只要当前EMA5在EMA20之上就满足条件

This commit is contained in:
zhangkun9038@dingtalk.com 2026-01-14 12:18:17 +08:00
parent 80acbac9f4
commit 38241423b2

View File

@ -440,6 +440,20 @@ class FreqaiPrimer(IStrategy):
df_1h['bb_lower_1h'] = bb_ma_1h - (bb_std_value * bb_std_1h) df_1h['bb_lower_1h'] = bb_ma_1h - (bb_std_value * bb_std_1h)
df_1h['bb_upper_1h'] = bb_ma_1h + (bb_std_value * bb_std_1h) df_1h['bb_upper_1h'] = bb_ma_1h + (bb_std_value * bb_std_1h)
# 添加 EMA5 和 EMA20 用于趋势过滤方案2宽松条件
df_1h['ema_5_1h'] = df_1h['close'].ewm(span=5, adjust=False).mean()
df_1h['ema_20_1h'] = df_1h['close'].ewm(span=20, adjust=False).mean()
# 检测 EMA5 向上穿越 EMA20添加安全检查
if len(df_1h) >= 2:
df_1h['ema5_cross_above_ema20'] = (
(df_1h['ema_5_1h'] > df_1h['ema_20_1h']) &
(df_1h['ema_5_1h'].shift(1) <= df_1h['ema_20_1h'].shift(1))
)
else:
# 数据不足时默认为False
df_1h['ema5_cross_above_ema20'] = False
# 使用 rolling 计算 RSI减少看前偏差 # 使用 rolling 计算 RSI减少看前偏差
delta_1h = df_1h['close'].diff() delta_1h = df_1h['close'].diff()
gain_1h = delta_1h.where(delta_1h > 0, 0).rolling(window=rsi_length_value).mean() gain_1h = delta_1h.where(delta_1h > 0, 0).rolling(window=rsi_length_value).mean()
@ -470,13 +484,13 @@ class FreqaiPrimer(IStrategy):
# 将 1h 数据重新索引到主时间框架 (3m),并填充缺失值 # 将 1h 数据重新索引到主时间框架 (3m),并填充缺失值
df_1h = df_1h.set_index('date').reindex(dataframe['date']).ffill().bfill().reset_index() df_1h = df_1h.set_index('date').reindex(dataframe['date']).ffill().bfill().reset_index()
df_1h = df_1h.rename(columns={'index': 'date'}) df_1h = df_1h.rename(columns={'index': 'date'})
# Include macd_1h and macd_signal_1h in the column selection # Include macd_1h, macd_signal_1h, ema_5_1h, ema_20_1h, ema5_cross_above_ema20 in the column selection
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', 'macd_1h', 'macd_signal_1h']].ffill() 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', 'macd_1h', 'macd_signal_1h', 'ema_5_1h', 'ema_20_1h', 'ema5_cross_above_ema20']].ffill()
# Validate that all required columns are present # Validate that all required columns are present
required_columns = ['date', 'rsi_1h', 'trend_1h', 'ema_50_1h', 'ema_200_1h', required_columns = ['date', 'rsi_1h', 'trend_1h', 'ema_50_1h', 'ema_200_1h',
'bb_lower_1h', 'bb_upper_1h', 'stochrsi_k_1h', 'stochrsi_d_1h', 'bb_lower_1h', 'bb_upper_1h', 'stochrsi_k_1h', 'stochrsi_d_1h',
'macd_1h', 'macd_signal_1h'] 'macd_1h', 'macd_signal_1h', 'ema_5_1h', 'ema_20_1h', 'ema5_cross_above_ema20']
missing_columns = [col for col in required_columns if col not in df_1h.columns] missing_columns = [col for col in required_columns if col not in df_1h.columns]
if missing_columns: if missing_columns:
logger.error(f"[{metadata['pair']}] 缺少以下列: {missing_columns}") logger.error(f"[{metadata['pair']}] 缺少以下列: {missing_columns}")
@ -485,7 +499,7 @@ class FreqaiPrimer(IStrategy):
# 确保所有需要的列都被合并 # 确保所有需要的列都被合并
required_columns = ['date', 'rsi_1h', 'trend_1h', 'ema_50_1h', 'ema_200_1h', required_columns = ['date', 'rsi_1h', 'trend_1h', 'ema_50_1h', 'ema_200_1h',
'bb_lower_1h', 'bb_upper_1h', 'stochrsi_k_1h', 'stochrsi_d_1h', 'bb_lower_1h', 'bb_upper_1h', 'stochrsi_k_1h', 'stochrsi_d_1h',
'macd_1h', 'macd_signal_1h'] 'macd_1h', 'macd_signal_1h', 'ema_5_1h', 'ema_20_1h', 'ema5_cross_above_ema20']
# 验证所需列是否存在 # 验证所需列是否存在
missing_columns = [col for col in required_columns if col not in df_1h.columns] missing_columns = [col for col in required_columns if col not in df_1h.columns]
@ -493,7 +507,7 @@ class FreqaiPrimer(IStrategy):
logger.error(f"[{metadata['pair']}] 缺少以下列: {missing_columns}") logger.error(f"[{metadata['pair']}] 缺少以下列: {missing_columns}")
raise KeyError(f"缺少以下列: {missing_columns}") raise KeyError(f"缺少以下列: {missing_columns}")
df_1h = df_1h[required_columns] # 确保包含 macd_1h 和 macd_signal_1h df_1h = df_1h[required_columns] # 确保包含所有必需的列包括EMA过滤相关列
# 合并 1h 数据 # 合并 1h 数据
dataframe = dataframe.merge(df_1h, how='left', on='date').ffill() dataframe = dataframe.merge(df_1h, how='left', on='date').ffill()
@ -640,8 +654,29 @@ class FreqaiPrimer(IStrategy):
# 辅助条件: 3m 和 15m 趋势确认(允许部分时间框架不一致) # 辅助条件: 3m 和 15m 趋势确认(允许部分时间框架不一致)
trend_confirmation = (dataframe['trend_3m'] == 1) | (dataframe['trend_15m'] == 1) trend_confirmation = (dataframe['trend_3m'] == 1) | (dataframe['trend_15m'] == 1)
# 新增EMA趋势过滤条件方案2宽松版本
# 条件1EMA5保持在EMA20之上 或 条件2最近20根1h K线内发生过向上穿越
# 这样既能捕捉趋势启动,又能在趋势延续时继续入场
if 'ema_5_1h' in dataframe.columns and 'ema_20_1h' in dataframe.columns:
# 条件1EMA5保持在EMA20之上
ema5_above_ema20 = dataframe['ema_5_1h'] > dataframe['ema_20_1h']
# 条件2最近20根1h K线内发生过向上穿越
if 'ema5_cross_above_ema20' in dataframe.columns:
# 使用rolling.max检查最近20根K线内是否有True值
recent_cross = dataframe['ema5_cross_above_ema20'].rolling(window=20, min_periods=1).max() == 1
# 两个条件满足其一即可
ema_trend_filter = ema5_above_ema20 | recent_cross
else:
# 如果没有交叉列,只用保持在上方的条件
ema_trend_filter = ema5_above_ema20
else:
# 如果列不存在创建一个全False的Series不允许入场
self.strategy_log(f"[{metadata['pair']}] 警告ema_5_1h或ema_20_1h列不存在过滤条件设为False")
ema_trend_filter = pd.Series(False, index=dataframe.index)
# 合并所有条件(减少强制性条件) # 合并所有条件(减少强制性条件)
# 至少满足多个条件中的一定数量 # 至少满足多个条件中的一定数量并且必须满足EMA趋势过滤
condition_count = ( condition_count = (
close_to_bb_lower_1h.astype(int) + close_to_bb_lower_1h.astype(int) +
rsi_condition_1h.astype(int) + rsi_condition_1h.astype(int) +
@ -650,7 +685,9 @@ class FreqaiPrimer(IStrategy):
(volume_spike | bb_width_condition).astype(int) + # 成交量或布林带宽度满足其一即可 (volume_spike | bb_width_condition).astype(int) + # 成交量或布林带宽度满足其一即可
trend_confirmation.astype(int) trend_confirmation.astype(int)
) )
final_condition = condition_count >= self.min_condition_count.value # 最终条件:基本条件 + EMA趋势过滤方案2宽松版
basic_condition = condition_count >= self.min_condition_count.value
final_condition = basic_condition & ema_trend_filter
# 设置入场信号 # 设置入场信号
dataframe.loc[final_condition, 'enter_long'] = 1 dataframe.loc[final_condition, 'enter_long'] = 1
@ -724,14 +761,29 @@ class FreqaiPrimer(IStrategy):
dataframe.loc[final_condition_updated, 'enter_price'] = dataframe.loc[final_condition_updated, 'close'] * 0.9833 dataframe.loc[final_condition_updated, 'enter_price'] = dataframe.loc[final_condition_updated, 'close'] * 0.9833
# 增强调试信息 # 增强调试信息
#self.strategy_log(f"[{metadata['pair']}] 入场条件检查:") # 确保ema_trend_filter是Series类型才能调用sum()
#self.strategy_log(f" - 价格接近布林带下轨: {close_to_bb_lower_1h.sum()} 次") if isinstance(ema_trend_filter, pd.Series):
#self.strategy_log(f" - RSI 超卖: {rsi_condition_1h.sum()} 次") ema_trend_count = ema_trend_filter.sum()
#self.strategy_log(f" - StochRSI 超卖: {stochrsi_condition_1h.sum()} 次") else:
#self.strategy_log(f" - MACD 上升趋势: {macd_condition_1h.sum()} 次") ema_trend_count = 0
#self.strategy_log(f" - 成交量或布林带宽度: {(volume_spike | bb_width_condition).sum()} 次")
#self.strategy_log(f" - 趋势确认: {trend_confirmation.sum()} 次") basic_condition_count = basic_condition.sum()
#self.strategy_log(f" - 最终条件: {final_condition.sum()} 次") final_condition_count = final_condition.sum()
self.strategy_log(f"[{metadata['pair']}] 入场条件检查:")
self.strategy_log(f" - 价格接近布林带下轨: {close_to_bb_lower_1h.sum()}")
self.strategy_log(f" - RSI 超卖: {rsi_condition_1h.sum()}")
self.strategy_log(f" - StochRSI 超卖: {stochrsi_condition_1h.sum()}")
self.strategy_log(f" - MACD 上升趋势: {macd_condition_1h.sum()}")
self.strategy_log(f" - 成交量或布林带宽度: {(volume_spike | bb_width_condition).sum()}")
self.strategy_log(f" - 趋势确认: {trend_confirmation.sum()}")
self.strategy_log(f" - EMA趋势过滤(在上方或20根K线内穿越): {ema_trend_count}")
self.strategy_log(f" - 基本条件满足: {basic_condition_count}")
self.strategy_log(f" - 最终条件(基本+EMA过滤): {final_condition_count}")
# 如果EMA条件满足但最终条件未满足输出详细信息
if ema_trend_count > 0 and final_condition_count == 0:
self.strategy_log(f"[{metadata['pair']}] 注意:检测到 {ema_trend_count} 次EMA趋势过滤满足但由于其他条件不足未能生成入场信号")
# 在populate_entry_trend方法末尾添加 # 在populate_entry_trend方法末尾添加
# 计算条件间的相关性 # 计算条件间的相关性
conditions = DataFrame({ conditions = DataFrame({
@ -740,7 +792,8 @@ class FreqaiPrimer(IStrategy):
'stochrsi': stochrsi_condition_1h, 'stochrsi': stochrsi_condition_1h,
'macd': macd_condition_1h, 'macd': macd_condition_1h,
'vol_bb': (volume_spike | bb_width_condition), 'vol_bb': (volume_spike | bb_width_condition),
'trend': trend_confirmation 'trend': trend_confirmation,
'ema_trend': ema_trend_filter
}) })
correlation = conditions.corr().mean().mean() correlation = conditions.corr().mean().mean()
#self.strategy_log(f"[{metadata['pair']}] 条件平均相关性: {correlation:.2f}") #self.strategy_log(f"[{metadata['pair']}] 条件平均相关性: {correlation:.2f}")