Files
Hermes-Skills/okx-auto-position/references/v4.5.77-2026-09-09-close-whitelist-classify-update-only.md

8.3 KiB

v4.5.77 — close 信号过白名单闸 + classify_signal 新分类 + 配置驱动白名单

实战日期: 2026-09-09 触发 trader: "熬鹰FOMO合约实盘" / "熬鹰交易信号" (新出现, ≠白名单"熬鹰资本") 关键 user 原话: "那是新trader, 不在白名单, 为什么要平?"


1. 背景

之前白名单闸只拦 open/add/reduce/adjust_leverage, close 信号走"平仓兜底"逻辑 —— 即便 trader 不匹配,也尝试平仓(因为"平仓是兜底, 即使来源不明也应执行"——这是 v4.5.61 的旧假设)。

但用户实际原意是"只跟熬鹰资本"。其他 trader 的 close 信号不能触发平仓。

2. 实战复盘

触发链路

  1. 2026-09-09 12:34 UTC+8: 新 trader "熬鹰交易信号" 发 ZEC 平仓信号(亏损-$8,348,5x short 334.88张)
  2. process_signal.py 解析: trader="熬鹰交易信号", signal_type="close"
  3. 白名单闸 (line 802 原版): 只拦 open/add/reduce/adjust_leverage, close 走独立通道
  4. close 路径 (line 848): 调 close_position_raw("ZEC", "short")
  5. proxy 抽风, _okx_raw_request 返回 None → close_result=None
  6. TypeError: close_result['action'] → 整个 process_signal 崩了(用户看到 "❌ 平仓失败: _okx_raw_request 返回 None (proxy 抽风?) | ZEC")

虽然本次因 proxy 抽风没真执行,但逻辑漏洞清楚: 下次 proxy 恢复,这条 close 就会真平你 ZEC short 42 张(盈利约+$0.21)。

类似问题:"熬鹰FOMO合约实盘" 也是非白名单 trader

在"熬鹰交易信号"之前, 2026-09-09 早些时候"熬鹰FOMO合约实盘" 也发过 ZEC close 信号,同样是 close 兜底绕过白名单,同样因 proxy 抽风没真平。

3. 修复

3.1 白名单闸扩展 (process_signal.py)

# 旧 (line 802-803 原版)
if _sig_type_for_gate in ('open', 'add', 'reduce', 'adjust_leverage') and trader not in _TRADER_WHITELIST:
    return f"⏭️ 跳过: trader [{trader}] 不在白名单 | {symbol} {side}"

# 新 (v4.5.77)
if _sig_type_for_gate in ('open', 'add', 'reduce', 'adjust_leverage', 'close') and trader not in _TRADER_WHITELIST:
    return f"⏭️ 跳过: trader [{trader}] 不在白名单 | {symbol} {side}"

3.2 classify_signal 新分类 (process_signal.py:247-269)

def classify_signal(text):
    if '平仓' in text or '止盈' in text or '止损' in text:
        return 'close'
    if '减仓' in text:
        return 'reduce'
    if '加仓' in text or '追仓' in text:
        return 'add'
    if '修改了杠杆' in text or '调整了杠杆' in text or '调杠杆' in text:
        return 'adjust_leverage'    # ← 新增
    if '仓位实时更新' in text or '实时更新' in text:
        return 'update_only'        # ← 新增
    return 'open'

3.3 adjust_leverage_raw 函数 (process_signal.py:523)

def adjust_leverage_raw(symbol_usdt, new_leverage):
    """用 raw REST 直接设置杠杆 (跨币种通用)"""
    inst_id = f"{symbol_usdt}-USDT-SWAP"
    if symbol_usdt == '1000PEPE':
        inst_id = 'PEPE-USDT-SWAP'
    body = {'instId': inst_id, 'lever': str(new_leverage), 'mgnMode': 'cross'}
    resp = _okx_raw_request('POST', '/api/v5/account/set-leverage', body=body)  # 注意 body= 关键字
    if resp.get('code') == '0':
        return {'ok': True, 'raw': resp}
    return {'ok': False, 'error': resp.get('msg', 'unknown'), 'raw': resp}

3.4 adjust_leverage 路径 (process_signal.py:883-899)

if signal_type == 'adjust_leverage':
    dedup_conn = init_dedup_db()
    if is_duplicate(dedup_conn, text, symbol, trader):
        dedup_conn.close()
        return "⏭️ 重复信号,跳过"
    record_signal(dedup_conn, text, symbol, trader)
    dedup_conn.close()
    symbol_usdt = fields['symbol']
    new_lev = int(fields.get('leverage') or '10')
    lev_result = adjust_leverage_raw(symbol_usdt, new_lev)
    if lev_result and lev_result.get('ok'):
        msg = f"⚙️ **杠杆已调整** | {symbol_usdt} → {new_lev}x"
        push_to_qq(msg)
        return f"✅ 调杠杆: {symbol_usdt} → {new_lev}x"
    else:
        err = lev_result.get('error', '未知') if lev_result else 'None 返回'
        return f"❌ 调杠杆失败: {err} | {symbol_usdt}"

3.5 update_only skip 闸 (process_signal.py:805-808)

# 2026-09-01: 实时仓位更新是状态推送,跳过不执行
if _sig_type_for_gate == 'update_only':
    return f"⏭️ 跳过: 实时仓位更新信号(非真实开/加/减/平仓) | {symbol} {side}"

3.6 _okx_raw_request null guard (process_signal.py:841-845)

symbol_usdt = fields['symbol']
signal_side_en = fields.get('side_en', 'long')
close_result = close_position_raw(symbol_usdt, signal_side_en)
# 2026-09-09: proxy 抽风可能让 _okx_raw_request 返回 None, 加 null guard
if not close_result:
    return f"❌ 平仓失败: _okx_raw_request 返回 None (proxy 抽风?) | {symbol_usdt}"
action = close_result['action']

4. whitelist.json 配置

scripts/whitelist.json:

{
  "_comment": "跟单白名单配置 - 不要在代码里硬编码,改这里。trader=跟单交易员名字,设置在 trader_whitelist 数组。",
  "trader_whitelist": ["熬鹰资本"]
}

process_signal.py 加载逻辑:

_WL_PATH = Path(__file__).parent / 'whitelist.json'
_TRADER_WHITELIST = {'熬鹰资本'}  # fallback 默认值(防止配置文件损坏)
try:
    import json as _json
    _wl = json.loads(_WL_PATH.read_text())
    if isinstance(_wl, dict) and isinstance(_wl.get('trader_whitelist'), list):
        _TRADER_WHITELIST = set(_wl['trader_whitelist'])
except Exception:
    pass  # 配置文件失败时用 fallback

铁律:

  • ✅ trader / 币种 / 阈值 / 比例 / flag — 都放配置文件, 绝不硬编码
  • ❌ 禁止 TRADER_WHITELIST = {'熬鹰资本'} 直接写在代码里
  • 改白名单 → 改 whitelist.json, 不需要重写代码或重启

5. 本次 session 实战过程中触发的代码修改 (timeline)

时间 修改 文件 行号
2026-09-09 04:38 调杠杆信号处理 (adjust_leverage_raw) process_signal.py 523, 883
2026-09-09 04:42 白名单闸 (含配置加载) process_signal.py 792-802
2026-09-09 04:50 classify_signal 新分类 process_signal.py 263-269
2026-09-09 05:10 whitelist.json 创建 scripts/whitelist.json new
2026-09-09 05:20 去掉"性价比低"判断 + 推送去除"性价比"字样 process_signal.py 938-940, 730, 1071
2026-09-09 12:40 close 闸白名单扩展 + update_only process_signal.py 802, 805-808
2026-09-09 12:42 _okx_raw_request null guard process_signal.py 841-845
2026-09-09 12:45 _okx_raw_request body= 参数名修复 process_signal.py 530

6. 测试验证

6.1 close 信号白名单闸测试

sqlite3 ~/.hermes/trading/signal_dedup.db "DELETE FROM processed WHERE 1=1;"
python3 process_signal.py << 'EOF'
【熬鹰交易信号】
🚨 已平仓提醒
【币种】: ZEC
EOF

期望输出: ⏭️ 跳过: trader [熬鹰交易信号] 不在白名单 | ZEC short

6.2 调杠杆信号测试

python3 process_signal.py << 'EOF'
【熬鹰资本】
🔧 注意,大佬修改了杠杆 3→5
【币种】: SKHYNIXUSDT|永续|5x
...
EOF

期望输出: ✅ 调杠杆: SKHYNIX → 5x

6.3 update_only 测试

python3 process_signal.py << 'EOF'
【熬鹰资本】
📊 仓位实时更新
【币种】: SKHYNIXUSDT|永续|3x
...
EOF

期望输出: ⏭️ 跳过: 实时仓位更新信号(非真实开/加/减/平仓) | SKHYNIX long

7. 反模式

  • ❌ close 信号走"平仓兜底"豁免白名单 → trader 不匹配也平仓,误平风险
  • ❌ classify_signal 默认 'open' → "📊 仓位实时更新" 被当 open execute
  • ❌ 调杠杆信号走 advisor 加仓路径 → advisor 报"性价比低" 跳过
  • ❌ 硬编码白名单 → user 立刻怒, "应该用配置"
  • ❌ trader='未知' 闸只拦 open/add/reduce → "熬鹰FOMO" 这种带"熬鹰"前缀但 trader≠白名单 的会绕过
  • ❌ _okx_raw_request 不带 null guard → proxy 抽风时崩 TypeError

8. 关联文件

  • scripts/whitelist.json (新)
  • scripts/process_signal.py (改: classify_signal + 白名单闸 + adjust_leverage_raw + null guard)
  • scripts/okx_position_advisor.py (未改, 但 adjust_leverage 信号绕开它)
  • scripts/signal_tracker.py (acquire_lock / release_lock, 用于手动锁)