AGENTS.md · diff

git:20261003.52c5a22 to git:20261003.8b2ea6a

2 added, 2 removed. Audit B to B.

# QuantMind 开发指南(AGENTS.md)
本文件为 AI 编码助手(Claude Code / Codex / QwenPaw 等)在此仓库中工作时提供指导。
## 项目概述
QuantMind 是一个量化交易平台,后端为 Python(FastAPI),前端为 Electron/React/TypeScript。开源版(OSS)采用单容器部署,所有后端服务运行在同一个容器中。
## 后端服务(统一入口 `backend/main_oss.py`)
| 服务 | 端口 | 职责 |
|------|------|------|
| api | 8000 | 用户认证、策略管理、社区 |
| engine | 8001 | Qlib 回测、AI 策略生成、模型推理 |
| trade | 8002 | 订单管理、持仓、风控 |
| stream | 8003 | 实时行情、WebSocket 推送 |
## 常用命令
### 后端
```bash
# 启动全部服务(Docker)
docker-compose up -d
# 本地运行单个服务
SERVICE_MODE=api python backend/main_oss.py
# 测试(在项目根目录执行)
python backend/run_tests.py unit # 单元测试
python backend/run_tests.py integration # 集成测试
python backend/run_tests.py all # 全部测试
python backend/run_tests.py trade-long-short # QMT MVP 链路测试
# 代码检查与格式化
ruff check backend/
ruff format backend/
```
### 前端(Electron 应用,位于 `electron/`)
```bash
npm install # 安装依赖
npm run dev # 开发模式(Electron 桌面端)
npm run dev:web # 开发模式(Web 浏览器)
npm run typecheck # 类型检查
npm run dashboard:build # 生产环境构建
```
## 架构要点
- **特征工程**:48 维特征由外部服务写入 `market_data_daily` 表
- **交易服务**:外部报单前强制「本地优先」落库持久化
- **Redis 库分配**:0=通用,1=认证,2=交易,3=行情,4=回测,5=缓存
- **共享模块**:`backend/shared/` 存放跨服务代码(DB 管理器、Redis 客户端、配置、日志)
- **瞬时时间(成交/委托)**:`sim_trades.executed_at` 等瞬时列一律 `TIMESTAMPTZ` + aware UTC。写入走 `backend/shared/utc_datetime.py` 的 `utc_now()` / `UtcDateTime`,JSON 输出带 `Z`。禁止再把 naive UTC 和 Asia/Shanghai 墙钟混用,否则 asyncpg 会让整笔成交回滚。存量库由 `backend/shared/db_init.sql` 第 66 节(存量库收敛,已合并原 `data/upgrade_v1.0.1 ~ v1.0.8`)对齐。
- **策略存储**:`backend/shared/strategy_storage.py` 是所有策略增删改查的唯一入口
- - **报告归档(技能中心「股票报告」页)**:`backend/services/engine/routers/report_archive.py` 是报告文件浏览/上传/移动/删除的唯一入口(前缀 `/api/v1/reports`,经 `engine_proxy.py` 白名单转发到 engine 8001)。归档根目录 `QM_REPORT_ARCHIVE_DIR`(默认 `/data/reports/trading_agents`),结构 `{根}/{市场中文名}/{股票名}/{股票名}{代码}_{日期}_{报告类型}.{md,pdf}`。各 QuantBot 技能落盘后由该模块统一呈现。
- - **投研报告产出方式**:容器内 TradingAgents 多 Agent 图管线(analyze/progress/report/history)**已下线**,禁止再新接;投研报告一律走 `skills/trading-agents`(智能体自主版,任意大模型可跑)等技能包生成 md+PDF 后落盘。
+ - **报告归档(技能中心「股票报告」页)**:`backend/services/engine/routers/report_archive.py` 是报告文件浏览/上传/移动/删除的唯一入口(前缀 `/api/v1/reports`,经 `engine_proxy.py` 白名单转发到 engine 8001)。归档根目录 `QM_REPORT_ARCHIVE_DIR`(默认 `/data/reports/stock_reports`),结构 `{根}/{市场中文名}/{股票名}/{股票名}{代码}_{日期}_{报告类型}.{md,pdf}`。QuantBot 各技能落盘后由该模块统一呈现。
+ - **投研报告产出方式**:投研报告一律由 QuantBot 技能包(`skills/stock-deep-research` 等)生成 md+PDF 后落盘到报告归档目录,由「技能中心 → 报告档案」浏览。**禁止**在 backend 内新增「容器内多 Agent 投研图管线」类实现(历史上有一条 langgraph 管线,因依赖未随镜像构建、线上零调用且无任何产出,已整体下线并删除)。
- **Celery worker 必须唯一**:`SERVICE_MODE=all` 下 `main_oss.py` 默认**不启动**内嵌 worker(需 `EMBEDDED_CELERY_WORKER=true`),消费队列的只有 `celery-worker` 容器。重复 worker 会瓜分 `qlib_backtest_srv` 队列消息,表现为定时任务随机「不执行」;排查看 `redis-cli client list | grep cmd=brpop` 应只有 1 个。
- **市场数据同步不内置默认调度**:是否开启、何时触发一律以用户在前端「同步调度」保存的 Redis 配置为准(`quantmind:sync_schedule:{market}`),未配置时 5 个市场全部 `enabled=false`;`MARKET_SUGGESTED_TIMES` 只是前端时间预填建议值(次日 00:00 以后错峰),不参与触发。详见 `backend/services/engine/README.md` →「定时调度与市场数据同步」。
- **代码版本落后提示(硬性规则)**:管理后台右上角「落后 N 个提交」只允许走下面这条链路。禁止改回 Gitee/GitHub compare 接口,禁止用两边 `git rev-list --count` 相减,禁止把计数文件提交进 `master`(会每记一次就多一个提交,而且文件内容永远比 HEAD 少 1)。
- 上游真相是客户会拉到的 Gitea `master`(绝对地址 `https://quantmindai.cn/gitea/qusong0627/QuantMInd.git`)。GitHub、Gitee 只是镜像,不能单独当计数源。
- 维护端在推送 `master` 之后执行 `python scripts/publish_release_index.py --push`。脚本用 `git rev-list` 生成 `release-index.json`(`commits` 从新到旧,`head` 为第一项),并强制更新远端分支 `release-index`(只动这一支)。客户服务器禁止运行该脚本。
- 客户后端只读 raw 绝对地址,默认 `https://quantmindai.cn/gitea/qusong0627/QuantMInd/raw/branch/release-index/release-index.json`,可用 `QUANTMIND_RELEASE_INDEX_URL` 覆盖。本机部署提交来自 `deploy/update.sh` 写入的 `backend/shared/version.json`(完整 `commit` 与 `rev_count`,已 gitignore)。落后数等于本机 SHA 在 `commits` 里的下标;SHA 不在列表中时 `status=diverged`,前端不显示个数。
- 容器内没有 `.git`。禁止在运行中的后端里 `git fetch` / `git rev-list`。对比逻辑只在 `backend/shared/version.py`。
## 股票代码标准化(重要,分层口径)
- **QuantDB parquet / Qlib / 行情数据层**:后缀式(如 `600036.SH`,Qlib 桥接用全小写 `sh600036`)。查 parquet、`stock-terminal`(仅接受后缀校验)、Qlib `D.features` 时必须用此格式,否则静默查空。
- **PG 数据库字段 / Redis 键 / 前端 / Strategy Lab SDK / 大多数 API**:前缀式(如 `SH600036`)。
- **层边界必须经 `StockCodeUtil` 显式转换,禁止散落手写切片**,禁止跨层混用:
- 后端:`backend/shared/stock_utils.py` → `StockCodeUtil.to_suffix(code)` / `.to_prefix(code)` / `.to_qlib(code)`
- 前端:`electron/src/utils/portfolioUtils.ts` → `normalizeStockCode(code)`(输出前缀式)
- **Redis 键格式**:
- 快照:`market:snapshot:sh600036`(快照键用小写前缀)
- 序列:`market:series:SH600036`(序列用标准前缀式)
- **市场自动识别**:
- `SH`:6xxxxx、9xxxxx
- `SZ`:0xxxxx、3xxxxx、2xxxxx
- `BJ`:4xxxxx、8xxxxx
## 环境变量
必需的 `.env` 键(默认值见 `docker-compose.yml`):
- `DB_HOST`、`DB_PORT`、`DB_NAME`、`DB_USER`、`DB_PASSWORD`
- `REDIS_HOST`、`REDIS_PORT`
- `SECRET_KEY`、`JWT_SECRET_KEY`
- `STORAGE_MODE=local`(OSS 版必需)
## 代码风格
- Python:行宽 88,使用 ruff 做检查与格式化
- TypeScript:提交前端改动前必须运行 `npm run typecheck`
## 开发与部署工作流
### 1. 前端开发(NPM 模式)
- **本地开发模式**:前端统一使用本地 `npm run dev`(Electron 桌面端 / Vite Web 模式,自带 HMR 热重载)。
- **前端修改规则**:**修改前端(electron/src)代码后,不需要每次重新构建或重启服务器上的 `web` 容器**,本地可实时热重载预览调试。提交前运行 `npm run typecheck` 保证类型安全即可。
### 2. 后端同步与部署
- **后端修改规则**:**修改后端(backend/)代码后,必须推送到仓库并同步重启远程服务器上的后端容器**。
```bash
# 1. 本地提交并推送
git add .
git commit -m "descriptive message"
git push gitee NEXT
# 2. 同步并重启后端服务(服务名见 docker compose config --services)
# 注意:目标服务器的 SSH 别名/主机与项目目录因人而异,部署前先向用户询问确认。
# 用 ${SSH_TARGET} 和 ${PROJECT_DIR} 表示用户提供的具体值。
ssh ${SSH_TARGET} "cd ${PROJECT_DIR} && git pull && docker compose restart quantmind celery-worker celery-beat"
```
### 3. 镜像构建规则(是否需重新打包)
- **后端代码走 bind mount**(`./backend:/app/backend` 等挂载进容器),镜像只含 Python 依赖环境。
- **纯代码改动(未新增 pip 依赖、未改 Dockerfile/构建参数)**:只需 `git pull && docker compose restart`,**无需重新打包镜像**。
- **需要重build 的场景**:①新增了 `requirements.txt` 未收录的 Python 依赖;②升级 torch/qlib/duckdb 等底层库;③全新服务器首次部署无现成镜像。
- **重build 方式**(利用 Docker build cache,通常仅增量安装新增包):服务器上执行 `docker compose build quantmind`,再 `docker compose up -d`。
- 本地 Windows 无法直接构建 linux/amd64 镜像,重build 一律在服务器或 CI 上进行。
### 4. QwenPaw(QuantBot)技能更新
- **统一入口**:`bash scripts/quantbot_init.sh`(在服务器上、项目根目录执行)。一次完成:本地 `skills/` 全部技能经 API 导入技能池 → 广播到 `default` 工作区并启用 → 写入量化人格(SOUL/PROFILE/AGENTS)。
- **部署脚本硬性规则**:`deploy/update.sh` / `deploy/full-deploy.sh` 的技能同步**只允许**调用 `deploy/sync-qwenpaw-skills.sh`。该脚本经 `docker exec qwenpaw` 探活并执行 `quantbot_init`(容器内 `127.0.0.1:8088`)。**禁止**在部署脚本里再写 `curl http://127.0.0.1:$QWENPAW_PORT/health` 或在 updater 容器宿主网络外直跑 `quantbot_init`——Web 一键更新的 updater 是 bridge 网络,`127.0.0.1` 不是宿主,会导致「升级成功但技能/人格未同步」。
- **禁止手工拷贝技能目录**(如直接 `rsync`/`docker cp` 到 `skill_pool/` 或 `workspaces/default/skills/`):磁盘文件与 `skill.json` 清单会漂移,导致后续上传冲突(31 个技能全量 conflict)且无法经 API 删除,只能逐个手工清理。
- 只更新技能:`--skills-only`;只写人格:`--persona-only`。
- 技能更新后需 `docker restart qwenpaw` 使其生效;验证用 `docker exec qwenpaw qwenpaw skills list`(技能数与启用数应一致)和 `docker exec qwenpaw qwenpaw skills test <name>`。
- QwenPaw 镜像自带 reportlab 与中文字体(`docker/Dockerfile.qwenpaw`),报告 PDF 转换优先在 qwenpaw 容器内直接执行 `python3 /app/backend/scripts/md_to_pdf_report.py`(backend 为 bind mount,共享 `/data` 卷),无需 `docker exec quantmind`。
## 关键文件
- `backend/main_oss.py` - 全部后端服务的统一入口
- `backend/run_tests.py` - 多模式测试运行器
- `backend/shared/` - 跨服务共享模块
- `backend/shared/version.py` - 部署版本读取与 Gitea release-index 落后提交对比(唯一入口)
- `backend/services/engine/routers/report_archive.py` - 报告归档 API(技能中心「股票报告」页后端)
- `scripts/publish_release_index.py` - 维护端发布 `release-index` 分支(推送 master 后执行)
- `docker-compose.yml` - 本地部署配置
- `scripts/quantbot_init.sh` - QwenPaw 技能/人格一键初始化(技能更新唯一入口)
- `deploy/sync-qwenpaw-skills.sh` - 部署链路技能同步唯一实现(经 docker exec qwenpaw,供 update/deploy/full-deploy 调用)
- `docker/Dockerfile.qwenpaw` - QwenPaw 扩展镜像(reportlab + docker CLI)