放宽熊市底部入场条件, 收紧牛市入场条件, 剧烈拉升检测升级,新增近期高点检测功能
This commit is contained in:
parent
b2cda90741
commit
b8beadbb2b
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user