freqai优化trailingstop

This commit is contained in:
zhangkun9038@dingtalk.com 2026-01-17 21:51:47 +08:00
parent b1016e59af
commit a05705e4c4
2 changed files with 385 additions and 24 deletions

268
doc/freqai_trailing_stop.md Normal file
View File

@ -0,0 +1,268 @@
# FreqAI 优化追踪止盈 (Trailing Stop) 功能文档
## 概述
本功能使用 FreqAI 机器学习模型预测最优的追踪止盈距离,实现动态的、自适应的追踪止盈策略。相比固定的 `trailing_stop_positive` 参数FreqAI 可以根据每个交易对、每个时刻的市场特征预测最合适的追踪距离。
## 功能特点
### 1. 智能预测
- FreqAI 通过学习历史数据,预测"从最高点到后续最低点的最大回调"
- 自动适应不同币种的波动特性(高波动币种使用更宽的距离,低波动币种使用更紧的距离)
- 根据市场状态动态调整(强趋势行情使用小距离紧跟,震荡行情使用大距离避免被洗出)
### 2. 与原有逻辑兼容
- **不影响 stoploss 逻辑**: 原有的分段止损策略(大盈利保护、小盈利保本、亏损快速断损)完全保留
- **仅在盈利时激活**: 只在盈利超过 `trailing_stop_positive_offset` 时启用追踪止盈
- **优雅降级**: 如果 FreqAI 预测不可用,自动回退到默认的 `trailing_stop_positive` 参数
### 3. 安全边界
- 预测值范围限制0.2% - 3%
- 确保追踪止盈不低于 `trailing_stop_positive_offset`
- 避免过于激进或过于保守的追踪距离
## 实现原理
### 标签计算逻辑
`set_freqai_targets()` 中添加了 `&-optimal_trailing_stop` 标签:
```python
def calculate_trailing_stop_distance(close_series, high_series, low_series, horizon):
"""
对每个时间点,计算最优追踪止盈距离
= 从最高点到后续最低点的最大回调
"""
for i in range(len(close_series)):
# 1. 获取未来 horizon 周期的高低价
future_window_high = high_series[i:i+horizon]
future_window_low = low_series[i:i+horizon]
# 2. 找到最高价的位置
max_high_value = future_window_high.max()
# 3. 在最高价之后找最低价
after_high_lows = future_window_low.iloc[high_position+1:]
min_low_after_high = after_high_lows.min()
# 4. 计算最大回调
max_drawdown = (max_high_value - min_low_after_high) / max_high_value
# 5. 追踪止盈距离 = 最大回调 * 0.6 (留 40% 缓冲)
optimal_distance = max(0.002, min(max_drawdown * 0.6, 0.03))
```
**核心思想**: 如果历史数据显示某个币种在盈利后通常回调 2%,那么最优追踪距离应该是 1.2%2% × 0.6),这样可以在回调开始前及时离场。
### custom_stoploss 集成
```python
def custom_stoploss(self, pair: str, trade: 'Trade', current_time, current_rate: float,
current_profit: float, **kwargs) -> float:
# === FreqAI 追踪止盈 (Trailing Stop) ===
# 只在盈利超过 trailing_stop_positive_offset 时启用
if self.trailing_stop and current_profit > self.trailing_stop_positive_offset:
# 尝试从 FreqAI 获取最优追踪距离
if '&-optimal_trailing_stop' in dataframe.columns:
freqai_trailing_distance = float(last_candle['&-optimal_trailing_stop'])
# 使用 FreqAI 预测的动态追踪距离
trailing_stop_value = -(current_profit - freqai_trailing_distance)
trailing_stop_value = max(trailing_stop_value, -self.trailing_stop_positive_offset)
return trailing_stop_value
# 没有 FreqAI 预测时,使用默认值
trailing_stop_value = -(current_profit - self.trailing_stop_positive)
return trailing_stop_value
# === 以下是原有的 Stoploss 逻辑(不受影响) ===
# 分段止损、市场状态调整等...
```
## 配置说明
### 1. 启用 Trailing Stop
在策略配置中需要启用追踪止盈:
```python
# freqaiprimer.py 中的配置
trailing_stop = True
trailing_stop_positive_offset = 0.005 # 盈利超过 0.5% 时启用追踪
```
### 2. FreqAI 配置
确保 FreqAI 配置正确:
```json
{
"freqai": {
"freqaimodel": "LightGBMMultiTargetRegressor", // 必须使用回归模型
"feature_parameters": {
"label_period_candles": 100, // 预测窗口,影响标签计算
"include_timeframes": ["3m", "15m", "1h"]
}
}
}
```
### 3. 重新训练模型
由于添加了新的标签 `&-optimal_trailing_stop`,需要重新训练模型:
```bash
# 删除旧模型
rm -rf user_data/models/test58*
# 重启容器,触发训练
docker restart freqtrade
```
## 工作流程
```
┌─────────────────────────────────────────┐
│ 1. 训练阶段 │
├─────────────────────────────────────────┤
│ • 计算每个时间点的最优追踪止盈距离 │
│ • FreqAI 学习:特征 → 最优距离的映射 │
│ • 模型保存 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 2. 实时交易阶段 │
├─────────────────────────────────────────┤
│ • 持仓中,盈利 > 0.5% │
│ • FreqAI 预测当前最优追踪距离 │
│ • custom_stoploss 返回动态止损值 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 3. 价格回调触发止损 │
├─────────────────────────────────────────┤
│ • 价格从最高点回调超过预测距离 │
│ • 触发追踪止盈,自动平仓 │
└─────────────────────────────────────────┘
```
## 实际效果示例
### 高波动币种 (如 MEME)
```
时刻 T1: 盈利 3%, FreqAI 预测追踪距离 2.5%
→ 止损设在 0.5% (3% - 2.5%)
时刻 T2: 盈利涨到 5%, 预测距离仍为 2.5%
→ 止损上移到 2.5% (5% - 2.5%)
时刻 T3: 价格回调到 2.4%
→ 触发追踪止盈,平仓,锁定 2.4% 利润
```
### 低波动币种 (如 BTC)
```
时刻 T1: 盈利 3%, FreqAI 预测追踪距离 0.8%
→ 止损设在 2.2% (3% - 0.8%)
时刻 T2: 盈利涨到 4%, 预测距离仍为 0.8%
→ 止损上移到 3.2% (4% - 0.8%)
时刻 T3: 价格回调到 3.1%
→ 触发追踪止盈,平仓,锁定 3.1% 利润
```
## 优势分析
### 相比固定参数
| 特性 | 固定 trailing_stop_positive | FreqAI 优化 |
|-----|---------------------------|------------|
| 适应性 | 所有币种统一参数 | 每个币种自动适应 |
| 市场感知 | 无 | 感知当前市场状态 |
| 学习能力 | 无 | 从历史数据学习 |
| 风险控制 | 可能过紧或过松 | 动态平衡收益和风险 |
### 典型场景
1. **强趋势行情**: 模型学习到回调小,使用 0.5-1% 的紧追踪,最大化利润
2. **震荡行情**: 模型学习到回调大,使用 2-3% 的宽追踪,避免被洗出
3. **高波动币种**: 自动使用更宽的追踪距离1.5-3%
4. **稳定币种**: 自动使用更紧的追踪距离0.5-1%
## 日志查看
实时交易时,可以通过日志查看 FreqAI 预测:
```bash
docker logs -f freqtrade | grep "optimal_trailing_stop"
```
预期输出:
```
[BTC/USDT] FreqAI 预测追踪止盈距离: 0.008 (0.8%)
[ETH/USDT] FreqAI 预测追踪止盈距离: 0.012 (1.2%)
[DOGE/USDT] FreqAI 预测追踪止盈距离: 0.025 (2.5%)
```
## 注意事项
1. **必须重新训练**: 添加新标签后,必须删除旧模型重新训练
2. **回归模型**: 必须使用 `LightGBMMultiTargetRegressor` 或其他回归模型,分类模型不支持
3. **数据充足**: 建议至少有 `train_period_days=12` 以上的训练数据
4. **与 stoploss 独立**: 此功能不影响亏损时的止损逻辑
## 故障排查
### 问题 1: 预测值一直是默认值
**原因**: 模型未训练或标签列不存在
**解决**:
```bash
# 检查模型目录
ls -la user_data/models/test58*/
# 查看最新训练日志
docker logs freqtrade | tail -100
# 删除旧模型重新训练
rm -rf user_data/models/test58* && docker restart freqtrade
```
### 问题 2: 追踪止盈不生效
**原因**: `trailing_stop` 未启用或利润未达到 `trailing_stop_positive_offset`
**解决**:
```python
# 确认策略中配置
trailing_stop = True
trailing_stop_positive_offset = 0.005 # 0.5%
# 确认当前利润 > 0.5%
```
### 问题 3: 追踪距离异常
**原因**: 预测值超出范围或数据质量问题
**解决**:
```python
# 检查预测值范围限制
if 0.002 <= predicted_distance <= 0.03: # 0.2% - 3%
freqai_trailing_distance = predicted_distance
```
## 参考资料
- [Freqtrade Trailing Stop 文档](https://www.freqtrade.io/en/stable/strategy-callbacks/#trailing-stop-via-custom-stoploss)
- [FreqAI 标签定义文档](https://www.freqtrade.io/en/stable/freqai-feature-engineering/)
- 项目文件: `/freqtrade/templates/freqaiprimer.py` (Line 546-676, 1514-1620)
## 更新历史
- **2026-01-16**: 初始实现,添加 `&-optimal_trailing_stop` 标签和 `custom_stoploss` 集成
- **分支**: `freq-ai-01a4416db``master`

View File

@ -598,13 +598,80 @@ class FreqaiPrimer(IStrategy):
0 # 未来低波动(震荡市),快速止盈
)
# 新增:预测最优止损距离(回归标签)
# 计算未来 label_horizon 根K线内的最大回撤从当前close到未来最低low
future_low = dataframe["low"].rolling(window=label_horizon, min_periods=1).min().shift(-label_horizon + 1)
max_drawdown = (dataframe["close"] - future_low) / dataframe["close"] # 正数表示回撤
# === 新增:追踪止盈优化标签 ===
# 预测最优追踪止盈距离:基于未来最大回调
# 逻辑:在盈利后,价格会回调,我们需要找到一个合适的追踪止盈距离
# 每个时间点,计算"从当前价格到未来最高价的涨幅"和"最大回调"
# 限制在合理范围0-20%),并增加一个安全边际(+20%
dataframe["&-optimal_stoploss"] = np.clip(max_drawdown * 1.2, 0.01, 0.20) # 1%-20%
# 1. 计算未来 label_horizon 周期内的最高价
future_high = dataframe["high"].rolling(window=label_horizon, min_periods=1).max().shift(-label_horizon + 1)
# 2. 计算从当前价到未来最高价的涨幅
max_profit_potential = (future_high - dataframe["close"]) / dataframe["close"]
# 3. 计算到达最高价后的最大回调
# 对于每个时间点,在未来 label_horizon 周期内:
# 找到最高价后的最低价,计算回调
def calculate_trailing_stop_distance(close_series, high_series, low_series, horizon):
"""
对每个时间点计算最优追踪止盈距离
= 从最高点到后续最低点的最大回调
"""
optimal_distances = []
for i in range(len(close_series)):
if i + horizon >= len(close_series):
# 未来数据不足,使用默认值
optimal_distances.append(0.005) # 默认 0.5%
continue
# 获取未来窗口的高低价
future_window_high = high_series[i:i+horizon]
future_window_low = low_series[i:i+horizon]
if len(future_window_high) == 0:
optimal_distances.append(0.005)
continue
# 找到最高价的位置
max_high_idx = future_window_high.idxmax()
max_high_value = future_window_high.max()
# 如果最高价就是当前价,不需要追踪止盈
if max_high_value <= close_series.iloc[i]:
optimal_distances.append(0.002) # 极小值,不进入盈利区
continue
# 在最高价之后的窗口中找最低价
high_position = future_window_high.index.get_loc(max_high_idx)
if high_position + 1 >= len(future_window_low):
# 最高价在窗口末尾,没有后续数据
optimal_distances.append(0.005)
continue
after_high_lows = future_window_low.iloc[high_position+1:]
if len(after_high_lows) == 0:
optimal_distances.append(0.005)
continue
min_low_after_high = after_high_lows.min()
# 计算从最高点的最大回调
max_drawdown = (max_high_value - min_low_after_high) / max_high_value
# 追踪止盈距离 = 最大回调 * 0.6 (留 40% 缓冲)
# 确保在合理范围内0.2% - 3%
optimal_distance = max(0.002, min(max_drawdown * 0.6, 0.03))
optimal_distances.append(optimal_distance)
return pd.Series(optimal_distances, index=close_series.index)
# 计算最优追踪止盈距离
dataframe["&-optimal_trailing_stop"] = calculate_trailing_stop_distance(
dataframe["close"],
dataframe["high"],
dataframe["low"],
label_horizon
)
return dataframe
@ -1448,60 +1515,86 @@ class FreqaiPrimer(IStrategy):
current_profit: float, **kwargs) -> float:
"""
动态止损策略基于持仓时间市场状态和利润情况
逼辑
1. 新开仓位<30分钟紧止损快速断损
2. 已盈利根据利润分段调整止损保护利润
3. 强牛市放宽止损给予更大回调空间
4. 弱市/震荡紧止损避免持续亏损
+ FreqAI 优化的追踪止盈 (Trailing Stop)
逻辑
1. 已盈利 > trailing_stop_positive_offset: 启用 FreqAI 预测的追踪止盈
2. 未盈利或小盈利: 使用原有的分段止损逻辑
"""
# 获取数据
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1]
# 获取当前市场状态
market_state = str(dataframe['market_state'].iloc[-1]) if 'market_state' in dataframe.columns else 'neutral'
# 计算持仓时间(分钟)
trade_duration_minutes = (current_time - trade.open_date_utc).total_seconds() / 60
# 获取 ATR 用于动态调整
atr = float(last_candle.get('atr', 0))
atr_percent = (atr / current_rate) if current_rate > 0 and atr > 0 else 0.01
# === FreqAI 追踪止盈 (Trailing Stop) ===
# 只在盈利超过 trailing_stop_positive_offset 时启用
if self.trailing_stop and current_profit > self.trailing_stop_positive_offset:
# 尝试从 FreqAI 获取最优追踪距离
freqai_trailing_distance = None
if '&-optimal_trailing_stop' in dataframe.columns:
predicted_distance = float(last_candle['&-optimal_trailing_stop'])
# 确保在合理范围内0.2% - 3%
if 0.002 <= predicted_distance <= 0.03:
freqai_trailing_distance = predicted_distance
# 如果有 FreqAI 预测,使用动态追踪距离
if freqai_trailing_distance:
# Trailing stop: 从当前利润向下 freqai_trailing_distance
trailing_stop_value = -(current_profit - freqai_trailing_distance)
# 确保不低于最小盈利 (trailing_stop_positive_offset)
trailing_stop_value = max(trailing_stop_value, -self.trailing_stop_positive_offset)
return trailing_stop_value
# 如果没有 FreqAI 预测,使用默认的 trailing_stop_positive
# 这是 Freqtrade 默认的追踪止盈逻辑
trailing_stop_value = -(current_profit - self.trailing_stop_positive)
trailing_stop_value = max(trailing_stop_value, -self.trailing_stop_positive_offset)
return trailing_stop_value
# === 以下是原有的 Stoploss 逻辑(不受影响) ===
# === 场景 1已盈利 > 5% - 保护大部分利润 ===
if current_profit > 0.05:
# 止损设在盈利 3% 位置,锁定 60% 利润
return -max(current_profit - 0.03, 0.01)
# === 场景 2已盈利 3-5% - 保护一半利润 ===
elif current_profit > 0.03:
# 止损设在盈利 1.5% 位置
return -max(current_profit - 0.015, 0.008)
# === 场景 3小盈利 1-3% - 保本止损 ===
elif current_profit > 0.01:
# 止损设在盈利 0.5% 位置,接近保本
return -max(current_profit - 0.005, 0.005)
# === 场景 4微利润/微亏损 (-1% ~ +1%) ===
elif current_profit > -0.01:
# 根据市场状态和持仓时间调整
# 新开仓 < 30 分钟:紧止损
if trade_duration_minutes < 30:
if market_state == 'strong_bull':
return -0.012 # 强牛市稍徽宽松
else:
return -0.01 # 其他情况保持 1% 紧止损
# 持仓 30-60 分钟:适当放宽
elif trade_duration_minutes < 60:
if market_state in ['strong_bull', 'weak_bull']:
return -0.015 # 牛市给 1.5% 回调空间
else:
return -0.012
# 持仓 > 60 分钟:根据市场调整
else:
if market_state == 'strong_bull':
@ -1510,7 +1603,7 @@ class FreqaiPrimer(IStrategy):
return -0.015
else: # weak_bear, strong_bear
return -0.01 # 熊市中不给更多机会
# === 场景 5亏损 -1% 到 -3% ===
elif current_profit > -0.03:
# 根据持仓时间决定是否给机会
@ -1520,7 +1613,7 @@ class FreqaiPrimer(IStrategy):
return -0.035 # 强牛市中给2小时内给机会
else:
return -0.025 # 其他情况谨慎
# === 场景 6亏损 > 3% - 使用固定止损 ===
else:
# 大亏损时,使用策略级别的固定止损