- 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__` 方法中,你添加了模型路径和存在性检查逻辑: ```python 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()` 中有如下逻辑: ```python 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_close` 和 `pred_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.005` 或 `rsi < 30`,但由于预测列缺失,信号未能有效生成 | --- ## 🛠️ **解决方案** ### ✅ 1. 确保模型训练并保存 在 `config_freqai.okx.json` 中确保: ```json "save_backtesting_prediction": true, "purge_old_models": false, "load_trained_model": true ``` 同时,在命令行运行时加上 `--freqaimodel-path` 参数以指定模型路径: ```bash freqtrade backtesting --strategy OKXRegressionStrategy --config config_freqai.okx.json --freqaimodel-path user_data/models/ ``` --- ### ✅ 2. 在策略中强制等待模型加载 修改 `set_freqai_targets()` 方法,确保模型已加载后再继续执行: ```python 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()` 方法中增加重试机制和错误提示: ```python 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()` 中加入模拟预测列逻辑,防止空列影响信号生成: ```python # 如果预测列缺失,用随机数模拟 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 ```python def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame: """ 设置回归模型的目标变量,为不同币对设置动态止损和ROI阈值。 输入:dataframe(K线数据:close, high, low),metadata(交易对信息),config(FreqAI配置) 输出:更新后的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 # 计算市场状态指标:ADX(14周期,与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