137S 策略 · 平仓尾部风险改进说明

文件:scripts/run_pyramid_live.py  ·  改动日期:2026-04-23  ·  版本:CLOSE_ALL v2

一、背景与风险识别

当策略权益规模达到 $20M(2000万) 量级时,BTC 持仓可能超过 100 BTC。 在此规模下,原始 CLOSE_ALL 实现存在三项系统性缺陷,可能导致止损完全失效。

1.1 原始代码(改前)

# run_pyramid_live.py — 原 CLOSE_ALL 分支(已废弃)
elif action == PyramidAction.CLOSE_ALL:
    # 问题①:滑点探盘固定用 1.0 BTC,无论实际持仓多大
    limit_px, best_px, actual_slip = _get_order_price(
        info, "BTC", is_buy=False, size=1.0,   ← 实际可能 100 BTC
        slippage_buffer=slippage, max_slippage=max_slippage,
    )
    # 问题②:sz=0.0 表示单笔全平,IOC 未成交则全部留仓
    exchange.order("BTC", is_buy=False, sz=0.0, limit_px=limit_px,
                   order_type={"limit": {"tif": "Ioc"}}, reduce_only=True)

# 问题③:插针时 SlippageTooHighError → 直接返回 ok=False,仓位原地不动
except SlippageTooHighError as exc:
    return ExecResult(ok=False, detail=f"slippage_too_high_close: {exc}")

1.2 三项核心缺陷

#缺陷在 $20M 下的实际后果
滑点按 1 BTC 评估 100 BTC 的真实市场冲击被严重低估;实际挂单价远离市场,IOC 必定大部分未成交
单笔 sz=0.0 全平 一次性挂出 100 BTC 卖单,流动性不足时绝大部分取消,止损形同虚设
插针直接放弃 插针(价格急跌)正是最需要平仓的时刻,代码却返回失败、仓位悬空,损失持续扩大
极端场景测算:权益 $20M、10x 杠杆、持仓 ~100 BTC。BTC 单日下跌 15% 时, 爆仓线约在 −10%(即 $54K→$49K)。插针可能在单根 4H K线内触穿止损位,交易所强平先于策略响应。 此时若 CLOSE_ALL 再失败,损失可能超出止损预期数倍。

二、改进方案:自适应拆单循环 NEW v2

2.1 设计原则

原则实现方式
按实际仓位拆单每轮先调用 info.user_state() 查询交易所真实持仓,再计算 clip 大小
滑点评估匹配实际大小_get_order_price(size=clip),用真实 clip BTC 数探盘
插针不等待、直接市价SlippageTooHighError → 立即 market_close(sz=clip),无任何等待
多层兜底连续 3 次市价兜底 → 强制全清;超 10 轮 → 最终 market_close()

2.2 关键参数

常量说明
CLIP_FRAC25%每轮平掉当前剩余仓位的 1/4
CLIP_MAX_BTC10 BTC单笔上限,控制单次市场冲击
CLIP_MIN_BTC0.01 BTC低于此视为已清仓,退出循环
MAX_ROUNDS10最大尝试轮数(4 轮理论上可清 68%,10 轮可清 94%)
INTER_SLEEP2 秒正常 clip 间隔;插针路径跳过等待
MKT_THRESH3 次连续 N 次市价兜底后触发强制全清
WIDE_SLIPmax(slippage×3, 1.5%)市价兜底宽容滑点,确保成交优先于价格

2.3 新版逻辑流程

# run_pyramid_live.py — CLOSE_ALL v2(自适应拆单)
for round_i in range(10):                           # MAX_ROUNDS = 10

    # Step 1:查询交易所实际持仓
    szi = _ca_query_pos()                              # info.user_state() → assetPositions[BTC].szi
    remaining = abs(szi)
    if remaining < 0.01: break                         # 已清仓,退出

    # Step 2:按实际仓位计算 clip
    clip = min(remaining * 0.25, 10.0)                 # 25%,最多 10 BTC
    is_buy = szi < 0                                   # 空头→买入平仓,多头→卖出平仓

    # Step 3:尝试 IOC 限价单(滑点按 clip 实际大小评估)
    try:
        limit_px, _, _ = _get_order_price(size=clip, ...)   ← 用真实 clip,非 1 BTC
        exchange.order("BTC", sz=clip, tif="Ioc", reduce_only=True)

    except SlippageTooHighError:                        # ← 插针保护
        # 不等待 30 秒!立即降级为市价单
        exchange.market_close("BTC", sz=clip, slippage=0.015)
        consecutive_mkt += 1

    # Step 4:连续 3 次市价兜底 → 强制全清
    if consecutive_mkt >= 3:
        exchange.market_close("BTC", slippage=0.015)   # 全部剩余
        _notify("插针强平 | N次市价兜底")
        break

    time.sleep(2)

else:                                                  # 10 轮耗尽
    exchange.market_close("BTC", slippage=0.015)       # 最终兜底
    _notify("平仓超限 | 10轮未清仓")
    return ExecResult(ok=False, ...)

三、改前 vs 改后对比

场景改前行为改后行为
$20M 大仓位正常平仓 sz=0.0 单笔挂出 100 BTC,IOC 部分成交,剩余留仓 分 4~10 轮,每轮 ≤10 BTC,每轮前查实际仓位
滑点评估 固定用 1 BTC 探盘,100 BTC 实际冲击被忽略 每 clip 用真实大小探盘,评估准确
插针(价格急跌) SlippageTooHighError → ok=False,仓位原地不动 立即市价单出局,零等待
IOC 未成交 无补救,止损失效 下一轮重查仓位、继续尝试
连续失败 没有重试机制 连续 3 次降级 → 强制市价清仓 + 邮件告警
程序卡死 / 极端行情 无保底 10 轮上限触发兜底 market_close() + 告警
普通下单的 30s 等待 CLOSE_ALL 中也有 30s 等待 30s 等待仅保留在普通限价路径;CLOSE_ALL 全程无等待

四、告警通知

触发条件邮件主题含义
连续 3 次市价兜底 插针强平 | N次市价兜底 | reason 插针期间多次滑点超限,已强制市价全清剩余仓位
10 轮未能清仓 平仓超限 | 10轮未清仓 | reason 极端行情下循环耗尽,已触发最终兜底市价单,需人工核查

五、日志示例

# 正常情况(4 轮平掉 $20M 仓位)
close_all reason=hard_stop_limit
  close_all[0] szi=102.43500 clip=10.00000 is_buy=False
    best=84320.0 lim=84235.7 slip=0.0010%
  close_all[1] szi=92.43500 clip=10.00000 is_buy=False
    ...
  close_all[4] cleared (remaining=0.00000)

# 插针情况(自动降级市价)
  close_all[2] szi=72.43500 clip=10.00000 is_buy=False
    slip_too_high (slippage 0.8200% > max 0.5000%), market fallback immediately
    → market_close(sz=10.0, slippage=0.015) 执行

# 连续 3 次插针(强制全清)
  close_all: 3 consec mkt orders, force market_close all
  → 邮件告警: 插针强平 | 3次市价兜底

六、遗留风险(未覆盖)

爆仓间隙风险:若 BTC 在单根 4H K线内下跌 >10%(触穿 10x 杠杆爆仓线), 交易所强平早于策略 60s 轮询触发。此为平台级风险,非程序层面可完全防御。 建议在权益超过 $5M 后将 base_leverage 降至 7x 或启用 adaptive_leverage
API 断线:info.user_state() 持续超时,_ca_query_pos() 返回 0 → 循环提前退出。可在监控层增加独立的持仓健康检查(heartbeat)。

返回报表中心