myTestFreqAI/chat/result6.md
zhangkun9038@dingtalk.com 6730a560fd log
2025-05-06 12:43:53 +08:00

11 KiB
Raw Permalink Blame History

  • Datetime: 2025-05-06 10:24:16

  • Model: _qwen3/qwen-plus-latest

  • Selected files:

    • neo-tree filesystem [1]
    • result/backtest-result-2025-05-06_02-15-16.json
    • result/backtest-result-2025-05-06_02-15-16.meta.json
    • result/backtest-result-2025-05-06_02-15-16_OKXRegressionStrategy.py
    • result/backtest-result-2025-05-06_02-15-16_config.json
    • result/output_filted.log
    • freqtrade/templates/OKXRegressionStrategy.py
    • config_examples/config_freqai.okx.json

当前策略OKXRegressionStrategy.py, 配置文件, 运行回测厚结果包含在log中, 根据其中的debug,info等信息, 结合下面列出的官方文档, 排查当前策略,配置代码是否工作正常符合预期, 如果不是,请给出分析的结论以及解决问题的 代码 块替换

自定义策略: https://www.freqtrade.io/en/stable/strategy-customization/ freqai 特征工程 : https://www.freqtrade.io/en/stable/freqai-feature-engineering/ freqai 参数表: https://www.freqtrade.io/en/stable/freqai-parameter-table/ freqai 配置: https://www.freqtrade.io/en/stable/freqai-configuration/ freaai 运行: https://www.freqtrade.io/en/stable/freqai-running/ freqai 开发手册: https://www.freqtrade.io/en/stable/freqai-developers/

根据你提供的策略代码 (OKXRegressionStrategy.py)、配置文件 (config_freqai.okx.json) 和回测日志 (output_filted.log),我进行了以下分析:


🔍 问题诊断

1. 模型加载状态

OKXRegressionStrategy.py__init__ 方法中,你添加了模型路径和存在性检查逻辑:

model_file = f"{freqai_model_path}/{model_identifier}/cb_okb_*.pkl"
if any(os.path.exists(f.replace('*', '1743465600')) for f in [model_file]):
    logger.info("✅ 模型文件存在,准备加载")
else:
    logger.warning("⚠️ 模型文件未找到,请确认是否训练完成")

但在日志中出现如下警告:

2025-05-06 02:12:04,218 - freqtrade.freqai.base_models.BaseRegressionModel - INFO - Could not find model at /freqtrade/user_data/models/test175/sub-train-OKB_1743465600/cb_okb_1743465600

➡️ 结论:模型未成功加载
虽然策略尝试从指定路径加载模型,但实际路径 /freqtrade/user_data/models/test175/sub-train-OKB_1743465600/cb_okb_1745971200.pkl 并不存在或未被正确生成。


2. 预测列缺失

populate_indicators() 中有如下逻辑:

required_pred_cols = ['pred_upper', 'pred_lower', '&-s_close', '&-roi_0_pred']
missing_pred_cols = [col for col in required_pred_cols if col not in dataframe.columns]
if missing_pred_cols:
    logger.warning(f"⚠️ 缺失预测列:{missing_pred_cols},尝试回退默认值")
    for col in missing_pred_cols:
        dataframe[col] = np.nan

日志中也出现了该警告:

2025-05-06 02:15:15,695 - OKXRegressionStrategy - WARNING - ⚠️ 缺失预测列:['pred_upper', 'pred_lower'],尝试回退默认值

➡️ 结论:模型未输出关键预测列(如 pred_upper, pred_lower
这导致交易信号无法正常触发,因为 enter_long 是基于 &-s_closepred_upper 的组合判断的。


📉 3. 回测结果表现异常

回测结果显示:

Avg Profit %: -0.26%
Tot Profit USDT: -3.811
Winrate: 47.3%
Profit Factor: 0.48

➡️ 结论:策略整体表现不佳,胜率低于 50%,且盈利因子小于 1说明策略不具备正期望收益。


🧰 根本原因分析

原因 描述
模型未保存/加载 日志显示模型路径下没有 .pkl 文件,说明 FreqAI 可能未成功保存模型
特征工程与目标列不匹配 策略定义了多个特征列,但模型可能未输出对应预测列(如 pred_upper, pred_lower
预测列缺失导致信号失效 enter_long 依赖于 &-s_close > 0.005rsi < 30,但由于预测列缺失,信号未能有效生成

🛠️ 解决方案

1. 确保模型训练并保存

config_freqai.okx.json 中确保:

"save_backtesting_prediction": true,
"purge_old_models": false,
"load_trained_model": true

同时,在命令行运行时加上 --freqaimodel-path 参数以指定模型路径:

freqtrade backtesting --strategy OKXRegressionStrategy --config config_freqai.okx.json --freqaimodel-path user_data/models/

2. 在策略中强制等待模型加载

修改 set_freqai_targets() 方法,确保模型已加载后再继续执行:

def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame:
    # 等待模型加载
    while not hasattr(self, "freqai") or self.freqai is None:
        time.sleep(1)

    # 正常设置目标列
    dataframe["&-s_close"] = (dataframe["close"].shift(-self.label_period_candles) - dataframe["close"]) / dataframe["close"]
    ...

3. 添加模型训练失败处理逻辑

fit() 方法中增加重试机制和错误提示:

def fit(self, data_dictionary: Dict, metadata: Dict, **kwargs) -> None:
    try:
        if not hasattr(self, 'model') or self.model is None:
            self.model = XGBRegressor(**self.freqai_config["model_training_parameters"])
        self.freqai.fit(data_dictionary, metadata, **kwargs)
    except Exception as e:
        logger.error(f"FreqAI 模型训练失败:{str(e)}")
        raise RuntimeError("模型训练失败,请检查数据完整性或重新训练模型")

4. 强制填充预测列(用于调试)

populate_indicators() 中加入模拟预测列逻辑,防止空列影响信号生成:

# 如果预测列缺失,用随机数模拟
if 'pred_upper' not in dataframe.columns:
    dataframe['pred_upper'] = dataframe['close'].rolling(window=10).mean() * 1.01
if 'pred_lower' not in dataframe.columns:
    dataframe['pred_lower'] = dataframe['close'].rolling(window=10).mean() * 0.99

最终建议

动作 建议
模型训练 使用 freqtrade train 显式训练模型,并确认模型保存到 user_data/models/
模型加载 修改策略逻辑,确保模型加载完成再进行指标计算
数据完整性 set_freqai_targets() 中验证所有目标列是否存在
模拟预测列 populate_indicators() 中加入模拟预测列,防止空列导致信号丢失
调整参数 尝试不同 n_estimators, learning_rate, label_period_candles 提高模型表现

📦 SEARCH/REPLACE 块

Filepath: freqtrade/templates/OKXRegressionStrategy.py Replace lines: 0-0

    def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame:
        """
        设置回归模型的目标变量为不同币对设置动态止损和ROI阈值。
        输入dataframeK线数据close, high, lowmetadata交易对信息configFreqAI配置
        输出更新后的dataframe包含目标标签
        """
        # 等待模型加载完成
        import time
        while not hasattr(self, "freqai") or self.freqai is None:
            time.sleep(1)

        # 获取配置参数
        label_period = self.freqai_config["feature_parameters"]["label_period_candles"]  # 标签预测周期如5分钟K线的N根
        pair = metadata["pair"]  # 当前交易对如DOGE/USDT

        # 计算未来价格变化率(现有逻辑)
        dataframe["&-s_close"] = (dataframe["close"].shift(-label_period) - dataframe["close"]) / dataframe["close"]

        # 计算不同时间窗口的ROI现有逻辑
        for minutes in [0, 15, 30]:
            candles = int(minutes / 5)  # 假设5分钟K线
            if candles > 0:
                dataframe[f"&-roi_{minutes}"] = (dataframe["close"].shift(-candles) - dataframe["close"]) / dataframe["close"]
            else:
                dataframe[f"&-roi_{minutes}"] = 0.0

        # 计算市场状态指标ADX14周期与label_period_candles对齐
        dataframe["adx"] = ta.ADX(dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=14)

        # 定义币对特定的ADX阈值和止损/ROI范围
        pair_thresholds = {
            "DOGE/USDT": {
                "adx_trend": 20,
                "adx_oscillation": 15,
                "stoploss_trend": -0.08,
                "stoploss_oscillation": -0.04,
                "stoploss_mid": -0.06,
                "roi_trend": 0.06,
                "roi_oscillation": 0.025,
                "roi_mid": 0.04
            },
            "BTC/USDT": {
                "adx_trend": 25,
                "adx_oscillation": 20,
                "stoploss_trend": -0.03,
                "stoploss_oscillation": -0.015,
                "stoploss_mid": -0.02,
                "roi_trend": 0.03,
                "roi_oscillation": 0.015,
                "roi_mid": 0.02
            },
            "SOL/USDT": {
                "adx_trend": 22,
                "adx_oscillation": 18,
                "stoploss_trend": -0.06,
                "stoploss_oscillation": -0.03,
                "stoploss_mid": -0.045,
                "roi_trend": 0.045,
                "roi_oscillation": 0.02,
                "roi_mid": 0.03
            },
            "XRP/USDT": {
                "adx_trend": 22,
                "adx_oscillation": 18,
                "stoploss_trend": -0.06,
                "stoploss_oscillation": -0.03,
                "stoploss_mid": -0.045,
                "roi_trend": 0.045,
                "roi_oscillation": 0.02,
                "roi_mid": 0.03
            },
            "OKB/USDT": {
                "adx_trend": 18,
                "adx_oscillation": 12,
                "stoploss_trend": -0.10,
                "stoploss_oscillation": -0.06,
                "stoploss_mid": -0.08,
                "roi_trend": 0.07,
                "roi_oscillation": 0.04,
                "roi_mid": 0.05
            }
        }


        # 动态化 &-roi_0_pred基于市场状态和币对
        dataframe["&-roi_0_pred"] = 0.0
        for index, row in dataframe.iterrows():
            thresholds = pair_thresholds.get(pair, {})
            if not thresholds:
                continue
            adx_value = row["adx"]
            if adx_value > thresholds["adx_trend"]:  # 趋势市场
                dataframe.at[index, "&-roi_0_pred"] = thresholds["roi_trend"]  # 较高ROI目标
            elif adx_value < thresholds["adx_oscillation"]:  # 震荡市场
                dataframe.at[index, "&-roi_0_pred"] = thresholds["roi_oscillation"]  # 较低ROI目标
            else:
                # 中性市场:使用中间值
                dataframe.at[index, "&-roi_0_pred"] = thresholds["roi_mid"]

        return dataframe