194 lines
8.7 KiB
Markdown
194 lines
8.7 KiB
Markdown
# 止盈止损策略调整文档
|
||||
|
|
|
|||
|
|
## 一、背景
|
|||
|
|
|
|||
|
|
策略 `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-22:trade.leverage 动态联动 + breakeven bug 修复
|
|||
|
|
- 所有写死阈值改为通过 `trade.leverage` 动态计算(6 处改动)
|
|||
|
|
- 修复 breakeven 返回值缺少 ×leverage 的 bug(止损线被杠杆压缩 10 倍)
|
|||
|
|
- `stoploss` 类属性保持 -0.80(`leverage()` 固定返回 10.0,类属性值正确)
|
|||
|
|
- 部署方式:scp + systemctl restart freqtrade
|
|||
|
|
- 服务状态:active (running)
|
|||
|
|
|
|||
|
|
### 2026-08-22:config.json stoploss 覆盖修复
|
|||
|
|
- **问题**:前两次部署只改了策略文件,未改 `config.json`。Freqtrade 中 config 的
|
|||
|
|
`stoploss` 会覆盖策略类的 `stoploss` 属性,导致线上实际硬止损仍是旧的 -0.10
|
|||
|
|
(保证金 -10% = 价格 -1%),修改后的 -0.80 从未生效。
|
|||
|
|
- **后果**:trade id=43(ETH/USDT,8/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`
|