diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index 08ae9622..9b5277a6 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -51,7 +51,7 @@ class FreqaiPrimer(IStrategy): } freqai_info = { - "identifier": "divergence_model", # 注意:实际目录是 test58,可能需要调整 + "identifier": "test58", # 与实际目录名一致,修正为 test58 "model": "LightGBMRegressor", "feature_parameters": { "include_timeframes": ["3m", "15m", "1h"], @@ -173,12 +173,7 @@ class FreqaiPrimer(IStrategy): # 从最新的子目录读取 metadata.json try: - # 优先使用实际目录名 test58(硬编码,临时解决方案) - # model_base_dir = "/freqtrade/user_data/models/test58" - - # 如果需要动态获取(推荐),取消注释以下代码: model_base_dir = os.path.join(self.config["user_data_dir"], "models", self.freqai_info["identifier"]) - pair_clean = pair.replace('/', '_') # 将 "KAITO/USDT" 转换为 "KAITO_USDT" sub_dirs = glob.glob(os.path.join(model_base_dir, f"sub-train-{pair_clean}_*")) @@ -244,6 +239,92 @@ class FreqaiPrimer(IStrategy): logger.info(f"[{pair}] 动态卖出阈值:{self.sell_threshold:.4f}") if not self.stats_logged: - logger.info("===== 所有币对的 labels_mean 和 labels_std 汇总... + logger.info("===== 所有币对的 labels_mean 和 labels_std 汇总 =====") + for p, stats in self.pair_stats.items(): + logger.info(f"[{p}] labels_mean:{stats['labels_mean']:.4f}, labels_std:{stats['labels_std']:.4f}") + logger.info("==============================================") + self.stats_logged = True -出错了,请刷新以重新连接或重试。 + logger.info(f"[{metadata['pair']}] 指标计算完成,列:{list(dataframe.columns)}") + return dataframe + + def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + if "&-price_value_divergence" in df.columns: + buy_condition = (df["&-price_value_divergence"] < self.buy_threshold) + buy_condition &= (df["volume_z_score"] > 1.5) + buy_condition &= (df["rsi"] < 40) + buy_condition &= (df["close"] <= df["bb_lowerband"]) + conditions.append(buy_condition) + else: + logger.warning("⚠️ &-price_value_divergence 列缺失,跳过该条件") + + if len(conditions) > 0: + df.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1 + logger.info(f"[{metadata['pair']}] 入场信号触发,条件满足") + + return df + + def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + if "&-price_value_divergence" in df.columns: + sell_condition = (df["&-price_value_divergence"] > self.sell_threshold) + sell_condition |= (df["rsi"] > 75) + conditions.append(sell_condition) + else: + logger.warning("⚠️ &-price_value_divergence 列缺失,跳过该条件") + + if len(conditions) > 0: + df.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1 + logger.info(f"[{metadata['pair']}] 出场信号触发,条件满足") + + return df + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, + min_roi: Dict[float, float], max_profit: float): + hold_time = (current_time - trade.open_date_utc).total_seconds() / 60 + + if hold_time < 15: + logger.info(f"[{trade.pair}] 持仓时间 {hold_time:.1f} 分钟,未达到最小持仓时间 15 分钟,暂不退出") + return None + + profit_ratio = (current_rate - trade.open_rate) / trade.open_rate + if profit_ratio >= self.trailing_stop_start and not self.trailing_stop_enabled: + self.trailing_stop_enabled = True + trade.adjust_max_rate(current_rate) + logger.info(f"[{trade.pair}] 价格上涨超过 {self.trailing_stop_start*100:.1f}%,启动 Trailing Stop") + + if self.trailing_stop_enabled: + max_rate = trade.max_rate + trailing_stop_price = max_rate * (1 - self.trailing_stop_distance) + if current_rate < trailing_stop_price: + logger.info(f"[{trade.pair}] 价格回落至 Trailing Stop 点 {trailing_stop_price:.6f},触发卖出") + return -1 + trade.adjust_max_rate(current_rate) + + if hold_time > 30: + logger.info(f"[{trade.pair}] 持仓时间超过30分钟,强制平仓") + return -1 + return None + + 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分钟内有近期交易,跳过本次入场") + return False + self.trailing_stop_enabled = 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: + logger.info(f"[{pair}] 退出交易,原因:{exit_reason}, 利润:{trade.calc_profit_ratio(rate):.2%}") + return True