From 2e4768f1c664a14b3907462dfead77e79ac6bbc0 Mon Sep 17 00:00:00 2001 From: "zhangkun9038@dingtalk.com" Date: Tue, 23 Dec 2025 17:57:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=81=E4=BB=93=E6=97=B6=E9=95=BFAI=20?= =?UTF-8?q?=E6=96=B0=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- freqtrade/templates/freqaiprimer.py | 43 ++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index 4ac95d66..d5df447d 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -329,7 +329,7 @@ class FreqaiPrimer(IStrategy): return dataframe def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame: - """定义 FreqAI 训练标签:简单二分类版本。""" + """定义 FreqAI 训练标签:简单二分类版本 + 持仓时长预测。""" label_horizon = 6 # 与 config 中 label_period_candles 对齐 # 动态计算上涨/下跌阈值 @@ -355,6 +355,20 @@ class FreqaiPrimer(IStrategy): 0, ) + # 新增:持仓时长预测标签 + # 计算到达最高点需要多少根K线(未来20根内的最高价位置) + max_lookahead = 20 # 预测未来20根K线(60分钟) + + # 找到未来20根K线内的最高价 + future_high_20 = dataframe["high"].rolling(window=max_lookahead, min_periods=1).max().shift(-max_lookahead + 1) + + # 计算到达最高点的K线数(简化版本:如果涨幅 > 1.5%,则标记为1=长期持有,否则0=短期) + dataframe["&s-optimal_hold_duration"] = np.where( + (future_high_20 - dataframe["close"]) / dataframe["close"] > 0.015, # 未来能涨 > 1.5% + 1, # 标记为长期持有(预计需要更长时间) + 0 # 短期快速出场 + ) + return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -865,6 +879,13 @@ class FreqaiPrimer(IStrategy): except Exception: trade_age_minutes = 0.0 + # 新增:读取 AI 预测的持仓时长信号 + hold_duration_signal = None + if '&s-optimal_hold_duration' in df.columns: + hold_duration_signal = float(last_row['&s-optimal_hold_duration']) + elif '&-optimal_hold_duration' in df.columns: + hold_duration_signal = float(last_row['&-optimal_hold_duration']) + # 基于持仓时长的阈值衰减:持仓越久,阈值越低,越容易出场 age_factor = min(trade_age_minutes / (24 * 60.0), 1.0) # 0~1,对应 0~24 小时+ dynamic_threshold = base_threshold * (1.0 - 0.3 * age_factor) @@ -873,6 +894,24 @@ class FreqaiPrimer(IStrategy): if current_profit <= 0.02: dynamic_threshold *= 0.8 + # 新逻辑:如果 AI 预测需要长期持有(hold_duration_signal=1),且持仓时间还很短,则提高阈值 + if hold_duration_signal is not None and hold_duration_signal > 0.6: # AI 预测需要长期持有 + # 如果持仓时间 < 30分钟,阈值提高 50%,更难出场 + if trade_age_minutes < 30: + dynamic_threshold *= 1.5 + logger.info( + f"[持仓时长 AI] [{pair}] AI 预测需要长期持有,提高阈值至 {dynamic_threshold:.2f}" + ) + # 如果持仓时间 30-60分钟,阈值提高 20% + elif trade_age_minutes < 60: + dynamic_threshold *= 1.2 + elif hold_duration_signal is not None and hold_duration_signal < 0.4: # AI 预测短期快速出场 + # 阈值降低 20%,更容易出场 + dynamic_threshold *= 0.8 + logger.info( + f"[持仓时长 AI] [{pair}] AI 预测短期出场,降低阈值至 {dynamic_threshold:.2f}" + ) + # 设定下限,避免阈值过低 dynamic_threshold = max(0.05, dynamic_threshold) @@ -880,12 +919,14 @@ class FreqaiPrimer(IStrategy): logger.info( f"[{pair}] ML 审核官拒绝出场: exit_signal 概率 {exit_prob:.2f} < 动态阈值 {dynamic_threshold:.2f}" f" | 原应出场原因: {exit_reason} | 持仓: {trade_age_minutes:.1f}min, 利润: {current_profit:.4f}" + f" | 持仓时长AI: {hold_duration_signal if hold_duration_signal is not None else 'N/A'}" ) allow_exit = False else: logger.info( f"[{pair}] ML 审核官允许出场: exit_signal 概率 {exit_prob:.2f} >= 动态阈值 {dynamic_threshold:.2f}" f" | 出场原因: {exit_reason} | 持仓: {trade_age_minutes:.1f}min, 利润: {current_profit:.4f}" + f" | 持仓时长AI: {hold_duration_signal if hold_duration_signal is not None else 'N/A'}" ) except Exception as e: logger.warning(f"[{pair}] ML 审核官出场检查失败,允许出场: {e}")