Files

194 lines
8.7 KiB
Markdown
Raw Permalink Normal View History

# 止盈止损策略调整文档
## 一、背景
策略 `MultiAssetChannelBreakoutV5` 运行在 10x 杠杆的期货模式下(OKX 隔离保证金),
timeframe 15m,交易 BTC/USDT、ETH/USDT、TRX/USDT。
原策略的止损阈值是按 **1x 杠杆(无杠杆)** 设计的,但实际运行在 10x 杠杆下。
Freqtrade 的 `current_profit``stoploss` 都是**保证金维度**的值,10x 杠杆会把价格
变动放大 10 倍,导致所有止损/保本阈值被压缩到原来的 1/10。
## 二、问题:止损被杠杆压缩到极窄
### Freqtrade 的杠杆计算公式
```
current_profit = (价格变动比例) × leverage ← 保证金维度
stop_loss_price = current_price × (1 - stoploss / leverage) ← 实际止损价
```
### 改前:阈值被压缩 10 倍
| 参数 | 值(保证金维度) | 实际价格含义 | 问题 |
|---|---|---|---|
| `stoploss` | -0.08 | 价格跌 **0.8%** 就止损 | 一根 15m K 线正常波动 0.5~1.5%,一根 K 线就被扫出去 |
| 保本触发 `>0.05` | 保证金+5% | 价格涨 **0.5%** 就保本 | 刚涨一点就锁死,任何回调都被止 |
| 回撤容忍 `return 0.10` | 保证金回撤10% | 容忍价格回撤 **1%** | 正常波动就触发 |
| breakeven `open×1.005` | 入场+0.5% | 保本线在入场+**0.5%** | 几乎等于入场价 |
### 实际后果
```
入场 → 价格涨 0.5% → 保本启动 → 一根 K 线回调 0.3% → 被止损出局 → 错过后面的趋势
```
策略设计是通道突破吃趋势,但止损参数相当于高频炒菜级别,两者严重不匹配。
## 三、改后:阈值 ×10 还原到价格维度
### 逐项对照
| 参数 | 改前 | 改后 | 改前价格含义 | 改后价格含义 |
|---|---|---|---|---|
| `stoploss` | -0.08 | **-0.80** | 价格跌 0.8% 止损 | 价格跌 **8%** 止损 |
| 保本触发 | `>0.05` | **`>0.50`** | 价格涨 0.5% 保本 | 价格涨 **5%** 保本 |
| 第二档 | `>0.15` | **`>1.50`** | 价格涨 1.5% | 价格涨 **15%** |
| 第三档 | `>0.30` | **`>3.00`** | 价格涨 3% | 价格涨 **30%** |
| 回撤容忍 | `return 0.10` | **`return 1.0`** | 容忍回撤 1% | 容忍回撤 **10%** |
| breakeven | `open×1.005` | **`open×1.05`** | 保本在入场+0.5% | 保本在入场+**5%** |
| 兜底 | `return 1.0` | **`return 10.0`** | 止损=$0→被挡→用硬止损 | 同理($0→被挡→用硬止损)|
### 关于 `stoploss` = -0.80
完整 ×10 是 -0.80(保证金-80% = 价格-8%)。10x 杠杆下 OKX 隔离保证金维持保证金率
约 1~2%,价格跌 8~9% 接近强平线。止损设在 -80% 意味着:
- 价格跌 8% 时触发止损,离强平还有约 1% 的缓冲
- 最大限度给趋势空间,不会被任何正常波动扫出去
- **风险**:如果止损滑点或急速下跌,可能被交易所强平而止损单未成交
### 关于兜底 `return 10.0`
`return 10.0` 的计算:`stop_price = current × (1 - 10/10) = $0`。止损设在 $0
比任何现有止损都低,被 Freqtrade 的"止损只升不降"规则挡住,硬止损保留。
这与 1x 下 `return 1.0``1 - 1/1 = 0`,也是 $0)行为完全等价。
## 四、改后代码(trade.leverage 动态联动版)
所有阈值通过 `trade.leverage` 动态计算,当前 10x 时行为与写死值完全一致,
如果未来杠杆变化会自动适配。
```python
# 硬止损(类属性,拿不到 trade.leverage,因 leverage() 固定返回 10.0 故 -0.80 正确)
stoploss = -0.80
def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, **kwargs):
L = trade.leverage
# 盈利 > 价格+30%: 允许 10% 价格回撤
if current_profit > 0.30 * L:
return 0.10 * L
# 盈利 > 价格+15%: 允许 10% 价格回撤
if current_profit > 0.15 * L:
return 0.10 * L
# 盈利 > 价格+5%: 止损上移到保本+5%
if current_profit > 0.05 * L:
breakeven_target = trade.open_rate * 1.05
return (current_rate - breakeven_target) / current_rate * L
# 兜底:$0→被挡→用硬止损(-80% 保证金 = -8% 价格)
return L
```
### 逐项对照(L=10 时与写死值一致)
| 表达式 | L=10 | 原写死值 | 价格含义 |
|---|---|---|---|
| `0.30 × L` | 3.00 | 3.00 | 价格+30% |
| `0.15 × L` | 1.50 | 1.50 | 价格+15% |
| `0.05 × L` | 0.50 | 0.50 | 价格+5% |
| `0.10 × L` | 1.0 | 1.0 | 允许价格回撤10% |
| `return L` | 10.0 | 10.0 | $0→被挡→用硬止损 |
## 五、Breakeven 返回值 Bug 修复
### Bug 描述
freqtrade 内部对 `custom_stoploss` 返回值的处理公式(`trade_model.py:837`):
```
stop_price = current_rate × (1 - 返回值 / leverage)
```
返回值是**保证金维度**的,freqtrade 内部会再除以 leverage。
原代码的 breakeven 返回值缺少 ×10
```python
# 原代码(有 bug
return (current_rate - breakeven_target) / current_rate
# 返回的是价格维度比例,但 freqtrade 当作保证金维度再除以 10
```
### Bug 影响
以入场 $100,000、当前价 $108,000 为例:
| | 原代码(bug) | 修复后 |
|---|---|---|
| 返回值 | 0.0278 | 0.278 |
| freqtrade 计算 | `108000 × (1 - 0.0278/10)` | `108000 × (1 - 0.278/10)` |
| 实际止损价 | **$107,700**(比预期高 $2,700 | **$105,000** ✓ |
| 效果 | 止损线被杠杆压缩 10 倍,过于贴近当前价 | 止损线正确钉在保本+5% |
修复方式:返回值乘以 `trade.leverage``× L`)。
## 六、改后行为对比(以 BTC $100,000 入场为例)
| 场景 | 改前 | 改后 |
|---|---|---|
| 入场后跌 0.8% | 保证金-8%,**触发硬止损,亏 $800 出局** | 保证金-8%,离 -80% 远,**继续持有** |
| 涨 0.5% 后回调 0.3% | 保本启动,**被止损出局,白干** | 还没到 5% 保本线,**继续持有** |
| 涨 5% 后回调 1% | 早被止损了 | 保本启动(止损在+5%),回调 1% **还在持有** |
| 涨 15% 后回调 10% | 不可能走到这 | 触发第二档止损,**仍盈利 +5% 出局** |
| 涨 30% 后回调 10% | 不可能走到这 | 触发第三档止损,**仍盈利 +20% 出局** |
| 跌 8% | 不可能走到这 | **触发硬止损,亏保证金 80% 出局** |
## 七、风险提示
1. **单次止损亏损极大**:从保证金-8% 变为-80%,每次止损亏掉保证金的 80%,
需要严格控制仓位和并发持仓数
2. **接近强平线**:价格-8% 止损与 OKX 强平线(约-8~9%)非常接近,
急速下跌时止损单可能来不及成交就被强平
3. **建议调低仓位**`max_open_trades``stake_amount` 适当降低,确保单次止损
不至于严重影响总资金
4. **需观察实盘表现**:参数调整后建议密切观察 1~2 周的交易记录,看止损频率是否下降、
盈亏比是否改善
5. **未改 ROI 止盈**`minimal_roi = {"0": 100}` 保持不变(等于关闭 ROI 止盈),
仍然依赖信号出场(跌破 141 周期低点),趋势策略让利润跑是合理的
## 八、部署记录
### 2026-08-21:止损阈值 ×10
- 将所有保证金维度的止损阈值乘以 10,抵消 10x 杠杆压缩
- `stoploss`: -0.08 → -0.30 → -0.80
- 部署方式:scp + systemctl restart freqtrade
- 服务状态:active (running)
### 2026-08-22trade.leverage 动态联动 + breakeven bug 修复
- 所有写死阈值改为通过 `trade.leverage` 动态计算(6 处改动)
- 修复 breakeven 返回值缺少 ×leverage 的 bug(止损线被杠杆压缩 10 倍)
- `stoploss` 类属性保持 -0.80`leverage()` 固定返回 10.0,类属性值正确)
- 部署方式:scp + systemctl restart freqtrade
- 服务状态:active (running)
### 2026-08-22config.json stoploss 覆盖修复
- **问题**:前两次部署只改了策略文件,未改 `config.json`。Freqtrade 中 config 的
`stoploss` 会覆盖策略类的 `stoploss` 属性,导致线上实际硬止损仍是旧的 -0.10
(保证金 -10% = 价格 -1%),修改后的 -0.80 从未生效。
- **后果**trade id=43ETH/USDT8/21 22:00 开仓)最高仅 +1.28% 价格,
未达保本触发线,custom_stoploss 返回兜底值被挡,价格跌 1% 即被旧硬止损扫出,
亏保证金 12.45%(-$13.53)。正是本文第二节描述的"改前"失败模式。
- **修复**`config.json``"stoploss": -0.1``"stoploss": -0.80`
- 部署方式:ssh sed + systemctl restart freqtrade
- 服务状态:active (running),启动日志 RPC 确认 `Stoploss: -0.8`
- **教训**:改策略的 `stoploss` 类属性时,必须同步检查 config.json 是否有 `stoploss`
字段覆盖;两者不一致时 config 优先。后续部署清单应包含此项校验。
- 本地文件:`user_data/strategies/multi_asset_channel_breakout_v5.py`
- 远程路径:`vultr:/opt/freqtrade/user_data/strategies/multi_asset_channel_breakout_v5.py`
- 远程配置:`vultr:/opt/freqtrade/user_data/config.json`