11 KiB
-
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_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 中确保:
"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阈值。
输入: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