# v4.5.4 平仓信号 dedup 误跳 — 真实事件复现 + 用户视角教训 **时间**: 2026-07-08 **事件**: 熬鹰ETH short 减仓→平仓 信号链 **触发用户反馈**: "要是我没发现,就一直不推了吗?重试机制呢" --- ## 事件完整时间线 1. **15:00** 熬鹰 ETH short 1773张 减仓信号 - process_signal 返回 `✅ 已推送 | ETH short 5x | 0.64张 | 性价比低` - 用户持仓: ETH short 1.85张 @5x (avgPx 1759.78, 浮亏 -$3.52) - ⚠️ 这是"减仓信号",不是"新开仓" — 本应触发 advisor 加仓路径 2. **15:01** 熬鹰 ETH short 平仓信号 (跟单号:`🚨 已平仓提醒`, +11.23%) - process_signal 返回 `⏭️ 重复信号,跳过` - **❌ BUG**: 平仓信号走了2分钟窗口 dedup,没走 close 独立通道 - ETH short 1.85张 **未自动平仓** 3. **15:05** ETH 继续涨到 1795 - 用户浮亏扩大到 -$6.58 - 用户追问: "这个没推平仓信号" 4. **15:06** agent 手动重跑同一平仓信号 - process_signal 返回 `✅ 平仓处理: closed | ETH short` - ETH short 1.85张 全平成功 - USDT 回到 $106.34 --- ## 根因 `process_signal.py` 主流程顺序错误: ```python # 错误顺序: dedup_conn = init_dedup_db() if is_duplicate(dedup_conn, text, symbol, trader): # ← close 信号在这里被误跳 return "⏭️ 重复信号,跳过" signal_type = classify_signal(fields) if signal_type == 'close': # 平仓逻辑,永远到不了这里 ... ``` `is_duplicate()` 的2分钟窗口把"减仓+平仓"两个**完全不同语义**的信号合并去重了。 --- ## 修复 (v4.5.4) ```python # 正确顺序: signal_type = classify_signal(fields) # ← 先分类 if signal_type == 'close': dedup_conn = init_dedup_db() msg_hash = hashlib.md5(text.encode()).hexdigest() # close 信号只看 raw_text hash 唯一性,不看2分钟窗口 if dedup_conn.execute("SELECT 1 FROM processed WHERE msg_hash = ?", (msg_hash,)).fetchone(): return "⏭️ 平仓信号完全重复跳过" # 走平仓独立路径 close_result = close_position_raw(symbol_usdt, signal_side_en) ... # 其他信号 (open/reduce) 才走2分钟窗口 dedup dedup_conn = init_dedup_db() if is_duplicate(dedup_conn, text, symbol, trader): return "⏭️ 重复信号,跳过" ``` **关键设计原则**: - `is_duplicate()` 永远**不应用于 close 信号** - close 信号 dedup 只能基于 raw_text hash (因为 text 内含最终收益额+标记价,天然唯一) - 平仓动作必须发生 — 用户可能不会逐条核对内部状态 --- ## 用户视角的关键反馈 (2026-07-08) - 用户**不会逐条核对 process_signal 返回值**——他只看 QQ 有没有收到平仓结果 - 一旦他认为"应该平但没平",他不会去查 process_signal 内部状态,而是**怀疑系统坏了** - 必须实现**重试机制**: 当平仓信号 dedup 命中时,不能简单 skip,必须**至少推一条"⚠️ 平仓信号疑似跳过,请人工确认持仓"到 QQ** --- ## 实测结果 修复后再次收到 ETH short 平仓信号 → 走独立通道 → 自动平仓成功 ✅ 推送 git commit: `v4.5.4: 平仓信号 dedup 误跳 bug 修复 — close 信号走独立通道 (2026-07-08 实测)` --- ## 给未来 session 的提醒 1. **不要相信 is_duplicate() 适用于所有信号类型** — 它是为 open/reduce 设计的 2. **close 信号是"必须执行"的命令**,不能和 open/reduce 共用去重窗口 3. **用户视角优先**: 平仓失败=系统坏了,不是"按规则跳过了" 4. **process_signal 返回值 ≠ 实际行为**: 返回"重复信号"不代表持仓已处理,必须查 raw REST 验证