Files
wiki/quant/stoploss_leverage_adjustment.md

8.7 KiB
Raw Permalink Blame History

止盈止损策略调整文档

一、背景

策略 MultiAssetChannelBreakoutV5 运行在 10x 杠杆的期货模式下(OKX 隔离保证金), timeframe 15m,交易 BTC/USDT、ETH/USDT、TRX/USDT。

原策略的止损阈值是按 1x 杠杆(无杠杆) 设计的,但实际运行在 10x 杠杆下。 Freqtrade 的 current_profitstoploss 都是保证金维度的值,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 隔离保证金维持保证金率 约 12%,价格跌 89% 接近强平线。止损设在 -80% 意味着:

  • 价格跌 8% 时触发止损,离强平还有约 1% 的缓冲
  • 最大限度给趋势空间,不会被任何正常波动扫出去
  • 风险:如果止损滑点或急速下跌,可能被交易所强平而止损单未成交

关于兜底 return 10.0

return 10.0 的计算:stop_price = current × (1 - 10/10) = $0。止损设在 $0, 比任何现有止损都低,被 Freqtrade 的"止损只升不降"规则挡住,硬止损保留。 这与 1x 下 return 1.01 - 1/1 = 0,也是 $0)行为完全等价。

四、改后代码(trade.leverage 动态联动版)

所有阈值通过 trade.leverage 动态计算,当前 10x 时行为与写死值完全一致, 如果未来杠杆变化会自动适配。

# 硬止损(类属性,拿不到 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

# 原代码(有 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_tradesstake_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.80leverage() 固定返回 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