放宽熊市底部入场条件, 收紧牛市入场条件, 剧烈拉升检测升级,新增近期高点检测功能

This commit is contained in:
zhangkun9038@dingtalk.com 2025-08-29 09:01:38 +08:00
parent b2cda90741
commit b8beadbb2b

View File

@ -750,12 +750,15 @@ class FreqaiPrimer(IStrategy):
# 剧烈拉升检测参数
H1_RAPID_RISE_LOOKBACK = 200 # 检查最近200根K线
H1_RAPID_RISE_THRESHOLD = 5 # 价格拉升阈值(百分比)
H1_MAX_CANDLES = 2 # 最多连续检查的K线数量
H1_MAX_CANDLES = 3 # 最多连续检查的K线数量
H1_UNSTABLE_REGION_THRESHOLD = 60 # 不稳定区域阈值百分比超过60%认为是中上部分)
H1_VOLUME_RATIO_THRESHOLD = 2.0 # 成交量比率阈值超过2倍认为异常放大
H1_VOLATILITY_THRESHOLD = 1.5 # 波动率阈值超过1.5倍标准差认为波动过大
def detect_h1_rapid_rise(self, dataframe: DataFrame, metadata: dict) -> tuple[bool, float]:
"""
检测最近K线中是否发生了剧烈拉升情况
简化逻辑回看1小时k线200根出现3周期以内拉升5%以上就忽略此币对入场信号
参数:
dataframe: 当前时间框架的数据
@ -767,61 +770,129 @@ class FreqaiPrimer(IStrategy):
pair = metadata.get('pair', 'Unknown')
try:
# 使用当前时间框架的数据而不仅仅是1小时框架
current_timeframe_data = dataframe.copy()
if len(current_timeframe_data) < self.H1_RAPID_RISE_LOOKBACK:
# 检查数据是否足够
if len(dataframe) < self.H1_RAPID_RISE_LOOKBACK:
logger.info(f"[{pair}] K线数据不足{self.H1_RAPID_RISE_LOOKBACK}根,无法进行剧烈拉升检测")
return False, 0.0
# 获取最近200根K线
recent_data = current_timeframe_data[-self.H1_RAPID_RISE_LOOKBACK:]
recent_data = dataframe[-self.H1_RAPID_RISE_LOOKBACK:]
# 初始化剧烈拉升标志
rapid_rise_detected = False
max_cumulative_change = 0
# 检查最多连续2根K线内的累计涨幅
for i in range(1, len(recent_data)):
# 计算从i-n到i的累计涨幅
for n in range(1, self.H1_MAX_CANDLES + 1):
if i - n >= 0:
start_price = recent_data['close'].iloc[i - n]
end_price = recent_data['close'].iloc[i]
cumulative_change = ((end_price - start_price) / start_price) * 100
if cumulative_change > max_cumulative_change:
max_cumulative_change = cumulative_change
if cumulative_change >= self.H1_RAPID_RISE_THRESHOLD:
rapid_rise_detected = True
break
if rapid_rise_detected:
# 检查3根K线内的累计涨幅
for i in range(self.H1_MAX_CANDLES, len(recent_data)):
start_price = recent_data['close'].iloc[i - self.H1_MAX_CANDLES]
end_price = recent_data['close'].iloc[i]
cumulative_change = ((end_price - start_price) / start_price) * 100
if cumulative_change > max_cumulative_change:
max_cumulative_change = cumulative_change
if cumulative_change >= self.H1_RAPID_RISE_THRESHOLD:
rapid_rise_detected = True
break
# 获取当前价格
current_price = dataframe['close'].iloc[-1] if len(dataframe) > 0 else 0
# 计算当前价格在最近价格范围中的位置百分比
# 获取当前价格并计算在最近价格范围中的位置百分比
current_price = dataframe['close'].iloc[-1]
low_price = recent_data['close'].min()
high_price = recent_data['close'].max()
if high_price > low_price:
price_position_pct = ((current_price - low_price) / (high_price - low_price)) * 100
else:
price_position_pct = 0.0
price_position_pct = ((current_price - low_price) / (high_price - low_price)) * 100 if high_price > low_price else 0.0
# 判断当前是否处于不稳固区域(剧烈拉升后价格处于中上部分)
is_unstable_region = rapid_rise_detected and (price_position_pct >= self.H1_UNSTABLE_REGION_THRESHOLD)
logger.info(f"[{pair}] 剧烈拉升检测 - 3根K线内最大累计涨幅: {max_cumulative_change:.2f}%, 阈值: {self.H1_RAPID_RISE_THRESHOLD}%, 检测结果: {rapid_rise_detected}")
logger.info(f"[{pair}] 剧烈拉升检测 - 最大{self.H1_MAX_CANDLES}根K线累计涨幅: {max_cumulative_change:.2f}%, 阈值: {self.H1_RAPID_RISE_THRESHOLD}%, 不稳固区域: {is_unstable_region}")
return is_unstable_region, price_position_pct
# 简化逻辑只要检测到剧烈拉升就返回True表示不稳固区域
return rapid_rise_detected, price_position_pct
except Exception as e:
logger.error(f"[{pair}] 剧烈拉升检测错误: {str(e)}")
return False, 0.0
def detect_recent_high_position(self, dataframe: DataFrame, lookback: int = 50) -> int:
"""
检测价格在近期高点中的位置
参数:
dataframe: 包含OHLCV数据的DataFrame
lookback: 回顾周期数默认为50
返回:
int: 0表示非近期高点附近1表示接近近期高点2表示处于近期高点
"""
if len(dataframe) < lookback:
return 0
recent_data = dataframe[-lookback:]
recent_high = recent_data['high'].max()
current_price = dataframe['close'].iloc[-1]
# 计算当前价格与近期高点的比率
high_ratio = current_price / recent_high
# 判断价格位置
if high_ratio >= 0.98:
return 2 # 处于近期高点
elif high_ratio >= 0.95:
return 1 # 接近近期高点
else:
return 0 # 非近期高点附近
def detect_price_bottom_pattern(self, dataframe: DataFrame) -> bool:
"""
检测价格是否处于底部形态
参数:
dataframe: 包含OHLCV数据的DataFrame
返回:
bool: True 如果检测到底部形态否则 False
"""
if len(dataframe) < 20:
return False
# 最近5根K线的最低价是否接近20周期最低价
recent_low = dataframe['low'].iloc[-5:].min()
period_low = dataframe['low'].rolling(window=20).min().iloc[-1]
# 价格是否接近布林带下轨
if 'bb_lowerband' in dataframe.columns:
bb_lower = dataframe['bb_lowerband'].iloc[-1]
close = dataframe['close'].iloc[-1]
# 价格在布林带下轨附近2%以内
near_bb_low = close <= bb_lower * 1.02
else:
near_bb_low = False
# RSI是否处于超卖区域
if 'rsi' in dataframe.columns:
rsi = dataframe['rsi'].iloc[-1]
oversold_rsi = rsi < 30
else:
oversold_rsi = False
# 底部反转K线模式如锤子线
# 收盘价接近最高价,开盘价接近最低价,实体较小
if len(dataframe) > 1:
current = dataframe.iloc[-1]
prev = dataframe.iloc[-2]
# 锤子线形态:实体较小,下影线较长
hammer = (current['close'] > current['open'] and
(current['close'] - current['open']) < (current['high'] - current['low']) * 0.3 and
(current['open'] - current['low']) > (current['high'] - current['low']) * 0.6)
# 看涨吞没形态
engulfing = (prev['close'] < prev['open'] and
current['close'] > current['open'] and
current['close'] > prev['open'] and
current['open'] < prev['close'])
else:
hammer = False
engulfing = False
# 底部形态判定:满足至少两个条件
is_bottom = (recent_low >= period_low * 0.98 and
(near_bb_low or oversold_rsi or hammer or engulfing))
return is_bottom
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
pair = metadata.get('pair', 'Unknown')
original_length = len(dataframe)
@ -835,11 +906,19 @@ class FreqaiPrimer(IStrategy):
# 检测是否发生剧烈拉升
is_unstable_region, price_position_pct = self.detect_h1_rapid_rise(dataframe=dataframe, metadata=metadata)
# 检查趋势得分是否低于阈值,如果是则禁止入场
if trend_score < self.ENTRY_TREND_SCORE_THRESHOLD:
# 检测价格在近期高点中的位置
high_position = self.detect_recent_high_position(dataframe=dataframe)
# 检测是否处于价格底部形态
is_price_bottom = self.detect_price_bottom_pattern(dataframe=dataframe)
# 检查趋势得分是否低于阈值,如果是则禁止入场,但对于明显底部形态可以适当放宽
if trend_score < self.ENTRY_TREND_SCORE_THRESHOLD and not is_price_bottom:
dataframe['enter_long'] = 0
logger.info(f"[{pair}] 趋势得分 {trend_score} 低于阈值 {self.ENTRY_TREND_SCORE_THRESHOLD},禁止入场")
return dataframe
elif trend_score < self.ENTRY_TREND_SCORE_THRESHOLD and is_price_bottom:
logger.info(f"[{pair}] 趋势得分 {trend_score} 低于阈值 {self.ENTRY_TREND_SCORE_THRESHOLD},但检测到底部形态,放宽入场条件")
# 计算EMA50和EMA20 - 用于不同市场状态的入场条件
dataframe["ema50"] = ta.EMA(dataframe, timeperiod=50)
@ -890,17 +969,15 @@ class FreqaiPrimer(IStrategy):
logger.info(f"[{pair}] 市场状态: {market_regime}, 阈值调整: {regime_adj['threshold_mult']}, 严格度调整: {regime_adj['strict_mult']}")
if is_green_channel:
# 🟢 牛市绿色通道持仓≤2个25USDT入场5条件需要满足4个且价格必须低于EMA50
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold * 1.8) # 超宽松偏离度
cond2 = (dataframe["volume_z_score"] > volume_z_score_threshold * 0.4) # 超低成交量要求
cond3 = (dataframe["rsi"] < rsi_threshold * 1.4) # 超宽松RSI
cond4 = (dataframe["close"] <= dataframe["bb_upperband"] * 1.05) # 允许上轨附近
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold * 1.4) # 超宽松STOCHRSI
# 🟢 牛市绿色通道持仓≤2个25USDT入场5条件全部需要满足且价格必须低于EMA50
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold * 1.5) # 调整为1.5倍,更严格
cond2 = (dataframe["volume_z_score"] > volume_z_score_threshold * 0.6) # 提高成交量要求至0.6倍
cond3 = (dataframe["rsi"] < rsi_threshold * 1.2) # 调整为1.2倍,更严格
cond4 = (dataframe["close"] <= dataframe["bb_upperband"] * 1.02) # 更严格限制在上轨附近
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold * 1.2) # 调整为1.2倍,更严格
core_conditions = [cond1, cond2, cond3, cond4, cond5]
# 使用向量化操作计算满足条件的数量
satisfied_count_vector = cond1.astype(int) + cond2.astype(int) + cond3.astype(int) + cond4.astype(int) + cond5.astype(int)
buy_condition = (satisfied_count_vector >= 4) & price_below_ema50 & ~is_unstable_region
# 必须全部满足5个条件
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & price_below_ema50 & ~is_unstable_region & (high_position == 0)
# 仅在日志中使用最后一行的值
if len(dataframe) > 0:
@ -909,27 +986,40 @@ class FreqaiPrimer(IStrategy):
elif trend_status == "bullish":
# 牛市正常通道:持仓>2个75USDT入场必须满足全部7个条件且价格必须低于EMA50
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold * 1.5) # 放宽到1.5
cond2 = (dataframe["volume_z_score"] > volume_z_score_threshold * 0.7) # 降低成交量要求
cond3 = (dataframe["rsi"] < rsi_threshold * 1.2) # 放宽RSI要求
cond4 = (dataframe["close"] <= dataframe["bb_upperband"]) # 可以在上轨附近入场
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold * 1.2) # 放宽STOCHRSI要求
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold * 1.2) # 提高要求至1.2
cond2 = (dataframe["volume_z_score"] > volume_z_score_threshold * 0.8) # 提高成交量要求至0.8倍
cond3 = (dataframe["rsi"] < rsi_threshold * 1.0) # 提高要求至1.0倍
cond4 = (dataframe["close"] <= dataframe["bb_upperband"] * 0.98) # 更严格限制在上轨下方
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold * 1.0) # 提高要求至1.0倍
cond6 = pd.Series([True] * len(dataframe), index=dataframe.index) # 取消熊市过滤
cond7 = pd.Series([True] * len(dataframe), index=dataframe.index) # 取消超买过滤
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & price_below_ema50 & ~is_unstable_region
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & price_below_ema50 & ~is_unstable_region & (high_position == 0)
logger.info(f"[{pair}] 🚀 牛市正常通道:持仓{open_trades}>2个75USDT入场必须满足全部7个条件")
elif trend_status == "bearish":
# 下跌趋势严格入场条件只抄底且价格必须高于EMA20
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold * 0.7) # 严格到0.7倍
cond2 = (dataframe["volume_z_score"] > volume_z_score_threshold * 1.3) # 提高成交量要求
cond3 = (dataframe["rsi"] < rsi_threshold * 0.8) # 严格RSI要求
cond4 = (dataframe["close"] <= dataframe["bb_lowerband"] * 0.95) # 必须跌破下轨
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold * 0.8) # 严格STOCHRSI要求
cond6 = ~bearish_signal_aligned # 保持熊市过滤
cond7 = ~stochrsi_overbought_aligned # 保持超买过滤
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & price_above_ema20 & ~is_unstable_region
logger.info(f"[{pair}] 📉 下跌趋势策略:严格入场条件")
if is_price_bottom:
# 检测到底部形态时,适当放宽条件
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold * 1.0) # 放宽到1.0倍
cond2 = (dataframe["volume_z_score"] > volume_z_score_threshold * 0.8) # 降低成交量要求
cond3 = (dataframe["rsi"] < rsi_threshold * 0.9) # 放宽RSI要求
cond4 = (dataframe["close"] <= dataframe["bb_lowerband"] * 1.05) # 允许在布林带下轨附近
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold * 0.9) # 放宽STOCHRSI要求
cond6 = pd.Series([True] * len(dataframe), index=dataframe.index) # 取消熊市过滤
cond7 = pd.Series([True] * len(dataframe), index=dataframe.index) # 取消超买过滤
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & price_above_ema20 & ~is_unstable_region
logger.info(f"[{pair}] 📉 下跌趋势但检测到底部形态,放宽入场条件")
else:
# 正常熊市条件
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold * 0.7) # 严格到0.7倍
cond2 = (dataframe["volume_z_score"] > volume_z_score_threshold * 1.3) # 提高成交量要求
cond3 = (dataframe["rsi"] < rsi_threshold * 0.8) # 严格RSI要求
cond4 = (dataframe["close"] <= dataframe["bb_lowerband"] * 0.95) # 必须跌破下轨
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold * 0.8) # 严格STOCHRSI要求
cond6 = ~bearish_signal_aligned # 保持熊市过滤
cond7 = ~stochrsi_overbought_aligned # 保持超买过滤
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & price_above_ema20 & ~is_unstable_region & (high_position == 0)
logger.info(f"[{pair}] 📉 下跌趋势策略:严格入场条件")
else: # ranging
# 震荡趋势close必须低于ema50
@ -940,7 +1030,7 @@ class FreqaiPrimer(IStrategy):
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold)
cond6 = ~bearish_signal_aligned
cond7 = ~stochrsi_overbought_aligned
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & price_below_ema50 & ~is_unstable_region
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & price_below_ema50 & ~is_unstable_region & (high_position == 0)
logger.info(f"[{pair}] ⚖️ 震荡趋势策略close必须低于ema20且高于ema50")
# 绿色通道和趋势状态的条件已经设置好buy_condition
@ -966,6 +1056,10 @@ class FreqaiPrimer(IStrategy):
else:
logger.info(f"[{pair}] ✅ 价格走势正常,当前价格位置: {price_position_pct:.2f}%")
# 记录近期高点位置检测结果
high_position_labels = {0: "非近期高点", 1: "接近近期高点", 2: "处于近期高点"}
logger.info(f"[{pair}] 近期高点位置: {high_position_labels[high_position]}high_position={high_position}")
# 获取条件结果的最后一行值(仅用于日志)
cond1_last = cond1.iloc[-1]
cond2_last = cond2.iloc[-1]
@ -982,6 +1076,7 @@ class FreqaiPrimer(IStrategy):
("rsi", rsi_value, "<", rsi_threshold, cond3_last),
("close <= bb_lowerband", bb_close_value, "<=", bb_lower_value, cond4_last),
("stochrsi_k", stochrsi_value, "<", stochrsi_threshold, cond5_last),
("底部形态", None, None, None, is_price_bottom),
]
# 根据趋势状态添加对应的条件6和7