for test make 4h to 1h

This commit is contained in:
Ubuntu 2025-07-09 17:30:49 +08:00
parent 435e278676
commit 03a824ea7a
2 changed files with 65 additions and 61 deletions

View File

@ -172,55 +172,55 @@ class FreqaiPrimer(IStrategy):
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") -> pd.Series:
def is_bearish_market(self, dataframe: DataFrame, metadata: dict, timeframe: str = "1h") -> pd.Series:
pair = metadata.get('pair', 'Unknown')
logger.debug(f"[{pair}] 开始计算 4h 熊市信号")
logger.debug(f"[{pair}] 开始计算 1h 熊市信号")
# 防御性检查
required_columns = ['close_4h', 'high_4h', 'low_4h', 'open_4h', 'stochrsi_k_4h', 'stochrsi_d_4h', 'bb_middle_4h']
required_columns = ['close_1h', 'high_1h', 'low_1h', 'open_1h', 'stochrsi_k_1h', 'stochrsi_d_1h', 'bb_middle_1h']
missing = [col for col in required_columns if col not in dataframe.columns]
if missing:
logger.error(f"[{pair}] 缺少必要列:{missing},返回全 False")
return pd.Series([False] * len(dataframe), index=dataframe.index)
# 检查 volume_z_score_4h 是否存在
has_volume_z_score = 'volume_z_score_4h' in dataframe.columns and not dataframe['volume_z_score_4h'].isna().all()
# 检查 volume_z_score_1h 是否存在
has_volume_z_score = 'volume_z_score_1h' in dataframe.columns and not dataframe['volume_z_score_1h'].isna().all()
# 条件 a价格远低于布林带中轨低于中轨 1% 以上)
cond_a = dataframe['close_4h'] < dataframe['bb_middle_4h'] * 0.99
cond_a = dataframe['close_1h'] < dataframe['bb_middle_1h'] * 0.99
# 条件 bSTOCHRSI 超过90
cond_b = dataframe['stochrsi_k_4h'] > 90
cond_b = dataframe['stochrsi_k_1h'] > 90
# 条件 c看跌蜡烛图形态
open_4h = dataframe['open_4h']
close_4h = dataframe['close_4h']
high_4h = dataframe['high_4h']
low_4h = dataframe['low_4h']
prev_open = open_4h.shift(1)
prev_close = close_4h.shift(1)
open_1h = dataframe['open_1h']
close_1h = dataframe['close_1h']
high_1h = dataframe['high_1h']
low_1h = dataframe['low_1h']
prev_open = open_1h.shift(1)
prev_close = close_1h.shift(1)
cond_engulfing = (
(prev_close > prev_open) &
(close_4h < open_4h) &
(close_4h < prev_open) &
(open_4h > prev_close)
(close_1h < open_1h) &
(close_1h < prev_open) &
(open_1h > prev_close)
)
cond_dark_cloud_cover = (
(prev_close > prev_open) &
(open_4h > prev_close) &
(close_4h < (prev_open + prev_close) / 2) &
(close_4h < open_4h)
(open_1h > prev_close) &
(close_1h < (prev_open + prev_close) / 2) &
(close_1h < open_1h)
)
body = abs(open_4h - close_4h)
upper_wick = high_4h - np.maximum(open_4h, close_4h)
lower_wick = np.minimum(open_4h, close_4h) - low_4h
cond_shooting_star = (upper_wick > 2 * body) & (lower_wick < body) & (close_4h < open_4h)
body = abs(open_1h - close_1h)
upper_wick = high_1h - np.maximum(open_1h, close_1h)
lower_wick = np.minimum(open_1h, close_1h) - low_1h
cond_shooting_star = (upper_wick > 2 * body) & (lower_wick < body) & (close_1h < open_1h)
cond_stochrsi_high = self.is_stochrsi_overbought(dataframe, period=10, threshold=85)
cond_c = cond_engulfing | cond_dark_cloud_cover | cond_shooting_star | cond_stochrsi_high
# 条件 d成交量显著下降
cond_d = dataframe['volume_z_score_4h'] < -1.0 if has_volume_z_score else pd.Series([False] * len(dataframe), index=dataframe.index)
cond_d = dataframe['volume_z_score_1h'] < -1.0 if has_volume_z_score else pd.Series([False] * len(dataframe), index=dataframe.index)
# 综合熊市信号(至少满足两个条件)
bearish_signal = (cond_a & cond_b) | (cond_a & cond_c) | (cond_b & cond_c) | (cond_a & cond_d)
@ -250,8 +250,8 @@ class FreqaiPrimer(IStrategy):
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
计算主时间框架3m 4h 时间框架的指标并映射到主 dataframe
包含 FreqAI 预测布林带RSI成交量 Z 分数等并确保 4h 数据列完整性
计算主时间框架3m 1h 时间框架的指标并映射到主 dataframe
包含 FreqAI 预测布林带RSI成交量 Z 分数等并确保 1h 数据列完整性
"""
pair = metadata.get('pair', 'Unknown')
logger.info(f"[{pair}] 当前可用列调用FreqAI前{list(dataframe.columns)}")
@ -272,46 +272,46 @@ class FreqaiPrimer(IStrategy):
dataframe["stochrsi_k"] = stochrsi["fastk"]
dataframe["stochrsi_d"] = stochrsi["fastd"]
# 获取 4h 时间框架数据
dataframe_4h = self.dp.get_pair_dataframe(pair=pair, timeframe="4h")
if dataframe_4h.empty or len(dataframe_4h) < 50:
logger.warning(f"[{pair}] 4h 数据为空或不足({len(dataframe_4h)} 根K线初始化空列")
for col in ['open_4h', 'high_4h', 'low_4h', 'close_4h', 'stochrsi_k_4h', 'stochrsi_d_4h',
'bb_upper_4h', 'bb_middle_4h', 'bb_lower_4h', 'volume_z_score_4h']:
# 获取 1h 时间框架数据
dataframe_1h = self.dp.get_pair_dataframe(pair=pair, timeframe="1h")
if dataframe_1h.empty or len(dataframe_1h) < 50:
logger.warning(f"[{pair}] 1h 数据为空或不足({len(dataframe_1h)} 根K线初始化空列")
for col in ['open_1h', 'high_1h', 'low_1h', 'close_1h', 'stochrsi_k_1h', 'stochrsi_d_1h',
'bb_upper_1h', 'bb_middle_1h', 'bb_lower_1h', 'volume_z_score_1h']:
dataframe[col] = np.nan
else:
# 计算 4h 指标
if len(dataframe_4h) >= 20: # 确保有足够数据计算 rolling(20)
stochrsi_4h = ta.STOCHRSI(dataframe_4h, timeperiod=14, fastk_period=3, fastd_period=3)
dataframe_4h['stochrsi_k'] = stochrsi_4h['fastk']
dataframe_4h['stochrsi_d'] = stochrsi_4h['fastd']
real = ta.TYPPRICE(dataframe_4h)
# 计算 1h 指标
if len(dataframe_1h) >= 20: # 确保有足够数据计算 rolling(20)
stochrsi_1h = ta.STOCHRSI(dataframe_1h, timeperiod=14, fastk_period=3, fastd_period=3)
dataframe_1h['stochrsi_k'] = stochrsi_1h['fastk']
dataframe_1h['stochrsi_d'] = stochrsi_1h['fastd']
real = ta.TYPPRICE(dataframe_1h)
upperband, middleband, lowerband = ta.BBANDS(real, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
dataframe_4h['bb_upper_4h'] = upperband
dataframe_4h['bb_middle_4h'] = middleband
dataframe_4h['bb_lower_4h'] = lowerband
dataframe_4h['volume_mean_20_4h'] = dataframe_4h["volume"].rolling(20).mean()
dataframe_4h['volume_std_20_4h'] = dataframe_4h["volume"].rolling(20).std()
dataframe_4h['volume_z_score_4h'] = (dataframe_4h["volume"] - dataframe_4h['volume_mean_20_4h']) / dataframe_4h['volume_std_20_4h']
dataframe_1h['bb_upper_1h'] = upperband
dataframe_1h['bb_middle_1h'] = middleband
dataframe_1h['bb_lower_1h'] = lowerband
dataframe_1h['volume_mean_20_1h'] = dataframe_1h["volume"].rolling(20).mean()
dataframe_1h['volume_std_20_1h'] = dataframe_1h["volume"].rolling(20).std()
dataframe_1h['volume_z_score_1h'] = (dataframe_1h["volume"] - dataframe_1h['volume_mean_20_1h']) / dataframe_1h['volume_std_20_1h']
# 清理 NaN 和无穷值
dataframe_4h['volume_z_score_4h'] = dataframe_4h['volume_z_score_4h'].replace([np.inf, -np.inf], 0).ffill().fillna(0)
dataframe_1h['volume_z_score_1h'] = dataframe_1h['volume_z_score_1h'].replace([np.inf, -np.inf], 0).ffill().fillna(0)
else:
logger.warning(f"[{pair}] 4h 数据不足以计算 volume_z_score_4h{len(dataframe_4h)} 根K线需至少20根")
dataframe_4h['volume_z_score_4h'] = np.nan
logger.warning(f"[{pair}] 1h 数据不足以计算 volume_z_score_1h{len(dataframe_1h)} 根K线需至少20根")
dataframe_1h['volume_z_score_1h'] = np.nan
# 映射 4h 数据到主时间框架
for col in ['open', 'high', 'low', 'close', 'stochrsi_k', 'stochrsi_d', 'bb_upper_4h', 'bb_middle_4h', 'bb_lower_4h', 'volume_z_score_4h']:
if col in dataframe_4h.columns:
dataframe[col if col.endswith('_4h') else f"{col}_4h"] = dataframe_4h[col].reindex(dataframe.index, method='ffill').bfill()
# 映射 1h 数据到主时间框架
for col in ['open', 'high', 'low', 'close', 'stochrsi_k', 'stochrsi_d', 'bb_upper_1h', 'bb_middle_1h', 'bb_lower_1h', 'volume_z_score_1h']:
if col in dataframe_1h.columns:
dataframe[col if col.endswith('_1h') else f"{col}_1h"] = dataframe_1h[col].reindex(dataframe.index, method='ffill').bfill()
else:
logger.warning(f"[{pair}] 4h 数据缺少列 {col},初始化为空")
dataframe[col if col.endswith('_4h') else f"{col}_4h"] = np.nan
logger.warning(f"[{pair}] 1h 数据缺少列 {col},初始化为空")
dataframe[col if col.endswith('_1h') else f"{col}_1h"] = np.nan
# 数据清理:处理 NaN 和无穷值
for col in ["ema200", "bb_upperband", "bb_middleband", "bb_lowerband", "rsi", "volume_z_score",
"&-price_value_divergence", "price_value_divergence", "open_4h", "high_4h", "low_4h",
"close_4h", "stochrsi_k_4h", "stochrsi_d_4h", "bb_upper_4h", "bb_middle_4h", "bb_lower_4h",
"volume_z_score_4h"]:
"&-price_value_divergence", "price_value_divergence", "open_1h", "high_1h", "low_1h",
"close_1h", "stochrsi_k_1h", "stochrsi_d_1h", "bb_upper_1h", "bb_middle_1h", "bb_lower_1h",
"volume_z_score_1h"]:
if col in dataframe.columns:
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], 0).ffill().fillna(0)
@ -385,9 +385,9 @@ class FreqaiPrimer(IStrategy):
f"&-price_value_divergence: {dataframe['&-price_value_divergence'].iloc[-1]:.6f}, "
f"volume_z_score: {dataframe['volume_z_score'].iloc[-1]:.2f}, "
f"bb_lowerband: {dataframe['bb_lowerband'].iloc[-1]:.6f}, "
f"close_4h: {dataframe['close_4h'].iloc[-1] if 'close_4h' in dataframe else 'N/A'}, "
f"stochrsi_k_4h: {dataframe['stochrsi_k_4h'].iloc[-1] if 'stochrsi_k_4h' in dataframe else 'N/A'}, "
f"volume_z_score_4h: {dataframe['volume_z_score_4h'].iloc[-1] if 'volume_z_score_4h' in dataframe else 'N/A'}")
f"close_1h: {dataframe['close_1h'].iloc[-1] if 'close_1h' in dataframe else 'N/A'}, "
f"stochrsi_k_1h: {dataframe['stochrsi_k_1h'].iloc[-1] if 'stochrsi_k_1h' in dataframe else 'N/A'}, "
f"volume_z_score_1h: {dataframe['volume_z_score_1h'].iloc[-1] if 'volume_z_score_1h' in dataframe else 'N/A'}")
if not self.stats_logged:
logger.info("===== 所有币对的 labels_mean 和 labels_std 汇总 =====")
@ -419,7 +419,7 @@ class FreqaiPrimer(IStrategy):
stochrsi_threshold = self.linear_map(trend_score, 0, 100, stochrsi_max, stochrsi_min)
# 计算熊市信号和 STOCHRSI 超买信号
bearish_signal = self.is_bearish_market(dataframe, metadata, timeframe="4h")
bearish_signal = self.is_bearish_market(dataframe, metadata, timeframe="1h")
bearish_signal_aligned = bearish_signal.reindex(dataframe.index, method='ffill').fillna(False)
stochrsi_overbought = self.is_stochrsi_overbought(dataframe, period=10, threshold=85)
stochrsi_overbought_aligned = stochrsi_overbought.reindex(dataframe.index, method='ffill').fillna(True)

View File

@ -8,7 +8,7 @@ def analyze_candlestick_data(file_path):
# 查看数据集行数和列数
rows, columns = df.shape
if rows < 500:
if rows < 1000:
# 短表数据行数少于500查看全量数据信息
print('数据全部内容信息:')
print(df.to_csv(sep='\t', na_rep='nan'))
@ -17,6 +17,10 @@ def analyze_candlestick_data(file_path):
print('数据前几行内容信息:')
print(df.head().to_csv(sep='\t', na_rep='nan'))
# 查看数据最后几行信息
print('数据最后几行内容信息:')
print(df.tail().to_csv(sep='\t', na_rep='nan'))
# 查看数据的基本信息
print('数据基本信息:')
df.info()