# v4.5.45 (2026-08-06) 锁状态推送 + 自动解锁 + 手动解锁 CLI ## 背景 用户连续纠正: - "锁了要你要消息推给我啊,不然我每次去问吗" → 锁了必须推 QQ - "现在都平仓了,肯定解锁啊" → 平仓必须自动解锁 - "还有几个锁?每次推送时要显示所有锁的状态" → 每次推送附所有锁 - "这就是了" → 用户期待"我转发信号 → 正常跟单",不要每次问 ## 3 个新行为 ### 1. 锁了必须推 QQ(原行为是沉默) **旧行为** `process_signal.py`: ```python allowed, lock_msg = check_signal_lock(symbol, trader, side_en) if not allowed: return f"🔒 {lock_msg}" # ❌ 只回 return, 不推 QQ ``` **新行为** (2026-08-06): ```python allowed, lock_msg = check_signal_lock(symbol, trader, side_en) if not allowed: msg = f"""🔒 **跟单被锁** 🔒 📊 币种: {symbol} 📈 当前信号: {trader} {side} {leverage}x 🚫 锁信息: {lock_msg} 💡 提示: 同币种只能锁一个 trader. 如要切跟, 用 `/unlock {symbol}` 解锁或告诉我换 trader. """ push_to_qq(msg) return f"🔒 {lock_msg} | 已推 QQ" ``` **为什么**: 用户原话"锁了要你要消息推给我啊,不然以为是Bug"。 ### 2. 平仓自动解锁 **旧行为**: close 信号 → 平仓 → 锁**留着**(下次同币种信号又拒) **新行为** (2026-08-06): ```python if signal_type == 'close': ... close_result = close_position_raw(symbol_usdt, signal_side_en) ... # 2026-08-06: 平仓后自动释放锁 from signal_tracker import release_lock released, lock_msg = release_lock(symbol_usdt, trader, auto=True) if released: msg += f"\n\n🔓 锁已释放: {lock_msg}" push_to_qq(msg) return f"✅ 平仓处理: {close_result['action']} | {symbol_usdt} {signal_side_en} | {lock_msg}" ``` **为什么**: 用户原话"现在都平仓了,肯定解锁啊"。 **lock_expiry 在 signal_tracker.py**: 默认 72h,但平仓不触发自动 unlock — **现在修了**。 ### 3. 每次推送附所有锁状态 **新增 helper** `_locks_status_text()`: ```python def _locks_status_text(): """所有锁状态 - 附加在每个推送尾部""" try: locks = list_locks() except Exception: return "" if not locks: return "" lines = ["\n\n🔒 **当前所有锁**:"] for l in locks: symbol = l['symbol'] trader = l['trader'] side = l['side'] locked_at = l['locked_at'] from datetime import datetime try: ts = datetime.fromisoformat(str(locked_at).replace('Z', '')) hours = (datetime.now() - ts).total_seconds() / 3600 age = f"{hours:.1f}h" except Exception: age = "?" lines.append(f" • {symbol} → **{trader}** {side} (锁 {age})") return "\n".join(lines) ``` **为什么**: 用户原话"还有几个锁?每次推送时要显示所有锁的状态,不然怎么记得呢"。 **未来集成点**: 加到 `push_to_qq(msg)` 之后(写在每个推送尾部): ```python msg += _locks_status_text() push_to_qq(msg) ``` **当前状态**: helper 已写但**未集成到主流程**。下次修复时把 helper 嵌入到所有 push_to_qq() 之后。 ### 4. 手动解锁 CLI **新增 CLI** `process_signal.py unlock`: ```bash # 列出所有锁 python3 process_signal.py unlock # 解锁指定币种 python3 process_signal.py unlock BTC # 解锁指定 trader 的锁(通常 auto=True) python3 process_signal.py unlock BTC 熬鹰资本 ``` **实现**: ```python def main(): if _sys.argv[1] == 'unlock': from signal_tracker import release_lock, list_locks if len(_sys.argv) < 3: locks = list_locks() print(locks or "无锁") return symbol = _sys.argv[2] trader = _sys.argv[3] if len(_sys.argv) > 3 else None released, msg = release_lock(symbol, trader, auto=True) push_to_qq(f"🔓 手动解锁: {msg}") ``` **为什么**: 给用户**手动解锁**的能力,不依赖平仓信号自动 unlock。 ## 验证 (2026-08-06) ```bash # 1. unlock 列表 $ python3 process_signal.py unlock [ {'symbol': 'BTC', 'trader': '熬鹰资本', 'side': 'long', 'locked_at': '...'}, {'symbol': 'ETH', 'trader': '麻吉大哥', 'side': 'long', 'locked_at': '...'}, ... ] # 2. 解锁 BTC $ python3 process_signal.py unlock BTC 🔓 解锁 BTC (前锁: 熬鹰资本 long) ``` ## 完整时间线 (2026-08-06) | 事件 | 时间(北京) | 动作 | |------|------|------| | 用户转发 BTC 做空信号 | 之前 | #45 入库,被熬鹰资本锁拒 | | 用户质问"做空信号没跟单" | 23:00 | 查锁:BTC 被熬鹰资本 long 锁 | | 用户说"锁了要推我" | 23:00 | 改 push_to_qq 锁拒绝路径 | | 用户说"平仓了肯定解锁" | 23:01 | 改 close 路径 auto-unlock | | 用户说"每次推送看所有锁" | 23:02 | 写 _locks_status_text() | | 用户说"刚才 BTC 再处理一次" | 23:05 | 手动 `unlock BTC` | | 用户转发测试 BTC short | 23:08 | 跑通,1.21 张,但实际未下单(测试单) | ## 仍未做的 1. **集成 `_locks_status_text()` 到所有 push_to_qq** — 当前只在 lock 拒绝路径用了 2. **close 自动 unlock 推到所有 push** — 当前只在 close 路径跑了一次 3. **unlock 应该有更智能的"自动检测平仓"** (检测 OKX 真实持仓为 0 时 unlock) ## 关联 - `signal_tracker.py` — `check_signal_lock`, `release_lock`, `list_locks` - `v4.5.41-signal-queue-done-but-no-order-execute.md` — 下游执行 - `v4.5.42-insufficient-funds-explicit-notify.md` — 余额不足显式推送 - `forwarder-rule-enable-killer.md` — 上游链断排查 - `agent-workflow-feedback-rules.md` — 用户纠正的工作流硬规则