入场条件更严格, 考虑4h stochrsi

This commit is contained in:
zhangkun9038@dingtalk.com 2025-07-06 04:48:38 +00:00
parent d25a5809d2
commit 61b448e8be
2 changed files with 142 additions and 27 deletions

Binary file not shown.

View File

@ -9,6 +9,7 @@ from functools import reduce
from freqtrade.persistence import Trade
import talib.abstract as ta
from pandas import DataFrame
import pandas as pd
from typing import Dict
from freqtrade.strategy import (DecimalParameter, IStrategy, IntParameter)
@ -160,6 +161,62 @@ class FreqaiPrimer(IStrategy):
dataframe["volume_z_score"] = dataframe["volume_z_score"].replace([np.inf, -np.inf], 0).ffill().fillna(0)
return dataframe
def is_stochrsi_overbought(self, dataframe: DataFrame, period=10, threshold=85) -> bool:
"""
判断当前 STOCHRSI 是否在过去 N K 线中平均高于阈值 85
"""
if 'stochrsi_k' not in dataframe.columns:
# 如果列不存在,返回全 False 的 Series
return pd.Series([False] * len(dataframe), index=dataframe.index)
# 计算滚动平均值并判断是否超过阈值
avg_stochrsi = dataframe['stochrsi_k'].rolling(window=period).mean()
return avg_stochrsi > threshold
def is_bearish_market(self, dataframe: DataFrame, metadata: dict, timeframe: str = "4h") -> DataFrame:
"""
判断是否为熊市信号使用已有的 4h 指标
返回一个与 dataframe 等长的布尔 Series
"""
# 条件1价格低于布林带中轨
cond1 = dataframe['close'] < dataframe['bb_middle']
# 条件2STOCHRSI 超买后反转K > 80 并开始向下交叉 D
cond2 = (dataframe['stochrsi_k'] > 80) & (dataframe['stochrsi_k'] < dataframe['stochrsi_d'])
# 条件3出现看跌蜡烛图形态如乌云盖顶、看跌吞没等
def detect_candlestick_bearish(df, i):
open = df['open'].iloc[i]
high = df['high'].iloc[i]
low = df['low'].iloc[i]
close = df['close'].iloc[i]
prev_open = df['open'].iloc[i - 1] if i > 0 else open
prev_close = df['close'].iloc[i - 1] if i > 0 else close
# 看跌吞没形态
if open < prev_close and close > prev_open and close < prev_close:
return True
# 乌云盖顶
if close < prev_open and open > prev_close and close < ((prev_open + prev_close) / 2):
return True
# 射击之星
body = abs(open - close)
upper_wick = high - max(open, close)
lower_wick = min(open, close) - low
if upper_wick > 2 * body and lower_wick < body:
return True
return False
# 在 dataframe 上逐行应用蜡烛图识别
bearish_candle = pd.Series(index=dataframe.index, dtype=bool)
for i in range(len(dataframe)):
bearish_candle.iloc[i] = detect_candlestick_bearish(dataframe, i)
# 构造最终的熊市信号
bearish_signal = cond1 & cond2 & bearish_candle
return bearish_signal
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
pair = metadata.get('pair', 'Unknown')
@ -190,10 +247,24 @@ class FreqaiPrimer(IStrategy):
dataframe["volume_mean_20"] = dataframe["volume"].rolling(20).mean()
dataframe["volume_std_20"] = dataframe["volume"].rolling(20).std()
dataframe["volume_z_score"] = (dataframe["volume"] - dataframe["volume_mean_20"]) / dataframe["volume_std_20"]
# 计算 STOCHRSI
dataframe['stochrsi_k'], dataframe['stochrsi_d'] = stochrsi(dataframe['close'], length=14, k=3, d=3, fillna=True)
# 计算 MASTCHRIS3 (假设为 STOCHRSI K 的 3 周期 SMA)
dataframe['mastchris3'] = sma(dataframe['stochrsi_k'], length=3)
# 获取 4h 时间框架的数据
dataframe_4h = self.dp.get_pair_dataframe(pair=pair, timeframe="4h")
# 在 4h 上计算 STOCHRSI 和布林带等指标
stochrsi = ta.STOCHRSI(dataframe_4h, timeperiod=14, fastk_period=3, fastd_period=3)
dataframe_4h['stochrsi_k'] = stochrsi['fastk']
dataframe_4h['stochrsi_d'] = stochrsi['fastd']
real = ta.TYPPRICE(dataframe_4h)
upperband, middleband, lowerband = ta.BBANDS(real, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
dataframe_4h['bb_upper'] = upperband
dataframe_4h['bb_middle'] = middleband
dataframe_4h['bb_lower'] = lowerband
# 将 4h 指标映射回主时间框架3m
for col in ['stochrsi_k', 'stochrsi_d', 'bb_upper', 'bb_middle', 'bb_lower']:
dataframe[col + '_4h'] = dataframe_4h[col].reindex(dataframe.index, method='ffill').fillna(method='bfill')
# 数据清理
for col in ["ema200", "bb_upperband", "bb_middleband", "bb_lowerband", "rsi", "volume_z_score", "&-price_value_divergence", "price_value_divergence"]:
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], 0).ffill().fillna(0)
@ -292,49 +363,98 @@ class FreqaiPrimer(IStrategy):
# 获取市场趋势得分
trend_score = self.get_market_trend(dataframe=dataframe, metadata=metadata)
# 动态调整成交量阈值牛市trend_score=100-> 0.5熊市trend_score=0-> 1.5
# 动态调整成交量阈值
volume_z_score_min = 0.5
volume_z_score_max = 1.5
volume_z_score_threshold = self.linear_map(trend_score, 0, 100, volume_z_score_max, volume_z_score_min)
# 动态调整 RSI 阈值牛市trend_score=100-> 40熊市trend_score=0-> 60
# 动态调整 RSI 阈值
rsi_min = 35
rsi_max = 55
rsi_threshold = self.linear_map(trend_score, 0, 100, rsi_max, rsi_min)
# 新增:动态调整 STOCHRSI 阈值,牛市 -> 30熊市 -> 50
# 新增:动态调整 STOCHRSI 阈值
stochrsi_min = 25
stochrsi_max = 45
stochrsi_threshold = self.linear_map(trend_score, 0, 100, stochrsi_max, stochrsi_min)
if "&-price_value_divergence" in dataframe.columns:
# 计算 STOCHRSI
stochrsi = ta.STOCHRSI(dataframe, timeperiod=14, fastk_period=3, fastd_period=3)
dataframe["stochrsi_k"] = stochrsi["fastk"]
# 添加 15m 时间框架的 EMA200
dataframe_15m = self.dp.get_pair_dataframe(pair=pair, timeframe="15m")
dataframe_15m["ema200_15m"] = ta.EMA(dataframe_15m, timeperiod=200)
dataframe["ema200_15m"] = dataframe_15m["ema200_15m"].reindex(dataframe.index, method="ffill")
# 获取 4h 时间框架的数据
dataframe_4h = self.dp.get_pair_dataframe(pair=pair, timeframe="4h")
# 确保指标存在(或提前在 populate_indicators 中计算好)
if not dataframe_4h.empty and 'stochrsi_k' in dataframe_4h.columns:
stochrsi_overbought = self.is_stochrsi_overbought(dataframe_4h, period=10, threshold=85)
else:
stochrsi_overbought = pd.Series([False] * len(dataframe), index=dataframe.index)
# 映射回主时间框架
stochrsi_overbought_aligned = stochrsi_overbought.reindex(dataframe.index, method='ffill').fillna(True)
# 计算指标(或提前在 populate_indicators 中完成)
stochrsi = ta.STOCHRSI(dataframe_4h, timeperiod=14, fastk_period=3, fastd_period=3)
dataframe_4h['stochrsi_k'] = stochrsi['fastk']
dataframe_4h['stochrsi_d'] = stochrsi['fastd']
real = ta.TYPPRICE(dataframe_4h)
upperband, middleband, lowerband = ta.BBANDS(real, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
dataframe_4h['bb_upper'] = upperband
dataframe_4h['bb_middle'] = middleband
dataframe_4h['bb_lower'] = lowerband
# 使用已经准备好的 dataframe_4h
bearish_signal = self.is_bearish_market(dataframe_4h, metadata, timeframe="4h")
# 映射回主时间框架
dataframe['bearish_signal'] = bearish_signal.reindex(dataframe.index, method='ffill').fillna(False)
# 现在你可以使用 dataframe_4h 做任何判断了
if not dataframe_4h.empty:
logger.info(f"[{pair}] 成功获取 4h 数据,共 {len(dataframe_4h)} 根 K 线")
# 示例:打印最新一根 K 线的收盘价
latest_close = dataframe_4h['close'].iloc[-1]
logger.info(f"[{pair}] 4h 最新收盘价: {latest_close}")
else:
logger.warning(f"[{pair}] 无法获取 4h 数据")
if "&-price_value_divergence" in dataframe.columns:
divergence_value = dataframe['&-price_value_divergence'].iloc[-1]
volume_z_score_value = dataframe['volume_z_score'].iloc[-1]
rsi_value = dataframe['rsi'].iloc[-1]
stochrsi_value = dataframe['stochrsi_k'].iloc[-1]
bb_close_value = dataframe['close'].iloc[-1]
bb_lower_value = dataframe['bb_lowerband'].iloc[-1]
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold)
cond2 = (dataframe["volume_z_score"] > volume_z_score_threshold)
cond3 = (dataframe["rsi"] < rsi_threshold)
cond4 = (dataframe["close"] <= dataframe["bb_lowerband"])
cond5 = (dataframe["stochrsi_k"] < stochrsi_threshold)
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5
cond6 = ~self.is_bearish_market(dataframe_4h, metadata, timeframe="4h")
# cond7 定义
cond7 = ~stochrsi_overbought_aligned
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7
conditions.append(buy_condition)
logger.debug(f"[{pair}] 买入条件检查 - "
f"&-price_value_divergence={divergence_value:.6f} < {self.buy_threshold:.6f}: {cond1.iloc[-1]}, "
f"volume_z_score={volume_z_score_value:.2f} > {volume_z_score_threshold:.2f}: {cond2.iloc[-1]}, "
f"rsi={rsi_value:.2f} < {rsi_threshold:.2f}: {cond3.iloc[-1]}, "
f"close={bb_close_value:.6f} <= bb_lowerband={bb_lower_value:.6f}: {cond4.iloc[-1]}, "
f"stochrsi_k={stochrsi_value:.2f} < {stochrsi_threshold:.2f}: {cond5.iloc[-1]}, "
f"非熊市={cond6.iloc[-1]}, "
f"STOCHRSI未持续超买={cond7.iloc[-1]}")
bearish_signal = cond6.iloc[-1]
buy_condition = cond1 & cond2 & cond3 & cond4 & cond5 & cond6
conditions.append(buy_condition)
divergence_value = dataframe['&-price_value_divergence'].iloc[-1] if not dataframe['&-price_value_divergence'].isna().all() else np.nan
volume_z_score_value = dataframe['volume_z_score'].iloc[-1] if not dataframe['volume_z_score'].isna().all() else np.nan
rsi_value = dataframe['rsi'].iloc[-1] if not dataframe['rsi'].isna().all() else np.nan
stochrsi_value = dataframe['stochrsi_k'].iloc[-1] if not dataframe['stochrsi_k'].isna().all() else np.nan
logger.debug(f"[{pair}] 买入条件检查 - "
f"&-price_value_divergence={divergence_value:.6f} < {self.buy_threshold:.6f}: {cond1.iloc[-1]}, "
f"volume_z_score={volume_z_score_value:.2f} > {volume_z_score_threshold:.2f}: {cond2.iloc[-1]}, "
f"rsi={rsi_value:.2f} < {rsi_threshold:.2f}: {cond3.iloc[-1]}, "
f"close={dataframe['close'].iloc[-1]:.6f} <= bb_lowerband={dataframe['bb_lowerband'].iloc[-1]:.6f}: {cond4.iloc[-1]}, "
f"stochrsi_k={stochrsi_value:.2f} < {stochrsi_threshold:.2f}: {cond5.iloc[-1]}")
f"close={bb_close_value:.6f} <= bb_lowerband={bb_lower_value:.6f}: {cond4.iloc[-1]}, "
f"stochrsi_k={stochrsi_value:.2f} < {stochrsi_threshold:.2f}: {cond5.iloc[-1]}, "
f"非熊市={bearish_signal}")
else:
logger.warning(f"[{pair}] ⚠️ &-price_value_divergence 列缺失,跳过买入信号生成")
@ -553,10 +673,6 @@ class FreqaiPrimer(IStrategy):
# 获取当前数据
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1]
# 检查 STOCHRSI K 和 MASTCHRIS3 是否均 > 85
if last_candle['stochrsi_k'] > 85 and last_candle['mastchris3'] > 85:
return False # 阻止开多
market_trend_score = self.get_market_trend(dataframe=DataFrame, metadata={'pair': pair})
cooldown_period_minutes = self.COOLDOWN_PERIOD_MINUTES.value if market_trend_score > 50 else self.COOLDOWN_PERIOD_MINUTES.value // 2
@ -576,7 +692,6 @@ class FreqaiPrimer(IStrategy):
return False
return True
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
rate: float, time_in_force: str, exit_reason: str,
current_time: datetime, **kwargs) -> bool: