279 lines
11 KiB
Markdown
279 lines
11 KiB
Markdown
|
||
|
||
- 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
|