# 交易信号三库架构 — DB Topology (2026-08-25) > User asked "币圈持仓有平仓信号吗?" → 暴露 schema 错位:我先查了不存在的 `recent_signals` 表,才意识到三个 DB 各司其职。 ## 三个 DB 各管一段 ``` ┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐ │ TelegramForwarder │ │ signal_queue.db │ │ signal_history.db │ │ db/forward.db │───→│ .hermes/trading/ │───→│ .hermes/trading/ │ │ │ │ │ │ │ │ Tables: │ │ Tables: │ │ Tables: │ │ • chats │ │ • queue │ │ • signals │ │ • users │ │ • sqlite_sequence │ │ • confirmed_signals │ │ • forward_rules ⚠️ │ │ │ │ • position_history │ │ • replace_rules │ │ Cols: id, raw_text, │ │ • position_locks ⚠️ │ │ • media_types │ │ status, created_at, │ │ • sqlite_sequence │ │ • keywords │ │ processed_at, │ │ │ │ • push_configs │ │ result, error, │ │ │ │ • rss_* │ │ retries │ │ │ │ • rule_syncs │ │ │ │ │ └──────────────────────┘ └──────────────────────┘ └──────────────────────┘ 转发规则 实时队列 历史 + 持仓锁 ``` ## 各表职责 ### 1. `forward.db::forward_rules` — 转发规则 - `enable_rule` 字段(0/1)是 silent killer:8h+ 无信号先查这条(v4.5.43,2026-08-06 session) - 管 TG channel → 内部 queue 的路由 - 与本 skill 无直接关系,但 `enable_rule=0` 会让 queue 不进数据 ### 2. `signal_queue.db::queue` — **实时信号流**(source of truth for "现在有没有信号") - `status='done'` + `result` 字段 = 最新处理结果 - `result` 内容:`✅ 已推送 | X long 5x | 28.0张 | 性价比高` / `⏭️ 跳过: 性价比低` / `⏭️ 跳过: trader [未知] 不在白名单` - **问"现在有没有平仓信号"先查这里** - 看 `raw_text` 是否含 `🚨 已平仓提醒` / `已平仓` ### 3. `signal_history.db::signals` — 历史信号落库 - 只追加,反映"曾经来过什么信号"(无论 execute 是否成功) - 有 `outcome` + `outcome_time` 字段记录执行结果 - **不是实时信号源**(等 queue 处理完才写) ### 4. `signal_history.db::position_locks` — **持仓归属**(source of truth for "现在持仓是谁的") - 唯一字段:`symbol, trader, side, locked_at, reason, auto_locked` - 当前持仓要查这张表(不是 `signals`) - `locked_at` 是 ISO 字符串(不是 timestamp!),parse 时 `datetime.fromisoformat()` - 用户问"持仓谁的" → 先看这张 ### 5. `signal_history.db::confirmed_signals` — execute 记录 - 哪条信号真正触发了下单 - v4.5.51 lock 追溯方法的核心表 ## 常见错位与正解 | 用户问题 | ❌ 错查 | ✅ 正查 | |---|---|---| | "现在持仓多少?" | `signals` 表(只有历史) | `position_locks` | | "持仓是谁的?" | `signals.trader`(可能 stale) | `position_locks.trader` | | "现在有没有平仓信号?" | `signals` 表(等落库有延迟) | `signal_queue.db::queue` (status='done') | | "X 信号 execute 成功了吗?" | `signals.outcome` | `confirmed_signals` | | "TG 转发还活着吗?" | `signals` 表 | `forward.db::forward_rules.enable_rule` | ## 实战速查 (一键脚本) ```python # 当前持仓 (5 个字段) sqlite3 /home/openclaw/.hermes/trading/signal_history.db \ "SELECT symbol, trader, side, locked_at, reason FROM position_locks;" # 最近 24h 平仓信号 (从 queue 实时拿) python3 -c " import sqlite3 conn = sqlite3.connect('/home/openclaw/.hermes/trading/signal_queue.db') rows = conn.execute(''' SELECT created_at, raw_text, result FROM queue WHERE status='done' AND raw_text LIKE '%已平仓%' ORDER BY id DESC LIMIT 5 ''').fetchall() for ts, txt, res in rows: print(ts, res, txt[:80]) " # forwarder 规则是否启用 sqlite3 /home/openclaw/TelegramForwarder/db/forward.db \ "SELECT id, source_chat, dest_chat, enable_rule FROM forward_rules;" ``` ## 教训 (本次 session 2026-08-25) 1. **`recent_signals` 表在 `signal_dedup.db`** — `signal_history.db` 里没有它(我第一查就报 "no such table: recent_signals") 2. **`position_locks.locked_at` 是 ISO 字符串不是 timestamp** — 不要 `int(timestamp/1000)`,用 `datetime.fromisoformat()` 3. **`signals` 表的 `timestamp` 字段是 ISO 字符串** — 同样不能用 `/1000` 转 unix 4. **`signal_queue.db::queue.created_at` 是 ISO 字符串** — 用 `datetime.fromisoformat()` parse ## 跟 v4.5.51 / v4.5.56 的关系 v4.5.51 锁追溯三步 (`position_locks` → `confirmed_signals` → `recent_signals`) 已覆盖,但**没说明为什么用三张表**。本 reference 补充了 DB 拓扑(为什么三表 + 各表职责),让"三步追溯"不再像 magic incantation。 ## 维护建议 - 任何新增"信号处理流程"的代码 → 必须先说明写入哪张表(queue / signals / confirmed_signals / position_locks) - 任何"实时状态"查询 → 必须说明用的是 queue 还是 signals - 新加 DB 必然更新本 reference + 顶部架构图