arbitrage-engine/docs/ai/06-decision-log.md
fanziqi 22787b3e0a docs: add AI documentation suite and comprehensive code review report
- 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
2026-03-03 19:01:18 +08:00

163 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
generated_by: repo-insight
version: 1
created: 2026-03-03
last_updated: 2026-03-03
source_commit: 0d9dffa
coverage: 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
### 决策 1PostgreSQL 作为进程间消息总线
**决策**:使用 PostgreSQL `NOTIFY/LISTEN` 在 signal_engine 和 live_executor 之间传递信号,而非 Redis pub/sub 或消息队列。
**原因**(从代码推断):
- 系统已强依赖 PG避免引入新的基础设施依赖。
- 信号触发频率低(每 15 秒最多一次PG NOTIFY 完全满足延迟要求。
- 信号 payload 直接写入 `signal_indicators`NOTIFY 仅做触发通知,消费者可直接查表。
**取舍**:单点依赖 PGPG 宕机时信号传递和持久化同时失败(可接受,因为两者本就强耦合)。
**来源**`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改15CPU降60%,信号质量无影响)`
---
### 决策 3agg_trades 表按月范围分区
**决策**`agg_trades` 使用 `PARTITION BY RANGE(time_ms)`,按月创建子表(如 `agg_trades_202603`)。
**原因**
- aggTrades 是最大的写入表(每秒数百条),无分区会导致单表膨胀。
- 按月分区支持高效的时间范围查询PG 分区裁剪)。
- 旧分区可独立归档或删除,不影响主表。
**取舍**:分区管理需要维护(`ensure_partitions()` 自动创建当月+未来2个月分区需定期执行跨分区查询性能取决于分区裁剪是否生效`time_ms` 条件必须是常量)。
**来源**`db.py:191-201, 360-393`
---
### 决策 4Cloud 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 ≥ threshold75"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`
---
### 决策 9live_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] 所有决策均从代码推断,无明确的 ADRArchitecture Decision Record文档。
- [unknown] 策略配置是否支持热重载signal_engine 是否每次循环都重读 JSON未确认。
- [risk] 决策 4双写+ 决策 9live 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_configs
- `backend/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 import
- `frontend/app/page.tsx:149-154` — 轮询间隔
- `backend/strategies/v51_baseline.json`, `v52_8signals.json`