trading skills: 文档同步 (单币上限规则/平仓盈亏字段/静默规则/推送模板)

This commit is contained in:
2026-09-25 01:25:13 +08:00
parent c83161d87e
commit f46abb8ca3
111 changed files with 12151 additions and 224 deletions
@@ -0,0 +1,130 @@
---
name: v4.5.53-unknown-trader-gate-2026-08-18
description: "2026-08-18 SNDK 做空 0.095 张误开仓 — parse_signal fallback '未知' 没有 gate。v4.5.53 在 process_signal() 主流程加了 trader='未知' 闸。"
version: 1.0.0
type: reference
status: active
---
# v4.5.53 (2026-08-18) 信号源缺失闸 — trader='未知' 直接跳过
## 事件
**时间**: 2026-08-18
**信号原文**:
```
✅ SNDK 做空 🟥 2x 新开仓
📊 信号源: 未知 158.00 SNDK(价值$?)
📐 性价比检查
• 盈亏比: 3.0:1 ✅
• 盈利额: +21.40 USDT ✅
...
```
**实际执行**:
- 入场: $1724.76
- 仓位: **0.095 张** ⚠️ (不该开)
- TP: $1499.38
- SL: $1799.79
**用户反馈**: "这个没信号源的就不要跟单了"
## 根因分析
`parse_signal()` 在 trader 字段提取不出来时,会**硬编码** `fields['trader'] = '未知'`(参见 `process_signal.py` 第 86 行, 2026-08-07 加的注释: "实在找不到 trader 标签 — 标 '未知' (不要硬编码 'X聚合社区', 会误导)")。
但**主流程 process_signal() 没有任何 gate 去尊重这个 '未知' 标记** —— 它继续往下走:
- `is_signal_stale()` → 通过(没时间戳算新鲜)
- `classify_signal()` → 'open'
- `is_duplicate()` → 新信号
- `run_advisor()` → 返回 rec
- `execute_order()` → **真的下了 0.095 张**
**为什么有这条 fallback 设计却没 gate**:
- 2026-08-07 加 fallback 的目的是:**显示**给用户看时标 "未知",**不要硬编码 'X聚合社区'** 误导
- 当时没人想到"主流程应该拒绝执行 '未知' 来源"
- 这是一个**典型 antipattern**: "parser 加了 fallback 让字段看着像合法值,但下游没加 'reject if fallback'" — parser 兜底 ≠ 业务允许
## 改动 (v4.5.53)
**位置**: `scripts/process_signal.py::process_signal()` 在 `is_signal_stale` 之前插闸
```python
_sig_type_for_gate = classify_signal(fields)
if trader == '未知' and _sig_type_for_gate in ('open', 'add', 'reduce'):
msg = f"""⏭️ **跳过跟单: 信号源未知** ⏭️
📊 币种: {symbol} {side} {leverage}x
🚫 信号文本里没有【交易员】/👉 跟单就选 / 任何 trader 标签
❌ 不自动跟单,避免执行来路不明的开仓
💡 如果这条信号确实要跟, 请手动开仓 + 告诉我 trader 名字, 我会加到名单。
"""
push_to_qq(msg)
# 留痕: tracker outcome=skipped_unknown_source
_tracker_record(trader='未知', ..., outcome='skipped_unknown_source')
return f"⏭️ 跳过: 信号源未知 | {symbol} {side}"
```
**关键设计**:
- **只阻断 open/add/reduce** — close (平仓) 不阻断, 平仓是兜底必须执行
- **推 QQ 通知** — 用户能看到"为什么没跟", 不是静默
- **写 tracker** — outcome='skipped_unknown_source' 留痕, 可查询
- **不修改 parse_signal** — fallback '未知' 保留, 这是正确的语义("我没找到 trader, 我不知道"), 是闸拒绝执行, 不是 parser 假装找到了
## 防回归检查 (下次类似修复时跑)
```python
import sys
sys.path.insert(0, '~/.hermes/skills/trading/okx-auto-position/scripts')
from process_signal import parse_signal, classify_signal
# 测试 1: 合法结构 + trader 缺失 → 应该 skip
text = '''【币种】SNDKUSDT
【方向】做空
【杠杆】2x
【仓位大小】158
【仓位价值】$
【开仓价】1724.76
'''
fields = parse_signal(text)
assert fields['trader'] == '未知', f"fallback broken: {fields.get('trader')}"
assert classify_signal(fields) == 'open'
print('✓ parser fallback + classifier still work — gate will trigger')
```
```python
# 测试 2: 完整 process_signal() 跑一遍 (mock push_to_qq + tracker)
import process_signal
captured = []
process_signal.push_to_qq = lambda msg: (captured.append(msg), True)[1]
result = process_signal.process_signal(text)
assert result.startswith('⏭️ 跳过: 信号源未知')
assert any('跳过跟单: 信号源未知' in m for m in captured)
print('✓ gate fires correctly')
```
## 通用教训 (class-level pitfall)
**Parser fallback without gate is a silent execution hazard.**
任何"parser 提取不到字段 → fallback 到默认值"的代码,如果这个默认值会让**下游误以为是合法值继续执行**,都是这种 bug 的高发地。
检查清单:
1. **parse** 时找不到 → 返回/标记什么?
2. **业务允许**这个 fallback 值进入主流程吗?
3. 如果不允许,**gate 在哪一道**(解析后立即?分类前?执行前?)
4. fallback 时的**通知**机制是什么?(静默跳过会让用户以为没信号,显式推 "跳过了" 才对)
这次的具体教训:
- ❌ parser fallback 到 '未知' → 主流程照样执行 → 误开仓
- ✅ parser fallback 到 '未知' → 主流程识别 '未知' → 跳过 + 通知用户
## 关联
- `SKILL.md` v4.5.53 章节 — 主 skill 已升级到 4.5.53
- `references/signal-source-drift-2026-07-15.md` — trader 名单失效 (相关但不同问题):那个是"知道是哪个 trader 但名单可能错",这个是"连是哪个 trader 都不知道"
- `scripts/process_signal.py:86` — parse_signal 的 fallback 源头
- `scripts/process_signal.py` 新闸位置 — 在 is_signal_stale 之前