- Generate full AI-consumable docs (docs/ai/): system overview, architecture, module cheatsheet, API contracts, data model, build guide, decision log, glossary, and open questions (deep tier coverage) - Add PROBLEM_REPORT.md: categorized bug/risk summary - Add DETAILED_CODE_REVIEW.md: full line-by-line review of all 15 backend files, documenting 4 fatal issues, 5 critical deployment bugs, 4 security vulnerabilities, and 6 architecture defects with prioritized fix plan
8.0 KiB
| generated_by | version | created | last_updated | source_commit | coverage |
|---|---|---|---|---|---|
| repo-insight | 1 | 2026-03-03 | 2026-03-03 | 0d9dffa |
deep |
06 — Decision Log
Purpose
记录项目中关键的技术决策、选型原因及权衡取舍(从代码注释和架构特征推断)。
TL;DR
- 选择 PostgreSQL 作为唯一消息总线(NOTIFY/LISTEN),避免引入 Kafka/Redis 等额外组件。
- signal_engine 改为 15 秒循环(原 5 秒),CPU 降 60%,信号质量无影响。
- 双写 Cloud SQL 作为灾备,失败不阻断主流程。
agg_trades按月分区,避免单表过大影响查询性能。- 认证采用自研 HMAC-SHA256 JWT,不依赖第三方库。
- 前端使用 Next.js App Router + 纯客户端轮询,不使用 WebSocket 推送。
- 策略参数外置为 JSON 文件,支持热修改无需重启进程。
- 信号评分采用多层加权体系(5层),每层独立可调,支持多策略并行。
Canonical Facts
决策 1:PostgreSQL 作为进程间消息总线
决策:使用 PostgreSQL NOTIFY/LISTEN 在 signal_engine 和 live_executor 之间传递信号,而非 Redis pub/sub 或消息队列。
原因(从代码推断):
- 系统已强依赖 PG;避免引入新的基础设施依赖。
- 信号触发频率低(每 15 秒最多一次),PG NOTIFY 完全满足延迟要求。
- 信号 payload 直接写入
signal_indicators表,NOTIFY 仅做触发通知,消费者可直接查表。
取舍:单点依赖 PG;PG 宕机时信号传递和持久化同时失败(可接受,因为两者本就强耦合)。
来源:live_executor.py:1-10 架构注释,signal_engine.py:save_indicator 函数。
决策 2:信号引擎循环间隔从 5 秒改为 15 秒
决策:LOOP_INTERVAL = 15(原注释说明原值为 5)。
原因:代码注释明确写道 "CPU降60%,信号质量无影响"。
取舍:信号触发延迟最坏增加 10 秒;对于短线但非高频的策略(TP/SL 以 ATR 倍数计,通常 >1% 波动),10 秒的额外延迟影响可忽略不计。
来源:signal_engine.py:39 LOOP_INTERVAL = 15 # 秒(从5改15,CPU降60%,信号质量无影响)
决策 3:agg_trades 表按月范围分区
决策:agg_trades 使用 PARTITION BY RANGE(time_ms),按月创建子表(如 agg_trades_202603)。
原因:
- aggTrades 是最大的写入表(每秒数百条),无分区会导致单表膨胀。
- 按月分区支持高效的时间范围查询(PG 分区裁剪)。
- 旧分区可独立归档或删除,不影响主表。
取舍:分区管理需要维护(ensure_partitions() 自动创建当月+未来2个月分区,需定期执行);跨分区查询性能取决于分区裁剪是否生效(time_ms 条件必须是常量)。
来源:db.py:191-201, 360-393
决策 4:Cloud SQL 双写(非阻塞)
决策:所有写入操作在本地 PG 成功后,尝试相同写入到 Cloud SQL(10.106.0.3),Cloud SQL 失败不影响主流程。
原因:提供数据异地备份;Cloud SQL 作为只读副本或灾备使用。
取舍:
- 本地 PG 和 Cloud SQL 可能出现数据不一致(local 成功 + cloud 失败)。
- 双写增加每次写操作的延迟(两个网络 RTT),但因为是 best-effort 且使用独立连接池,实际阻塞极少。
- live_executor 直接连 Cloud SQL(
DB_HOST=10.106.0.3),绕过本地 PG。
来源:db.py:23-29, 80-118,live_executor.py:50-55
决策 5:自研 JWT(不用 PyJWT 等第三方库)
决策:使用 Python 标准库 hmac、hashlib、base64 手动实现 JWT 签发和验证。
原因(推断):减少依赖;JWT 结构相对简单,HMAC-SHA256 签名几十行代码即可实现。
取舍:
- 需要自行处理过期、revoke、refresh token 等逻辑(代码中已有
refresh_tokens表)。 - 非标准实现可能在边界情况(时钟偏差、特殊字符等)上与标准库行为不同。
- 无 JWT 生态工具支持(调试工具、密钥轮转库等)。
来源:auth.py:1-6(import hashlib, secrets, hmac, base64, json),auth.py:16-19
决策 6:策略配置外置为 JSON 文件
决策:V5.x 策略的权重、阈值、TP/SL 倍数等参数存放在 backend/strategies/*.json,signal_engine 每次 load_strategy_configs() 读取。
原因:
- 策略调优频繁(v51→v52 权重变化显著),外置避免每次改参数都要修改代码。
- 多策略并行:signal_engine 同时运行 v51_baseline 和 v52_8signals,对每个 symbol 分别评分。
- [inference] 支持未来通过前端或 API 修改策略参数而不重启进程(目前 signal_engine 每次循环重读文件 —— 需确认)。
取舍:JSON 文件无类型检查,配置错误在运行时才发现;缺少配置 schema 校验。
来源:signal_engine.py:41-67,backend/strategies/v51_baseline.json,backend/strategies/v52_8signals.json
决策 7:信号评分采用五层加权体系
决策:信号评分分为 5 个独立层次(方向层、拥挤层、资金费率层、环境层、确认层、清算层、辅助层),每层有独立权重,总分 0~100,阈值 75 触发信号。
设计特点:
- 方向层(CVD)权重最高(V5.1: 45分,V5.2: 40分),是核心指标。
- "standard" 档位:score ≥ threshold(75);"heavy" 档位:score ≥ max(threshold+10, 85)。
- 信号冷却:同一 symbol 同一策略触发后 10 分钟内不再触发。
- CVD 快慢线需同向才产生完整方向信号;否则标记
no_direction=True不触发。
取舍:权重缩放逻辑较复杂(各层原始满分不统一,需先归一化再乘权重);market_indicators 缺失时给默认中间分,保证系统在数据不完整时仍能运行。
来源:signal_engine.py:410-651
决策 8:前端使用轮询而非 WebSocket
决策:React 前端对 /api/rates 每 2 秒轮询,慢速数据(stats/history/signals)每 120 秒轮询,K 线图每 30 秒刷新。
原因(推断):
- 实现简单,无需维护 WebSocket 连接状态和断线重连逻辑。
- 数据更新频率(2 秒/30 秒)对轮询友好;WebSocket 的优势在于毫秒级推送。
- FastAPI 已支持 WebSocket,但实现 SSE/WS 推送需要额外的后端状态管理。
取舍:每 2 秒轮询 /api/rates 会产生持续的服务器负载;当用户量增加时需要加缓存或换 WebSocket。
来源:frontend/app/page.tsx:149-154
决策 9:live_executor 和 risk_guard 直连 Cloud SQL
决策:live_executor.py 和 risk_guard.py 默认 DB_HOST=10.106.0.3(Cloud SQL),而不是本地 PG。
原因(推断):这两个进程运行在与 signal_engine 不同的环境(可能是另一台 GCP VM 或容器),直连 Cloud SQL 避免通过本地 PG 中转。
取舍:live_executor 和 signal_engine 使用不同的 PG 实例,理论上存在数据读取延迟(双写同步延迟)。
来源:live_executor.py:50-55,risk_guard.py:47-53
Interfaces / Dependencies
无额外接口依赖,均为内部架构决策。
Unknowns & Risks
- [inference] 所有决策均从代码推断,无明确的 ADR(Architecture Decision Record)文档。
- [unknown] 策略配置是否支持热重载(signal_engine 是否每次循环都重读 JSON)未确认。
- [risk] 决策 4(双写)+ 决策 9(live executor 直连 Cloud SQL)组合下,若本地 PG 和 Cloud SQL 数据不一致,live_executor 可能读到滞后的信号或重复执行。
Source Refs
backend/signal_engine.py:39— LOOP_INTERVAL 注释backend/signal_engine.py:44-67— load_strategy_configsbackend/signal_engine.py:410-651— evaluate_signal 完整评分逻辑backend/db.py:23-29, 80-118— Cloud SQL 双写连接池backend/live_executor.py:50-55— DB_HOST 配置backend/auth.py:1-6— 自研 JWT importfrontend/app/page.tsx:149-154— 轮询间隔backend/strategies/v51_baseline.json,v52_8signals.json