diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index bd2dcee3..5f9a25d0 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -69,17 +69,19 @@ class FreqaiPrimer(IStrategy): "verbose": -1, }, "fit_live_predictions_candles": 100, + "live_retrain_candles": 100, } def __init__(self, config: dict, *args, **kwargs): super().__init__(config, *args, **kwargs) - logger.setLevel(logging.DEBUG) + logger.setLevel(logging.DEBUG) # 保持 DEBUG 级别以查看更多日志 logger.debug("✅ 策略已初始化,日志级别设置为 DEBUG") self.trailing_stop_enabled = False self.trailing_stop_start = 0.03 self.trailing_stop_distance = 0.01 self.pair_stats = {} self.stats_logged = False + self.fit_live_predictions_candles = self.freqai_info.get("fit_live_predictions_candles", 100) def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame: dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period) @@ -165,27 +167,31 @@ class FreqaiPrimer(IStrategy): 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) + # 添加调试日志:打印关键指标 + logger.debug(f"[{pair}] 最新数据 - close:{dataframe['close'].iloc[-1]:.6f}, " + f"rsi:{dataframe['rsi'].iloc[-1]:.2f}, " + 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}") + # 获取 labels_mean 和 labels_std labels_mean = None labels_std = None - # 调试:打印 freqai_info 和 user_data_dir logger.debug(f"freqai_info identifier:{self.freqai_info['identifier']}") logger.debug(f"user_data_dir:{self.config['user_data_dir']}") - # 从最新的子目录读取 metadata.json try: model_base_dir = os.path.join(self.config["user_data_dir"], "models", self.freqai_info["identifier"]) - pair_base = pair.split('/')[0] if '/' in pair else pair # 取币对基础部分,例如 "TRUMP/USDT" -> "TRUMP" + pair_base = pair.split('/')[0] if '/' in pair else pair sub_dirs = glob.glob(os.path.join(model_base_dir, f"sub-train-{pair_base}_*")) if not sub_dirs: logger.warning(f"[{pair}] 未找到任何子目录:{model_base_dir}/sub-train-{pair_base}_*") else: - # 按时间戳排序,获取最新的子目录 latest_sub_dir = max(sub_dirs, key=lambda x: int(x.split('_')[-1])) - pair_base_lower = pair_base.lower() # 用于 metadata.json 文件名,例如 "TRUMP" -> "trump" - timestamp = latest_sub_dir.split('_')[-1] # 提取时间戳,例如 "1748743825" + pair_base_lower = pair_base.lower() + timestamp = latest_sub_dir.split('_')[-1] metadata_file = os.path.join(latest_sub_dir, f"cb_{pair_base_lower}_{timestamp}_metadata.json") if os.path.exists(metadata_file): @@ -199,22 +205,19 @@ class FreqaiPrimer(IStrategy): except Exception as e: logger.warning(f"[{pair}] 无法从子目录读取 labels_mean 和 labels_std:{e},重新计算") - # 回退:如果无法从文件读取,则重新计算 if labels_mean is None or labels_std is None: logger.warning(f"[{pair}] 无法获取 labels_mean 和 labels_std,重新计算") dataframe["&-price_value_divergence_actual"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"] dataframe["&-price_value_divergence_actual"] = dataframe["&-price_value_divergence_actual"].replace([np.inf, -np.inf], 0).ffill().fillna(0) - recent_data = dataframe["&-price_value_divergence_actual"].tail(self.freqai_info["fit_live_predictions_candles"]) + recent_data = dataframe["&-price_value_divergence_actual"].tail(self.fit_live_predictions_candles) labels_mean = recent_data.mean() labels_std = recent_data.std() if np.isnan(labels_std) or labels_std == 0: labels_std = 0.01 logger.warning(f"[{pair}] labels_std 计算异常,使用默认值 0.01") - # 存储统计信息 self.pair_stats[pair] = {"labels_mean": labels_mean, "labels_std": labels_std} - # 计算动态阈值 if labels_std > 0.015: k_buy = 1.2 k_sell = 1.5 @@ -249,8 +252,6 @@ class FreqaiPrimer(IStrategy): logger.info("==============================================") self.stats_logged = True - # 移除问题日志行,避免 KeyError - # logger.info(f"[{metadata['pair']}] 指标计算完成,列:{list(dataframe.columns)}") return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -258,17 +259,31 @@ class FreqaiPrimer(IStrategy): conditions = [] if "&-price_value_divergence" in dataframe.columns: - buy_condition = (dataframe["&-price_value_divergence"] < self.buy_threshold) - buy_condition &= (dataframe["volume_z_score"] > 1.5) - buy_condition &= (dataframe["rsi"] < 40) - buy_condition &= (dataframe["close"] <= dataframe["bb_lowerband"]) + # 逐个检查买入条件 + cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold) + cond2 = (dataframe["volume_z_score"] > 1.5) + cond3 = (dataframe["rsi"] < 40) + cond4 = (dataframe["close"] <= dataframe["bb_lowerband"]) + buy_condition = cond1 & cond2 & cond3 & cond4 conditions.append(buy_condition) + + # 添加调试日志:打印条件是否满足 + logger.debug(f"[{pair}] 买入条件检查 - " + f"&-price_value_divergence < {self.buy_threshold:.6f}: {cond1.iloc[-1]}, " + f"volume_z_score > 1.5: {cond2.iloc[-1]}, " + f"rsi < 40: {cond3.iloc[-1]}, " + f"close <= bb_lowerband: {cond4.iloc[-1]}") else: logger.warning(f"[{pair}] ⚠️ &-price_value_divergence 列缺失,跳过该条件") if len(conditions) > 0: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1 - logger.info(f"[{pair}] 入场信号触发,条件满足") + # 检查是否同时有卖出信号 + if 'exit_long' in dataframe.columns and (dataframe["exit_long"] == 1).any(): + logger.warning(f"[{pair}] 同时检测到买入和卖出信号,忽略买入信号") + dataframe['enter_long'] = 0 + else: + logger.debug(f"[{pair}] 入场信号触发,条件满足") # 改为 DEBUG 级别 return dataframe @@ -277,15 +292,22 @@ class FreqaiPrimer(IStrategy): conditions = [] if "&-price_value_divergence" in dataframe.columns: - sell_condition = (dataframe["&-price_value_divergence"] > self.sell_threshold) - sell_condition |= (dataframe["rsi"] > 75) + # 逐个检查卖出条件 + cond1 = (dataframe["&-price_value_divergence"] > self.sell_threshold) + cond2 = (dataframe["rsi"] > 75) + sell_condition = cond1 | cond2 conditions.append(sell_condition) + + # 添加调试日志:打印条件是否满足 + logger.debug(f"[{pair}] 卖出条件检查 - " + f"&-price_value_divergence > {self.sell_threshold:.6f}: {cond1.iloc[-1]}, " + f"rsi > 75: {cond2.iloc[-1]}") else: logger.warning(f"[{pair}] ⚠️ &-price_value_divergence 列缺失,跳过该条件") if len(conditions) > 0: dataframe.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1 - logger.info(f"[{pair}] 出场信号触发,条件满足") + logger.debug(f"[{pair}] 出场信号触发,条件满足") # 改为 DEBUG 级别 return dataframe @@ -319,14 +341,18 @@ class FreqaiPrimer(IStrategy): def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, **kwargs) -> bool: + # 添加调试日志:检查是否允许买入 recent_trades = Trade.get_trades( pair=pair, is_open=False, close_date=current_time - datetime.timedelta(minutes=5) ).all() if len(recent_trades) > 0: - logger.info(f"[{pair}] 5分钟内有近期交易,跳过本次入场") + logger.info(f"[{pair}] 5分钟内有近期交易({len(recent_trades)} 笔),跳过本次入场") return False + + # 检查其他限制(例如资金、仓位等) + logger.debug(f"[{pair}] 允许买入 - 订单类型:{order_type}, 数量:{amount:.2f}, 价格:{rate:.6f}, 时间:{current_time}") self.trailing_stop_enabled = False return True