From 0ffd574f3087bff390ccfb70a211fce3900a6a93 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 8 Sep 2026 23:58:51 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=B8=8B=E9=98=B6=E6=AE=B5=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E8=AE=A1=E5=88=92=EF=BC=88=E6=9E=B6=E6=9E=84=20v2=20?= =?UTF-8?q?=E8=90=BD=E5=9C=B0=20M6-M8=EF=BC=89+=20MySQL=20=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E6=96=87=E6=A1=A3=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/DEV_PLAN_v2.md:基于 ARCHITECTURE_v2 与 M0-M5 现状的下一阶段计划 (M-DB 迁移收尾 → M6 因子定义入库/复合因子/口径修复 → M7 Selection/Signal/ Portfolio 引擎分层 → M8 Strategy 平台化/Web 做实/Agent 工具补齐;含本机 Redis 127.0.0.1:6379 的接入触发点与执行顺序) - ROADMAP.md:登记 M-DB 里程碑并指向 DEV_PLAN_v2 - USAGE.md / README.md:数据库描述由 SQLite 更新为 MySQL(config.yaml database.mysql) --- README.md | 6 +- docs/DEV_PLAN_v2.md | 241 ++++++++++++++++++++++++++++++++++++++++++++ docs/ROADMAP.md | 7 +- docs/USAGE.md | 32 ++++-- 4 files changed, 272 insertions(+), 14 deletions(-) create mode 100644 docs/DEV_PLAN_v2.md diff --git a/README.md b/README.md index 9b693c2..03f80bb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 个人 A 股量化研究平台:**选股 · 因子研究 · 回测**,面向低频 / 中低频交易研究。 -> 核心量化引擎:[Qlib](https://github.com/microsoft/qlib) · 首选数据源:Tushare(新浪财经为备用)· 当前数据库:SQLite(未来 MySQL,业务层无感切换) +> 核心量化引擎:[Qlib](https://github.com/microsoft/qlib) · 首选数据源:Tushare(新浪财经为备用)· 当前数据库:MySQL(业务层无感切换;SQLite 仅兜底) ## 定位 @@ -22,7 +22,7 @@ FastAPI (backend) ↓ Research Specification Application Service ── Quant Service ── Qlib Adapter ── Qlib ↓ ↓ -Repository / DAO Parquet / SQLite +Repository / DAO Parquet / MySQL ``` ### 目录结构 @@ -85,7 +85,7 @@ uv run alembic revision --autogenerate -m "add xxx table" # 修改 Model 后 ## 开发阶段(对应架构文档 §20) -- **Phase 1 数据**:Tushare → 标准化 → SQLite(stock / daily / 复权因子 / 交易日历 / 财务指标),Parquet 导出 +- **Phase 1 数据**:Tushare → 标准化 → MySQL(stock / daily / 复权因子 / 交易日历 / 财务指标),Parquet 导出 - **Phase 2 Qlib**:Parquet → Qlib Dataset → 因子(Alpha158 / 自定义)→ LightGBM → 回测 - **Phase 3 Web**:股票池 / 因子研究 / 选股 / 回测 / 结果可视化(Next.js + ECharts) - **Phase 4 Experiment**:所有研究自动可复现存档 diff --git a/docs/DEV_PLAN_v2.md b/docs/DEV_PLAN_v2.md new file mode 100644 index 0000000..63a4922 --- /dev/null +++ b/docs/DEV_PLAN_v2.md @@ -0,0 +1,241 @@ +# 下阶段开发计划(架构 v2 落地 · 2026-09) + +> 依据:[ARCHITECTURE_v2.md](./ARCHITECTURE_v2.md)(新架构 v2)、[AGENT.md](../AGENT.md)(开发约束)、 +> 两份 2026-09 只读调研(后端研究/量化层、前端/Agent/数据层)与 docs/ROADMAP.md 的 M0–M5 记录。 +> 本文件回答:**M5(AI Agent Phase 5)之后,下一阶段做什么、按什么顺序做。** + +--- + +## 0. 当前状态与总判断 + +**已完成(M0–M5)**:数据层(Tushare 首选 + 新浪兜底 + 审计)→ ResearchSpec → 本地研究引擎 +(9 个行情因子 / IC·分层评估 / 低频 TopN 等权回测)→ Job/Experiment 异步归档 → Web 六页 → +AI Agent(6 个受控 Tool)。**数据库已于 2026-09 从 SQLite 全量迁移至 MySQL**(见 §5)。 + +**对照 v2 的核心差距**(调研结论): + +1. **研究是一条"单行道"**:ResearchSpec → 取数 → 因子 → TopN 回测 一次性跑完; + v2 强调的 Factor Research / **Composite Factor / Selection / Signal / Portfolio 分层**尚未拆分, + "选股"只是回测器内部逻辑,无法单独回答 v2 §8 的两个问题: + 「2023-08-15 为什么选这只股票」「2026-09-08 当前有哪些股票满足策略」。 +2. **因子定义 / 策略等研究元数据未入库**:DB 只有 8 张表 + (stock / stock_daily / adjust_factor / financial_indicator / trading_calendar / sync_log / job / experiment), + v2 §8 期望的 factor_definition / factor_test / factor_composite / universe / selection_rule / + signal_rule / strategy / portfolio 等全缺;factor_test 与回测结果只以 Experiment 的 result_json 快照存在。 +3. **口径与数据完整性风险待修**:研究链路不消费复权因子(stock_daily 混合 tushare 不复权与新浪前复权行); + universe 用"当前名称含 ST"近似、无历史成分/停牌;Job.stage 从未赋值、SSE 是查库轮询。 +4. **前端/Agent 待做实**:Selection/Signal 页面不存在;因子组合页无持久化实体;回测结果字段大量未渲染; + Agent 工具仅 6 个,v2 §24 期望的 screen_stocks / explain_selection / generate_signals / + create_strategy / create_composite_factor 等依赖尚未存在的引擎。 + +**阶段命名延续 ROADMAP**:M6 研究层落地 → M7 引擎分层 → M8 平台化与 Agent 增强。 +数据库专项单独记 M-DB。 + +--- + +## 1. M-DB:数据库 MySQL 迁移(已完成,本文件记录收尾项) + +**已交付(2026-09)**: + +- MySQL(192.168.1.10:3306,MariaDB 10.11,库 `qlib`,utf8mb4)建 8 张业务表 + alembic_version, + 全部经 `alembic upgrade head`(迁移 e4d18825…53113c80…91c4e27a…d3f6c9a2),结构由既有 ORM 模型保证。 +- `scripts/migrate_sqlite_to_mysql.py`:一次性工具 —— sqlite 直连 + pymysql chunk 多值 INSERT + + keyset 分页续传 + 幂等(已迁则跳过)+ `--verify-only` 逐表 COUNT 与抽样比对;支持 `--only <表>` 并行大表。 +- 配置:`config.yaml → database.mysql`(host/port/db/user/charset 明文),密码经 `.env` 的 + `MYSQL_PASSWORD`(AGENT.md §33:密钥不进 config/git);URL 优先级 + `DATABASE_URL 环境变量 > config.yaml database.mysql > sqlite:///./data/quant.db 兜底`; + 实现集中在 `backend/app/core/config.py::_build_mysql_url`,业务层零改动(DAO/Repository 隔离兑现)。 +- 测试隔离:`backend/tests/conftest.py` 强制每进程 `/tmp/qlib-pytest-.db` SQLite(测试绝不触 MySQL)。 +- 驱动:`pymysql>=1.1` 已入 `backend/pyproject.toml`。 + +**遗留收尾(小任务,可随时做)**: + +| 项 | 说明 | 建议 | +|---|---|---| +| `job/experiment.result_json` 用 MySQL `TEXT`(≤65,535B) | SQLite TEXT 无上限、测试测不出 | 日级回测结果变长后改 `MEDIUMTEXT`(新 Alembic 迁移 + Model 类型 variant) | +| Repository 批量 upsert 在 MySQL 的压测 | `tuple_.in_()` 行值、`add_all` 单事务无分块 | 全市场日线增量同步跑一次,观察锁等待/包大小 | +| MySQL 路径无自动化测试 | 测试全 SQLite 方言 | 视需要加一个「连 MySQL 的集成冒烟」开关(默认关,显式 env 开启) | +| `scripts/migrate_sqlite_to_mysql.py` 属一次性工具 | 保留供重建目标库 | 文档标注不可再对生产执行(除 --verify-only) | +| 前端/文档残留 "SQLite" 文案 | app-shell / page.tsx / sync.py docstring | 随各页面改动顺手清理 | + +--- + +## 2. 下阶段总路线 + +```text +M6 研究层落地 因子元数据入库 + 复合因子模块 + 口径修复 + ↓ +M7 引擎分层 Selection Engine → Signal Engine → Portfolio Engine + ↓ +M8 平台化 Strategy 模型 + /api/strategies + Agent 增强 + 页面做实 +``` + +设计红线(贯穿):v2 §25「历史回测与当前选股用同一套 Engine」、§26 Experiment 可复现、 +AGENT.md「简单优先 / 每阶段可运行 / 一个功能一个 commit / 先 Domain 后 UI / 带测试」。 + +--- + +## 3. M6:研究层落地(因子目录、复合因子、口径) + +> 目标:把"因子"从代码注册表变成**可入库、可版本化、可组合**的研究资产,并修掉混合口径风险。 +> 本阶段只做研究和评估能力,不引入选股/信号新概念 —— 避免一次改动跨太多层。 + +### 3.1 因子定义入库 + 版本化目录 + +- Domain:`FactorDefinition`(name/description/formula/input_data/frequency/lookback/direction/ + normalization/neutralization/availability_rule/version/requires…);`FactorDefinitionRepository`(Protocol)。 +- Infra:Model `factor_definition` → Alembic → Repo;注册表 seeding:把 `quant/factors.py` 现有 9 个 + 内置因子的元数据灌库(一次性 seed 脚本或 `sync factors` CLI 子命令),代码仅保留计算函数映射 + (name → compute)。 +- API:`GET /api/factors` 改读库;`POST/PUT /api/factors`(管理员自定义因子元数据,计算待 M6.3 表达式引擎)。 +- 验收:迁移测试通过;`/api/factors` 展示入库元数据;未知因子抛 `FactorError`; + 现有因子研究 Job 结果与改动前一致(回归)。 + +### 3.2 Composite Factor Engine 模块化 + +- 现状:`composite_score` 内联在 local_engine(截面 zscore × 方向 × 权重 求和)。 +- 目标:抽出独立模块 `quant/composite.py`:`CompositeSpec{components[{name,weight,direction}], method}` + → 输出 score 面板(先固定权重 + zscore;Rank/Z/IC 加权留接口,不提前实现)。 +- Model 表 `factor_composite(+factor_composite_component)` 落库(供保存/复用)。 +- 回测/因子测试改调同一 `composite_score` 函数。 +- 验收:与现有默认 spec 的回测结果**逐项一致**的回归测试;API `GET/POST /api/composites` CRUD。 + +### 3.3 研究读取口径修复(横切前置,建议最先做) + +- 决策:研究行情统一为**不复权 close 为主口径**,凡需除权处理处显式声明; + 或在 Repository 读路径按 `source/adjust` 过滤/提供 qfq 查询(利用 adjust_factor 前复权)。 +- ResearchSpec 增加 `price_adjustment` 字段并在 API/结果 config_snapshot 显式记录口径。 +- 验收:同一只含新浪补缺行的股票,回测口径一致且 config_snapshot 可溯源; + 文档声明口径;对现有结果差异给出解释(如有)。 + +### 3.4 因子测试结果结构化(可选,如时间紧可并入 M8) + +- Model `factor_test`(结果结构化落库,替代只存 result_json);factor_test 多因子 spec 不再静默只测第一个。 + +--- + +## 4. M7:引擎分层(Selection → Signal → Portfolio) + +> 目标:把回测器里的"选股/买卖/仓位"逻辑拆成 v2 定义的独立引擎, +> 让**当前选股与历史回测共用同一套代码**(v2 §25 一致性红线)。 + +### 4.1 Selection Engine(先做) + +- Domain:`SelectionResult{as_of_date, symbol, rank, score, factor_values, filter_status, selection_reason}` + 与用例 `select(spec, as_of)`(可对任意历史/当前日期执行)。 +- 三种模式中先落地 A(条件选股:ROE/PE/涨跌幅等条件表达式)+ B(因子评分 TopN/Top%); + 模式 C(模型预测)留待 Qlib 模型链路(M8.3)。 +- `TopKBacktestRunner._rebalance` 改为**消费 Selection 结果**(同一引擎)。 +- Model:`selection_result / selection_snapshot`(as_of、spec 快照、结果)。API:`POST /api/selections` + (提交即算)与 `GET /api/selections/{id}`。 +- 验收(v2 §27):`select(as_of=历史日)` 与该日回测调仓选股**逐 symbol 一致**的测试; + API 可查"任意日 TopN 与理由";SelectionResult 成为前后端标准化 DTO。 + +### 4.2 Signal Engine(规则型) + +- Domain:`SignalRule / SignalEvent{signal_date, symbol, signal_type(BUY/SELL/HOLD/WATCH), + trigger_reason, score, price}`;规则引擎执行"分数阈值 + 技术条件 + 市场状态"组合。 +- Model:`signal_rule / signal_event`;回测与选股都能产事件并落库。 +- API:`POST /api/signals`、`GET /api/signals/history`。 +- 验收:能回答「某策略 2023-08-15 为什么对这只股票给 BUY/SELL」;与回测 signal_history 同引擎(一致性测试)。 + +### 4.3 Portfolio Engine + +- 把回测器 `_rebalance` 的资金/仓位逻辑抽为 portfolio 模块:weighting(等权起步)、 + 单股/行业上限、现金管理;约束默认关闭并在结果 unimplemented 如实标注。 +- Model:`portfolio / portfolio_position`(可选,先服务回测即可,不提前建表)。 +- 验收:默认配置下回测数值与现实现一致;新约束开关正确进入 config_snapshot/unimplemented。 + +### 4.4 Universe 规则化(横切,可与 4.1 并行) + +- Model `universe / universe_rule` + 规则执行器(把 filter_stocks 的 exclude_st/min_listing_days/ + market 规则化入库并补 exclude_suspended 的显式处理或 unimplemented); + 预留 `index_constituent_history`(历史成分,防幸存者偏差)。 +- 验收:universe 可持久化、被 Spec 引用后结果与现有一致;文档声明 ST 快照近似。 + +--- + +## 5. M8:平台化与增强(Strategy / Agent / Web / 基础设施) + +### 5.1 Strategy 模型与 /api/strategies + +- Domain:Strategy 聚合 universe + factors/composite + selection + signal + portfolio + rebalance; + ResearchSpec 可由 strategy 展开。Model `strategy(+子表或 JSON 明细)`;API CRUD。 +- Experiment 归档时记录 strategy_version;Agent 增加 `create_strategy` Tool(受控白名单)。 +- 验收:Agent「建立策略 X」→ 策略入库 → 回测 → Experiment 带 strategy_version 可复跑一致。 + +### 5.2 Web 页面做实(前端主线,与 M6–M8 后端里程碑并行推进) + +- 优先:**回测页做实**(零新后端 API):渲染 trades 明细/分年度收益/持仓演化/成本参数表单/config_snapshot, + `lib/types.ts` 补全 Trade entry/exit_price、FactorTestReport.factor_name 等缺失字段, + Experiment 类型收编进 lib/types.ts(result 改联合类型)。 +- **因子研究做实**:IC 时序折线、因子横向对比(数据来自既有 DTO 扩展)。 +- **Selection 页(新路由)**:M7.1 后端就绪后,选当前/历史日 → SelectionResult 表 + 一键跳回测。 +- **Signal 页(新路由)**:M7.2 后端就绪后,信号时间线与理由、风险标志。 +- Job 进度 UI 切换 SSE(需先给执行器补 stage 上报,见 §6)。 +- 每页验收:tsc --noEmit 通过、无未渲染的标准字段、无 SQLite 字样残留。 + +### 5.3 Qlib 模型链路(模型选股 Selection 模式 C 的前置,按需) + +- `qlib_adapter/feature.py` Alpha158 特征 + `model.py` LightGBM walk-forward(seed 固定); + 输出预测分 → Selection 模式 C。作为可选增强,不阻塞 M6–M8 主干。 + +### 5.4 AI Agent 工具补齐(依赖 M6/M7/M8.1 引擎) + +- 在引擎就绪后逐个补齐 v2 §24 工具:`inspect_factor / create_composite_factor / screen_stocks / + explain_selection / generate_signals / create_strategy / get_backtest_result / create_experiment`; + 每个工具经白名单 + Job/Experiment 链路(不直接写库)。 +- 前端加一个最小 AI 对话入口(后端 /api/agent/chat 已有)—— 可与 M8.2 任意页面里程碑合并。 + +--- + +## 6. 基础设施专项(含本机 Redis 的定位) + +**本机已探测:Redis 服务在 127.0.0.1:6379 正常响应(无 redis-cli,连接经 socket 验证)。** + +v2 §22 与 AGENT.md §19/§38 的原则是"第一版不引复杂队列;复杂后再引入 Redis + Celery/RQ"。 +据此把 Redis 放入**明确的触发点**而非一开始就用: + +| 触发场景 | Redis 用途 | 何时启用 | +|---|---|---| +| Job 并发/排队:subprocess 双并发上限开始频繁打满、丢任务需可恢复队列 | RQ/普通队列替代 FastAPI BackgroundTasks 调度 | M8 之后 / 出现排队丢失问题 | +| Job 细粒度 stage + SSE 长连:executor 每阶段写 stage 后广播 | Redis pub/sub 做 SSE 事件总线(替代 0.4s 查库轮询) | M8.2 前端切 SSE 时(可先行) | +| 行情/因子查询热点:全市场研究重复预热 | 因子结果 / 最新交易日缓存(key 带 as_of 失效) | 研究链路稳定后按 profile 决定 | + +近期只需做一件准备工作:`config.yaml` 预留 `redis.url_env: "REDIS_URL"` + 可选 +`redis.url: "redis://127.0.0.1:6379/0"` 配置段,代码不改;真正启用时再落基础设施代码。 + +**其余基础设施待办**(低优先):Job `cancelled` 取消 API 与 `/api/jobs` 列表端点;executor 增加 stage +上报(data_loading/factor_calculation/backtesting…)——这是前端做 SSE 进度条的前提。 + +--- + +## 7. 建议的执行顺序(前 6 个 commit 序列) + +按 AGENT.md「最小修改、每阶段可跑、一功能一 commit」,给 M6 起步的推荐顺序: + +1. **M6.3 口径修复**(横切前置,改动小收益大):Repository 读路径按 source/adjust 过滤 + + ResearchSpec 口径字段 + 文档。验收:混合行场景回测口径一致。 +2. **M6.1 因子定义入库**:Model+迁移+seed+`GET /api/factors` 读库。验收:迁移测试 + 回归。 +3. **M6.2 Composite Engine 模块化**:`quant/composite.py` + 回测改调 + `factor_composite` 表 CRUD。 + 验收:默认 spec 回测结果逐项一致的回归测试。 +4. **M7.4 Universe 规则化**:`universe/universe_rule` 表 + 执行器(结果与现有一致)。可与 2/3 并行。 +5. **M7.1 Selection Engine**:`select(spec, as_of)` + TopK 回测改消费 Selection + + `selection_result/snapshot` 表 + API。验收:v2 §27 历史日一致测试。 +6. **M7.2 Signal Engine**:规则 + `signal_event` 落库 + API(回测/选股同引擎)。 + +之后进入 M8(Strategy 模型 → Web 页面做实 → Agent 工具补齐)与 §6 基础设施专项。 + +每步都遵循:Domain → Repository Protocol → Infra(Model→Migration→Repo) → Service → API → 前端 → +测试(相关模块)→ 更新文档;新增表一律 Model→Alembic→测试。 + +--- + +## 8. 风险清单(开发中持续检查) + +- **历史一致性**:Selection/Backtest 拆分后默认结果必须与拆分前一致(用回归测试锁住,别让重构悄悄改策略语义)。 +- **未来函数 / 幸存者偏差**:universe 的 ST/行业/成分历史口径补齐前,回测文档须明示近似; + 财务数据始终按 announce_date 可见(已有表键支持)。 +- **MySQL 行为差异**:TEXT 上限、批量 upsert 锁等待、pymysql 流式读取 —— 见 §1 遗留收尾表。 +- **不要提前堆量**:M6 阶段不引入模型选股/ML;M7 不做多因子合成优化;M8 的 Qlib 模型链路按需推进。 +- **前端契约**:SelectionResult/SignalResult 先定 DTO(后端 entity)再画页面,避免前后端口径漂移。 diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 9e597b4..4348707 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -2,7 +2,9 @@ > 依据:[AGENT.md](./AGENT.md)(开发约束)、[docs/ARCHITECTURE.md](./ARCHITECTURE.md)(架构) > 引擎:Qlib 只做计算引擎 · 数据源:Tushare 第一、Sina 备用 · 数据库:SQLite 起步、SQLAlchemy 保证 MySQL 可切换 -> 现处:**M0 已完成(工程骨架 + 可运行后端)**,下一步进入 **Phase 1 数据层** +> 现处:**M5 已完成(AI Agent Phase 5)**;**M6 起的下一阶段见 [docs/DEV_PLAN_v2.md](./DEV_PLAN_v2.md)** +> (架构 v2 落地:因子元数据入库 → 复合因子 → Selection/Signal/Portfolio 引擎分层 → Strategy 平台化) +> 2026-09 增补:数据库已切 MySQL(见 M-DB 行),本文件 M0–M5 为历史记录,新计划以 DEV_PLAN_v2 为准。 --- @@ -15,7 +17,8 @@ | M2 ✅ | Phase 2 研究引擎(Spec→因子→评估→低频回测→标准结果;Qlib 桥接占位见 §2 备注) | commit e9f59d3 · 60 tests | | M3 ✅ | Phase 3 Web(业务 API + Next.js 前端闭环) | commit 92627f5 · 68 tests | | M4 ✅ | Phase 4 Experiment 归档 + Job/SSE 异步(一键复跑) | commit 0ea229d · 73 tests | -| M5 ✅ | Phase 5 AI Agent(受控 Tool 白名单 + LLM 编排;Key 配置见 .env) | commit(本轮)· 79 tests | +| M5 ✅ | Phase 5 AI Agent(受控 Tool 白名单 + LLM 编排;Key 配置见 .env) | commit(M5)· 79 tests | +| M-DB ✅ | 数据库 SQLite → MySQL(config.yaml database.mysql 配置化 + 迁移脚本 + 一致性校验) | 2026-09 · 见 DEV_PLAN_v2 §1 | 每阶段结束时同步更新:README / AGENT.md 相关清单 / 文档;**禁止跨阶段提前堆量**(AGENT.md §38)。 diff --git a/docs/USAGE.md b/docs/USAGE.md index f983295..199486a 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -64,7 +64,8 @@ cp .env.example .env |---|---|---| | `TUSHARE_TOKEN` | 同步数据时必填 | Tushare Pro token | | `LLM_API_KEY` | 使用 Agent 时必填 | 大模型 API Key(URL/模型名在 config.yaml) | -| `DATABASE_URL` | 否 | 留空使用 SQLite `<项目根>/data/quant.db` | +| `DATABASE_URL` | 否 | 默认库 = `config.yaml → database.mysql`(MySQL `192.168.1.10/qlib`);设此项可覆盖(如切回 SQLite) | +| `MYSQL_PASSWORD` | 使用 MySQL 默认库时必填 | MySQL 密码(`config.yaml database.mysql.password_env` 引用;host/db/user 在 config.yaml) | | `APP_SECRET_KEY` | 否 | 应用密钥(未接登录,可暂不改) | > 约定:**密钥只放 `.env`**;URL、模型名等可配置项放 `config.yaml`。`.env` 已被 gitignore,严禁提交。 @@ -76,7 +77,12 @@ cp .env.example .env ```yaml app: {name, version, debug, secret_key_env} api: {prefix: "/api"} -database: {url_env: "DATABASE_URL", echo: false, migrations_dir: ...} +database: + url_env: "DATABASE_URL" + migrations_dir: ... + mysql: {enabled: true, host: "192.168.1.10", port: 3306, + db: "qlib", user: "qlib", password_env: "MYSQL_PASSWORD", charset: "utf8mb4"} +# URL 优先级:DATABASE_URL 环境变量 > database.mysql 组装 > sqlite:///./data/quant.db 兜底 data_source: {primary: "tushare", fallback: "sina", tushare_token_env: "TUSHARE_TOKEN"} storage: {parquet_dir: "data/parquet", qlib_dir: "data/qlib", ...} # 相对项目根 agent: @@ -94,12 +100,17 @@ agent: ```bash cd backend -uv run alembic upgrade head # 建表(首次会自动建 data/quant.db) +uv run alembic upgrade head # 在 config 指定库上建表(默认 MySQL qlib;SQLite 兜底库为 data/quant.db) # 开发中改 Model 后: uv run alembic revision --autogenerate -m "desc" uv run alembic upgrade head +# 对指定库执行(不随默认配置): +DATABASE_URL='mysql+pymysql://user:pass@host/db' uv run alembic upgrade head ``` +> 2026-09 已把数据从 SQLite(`data/quant.db`)全量迁移至 MySQL(`192.168.1.10:3306/qlib`), +> 迁移工具与校验见 `scripts/migrate_sqlite_to_mysql.py`(`--verify-only` 可复查一致性)。 + --- ## 4. 数据同步与导出 @@ -320,9 +331,12 @@ pnpm run build - **前端连不上后端 / NetworkError**:前端默认经 Next **同源代理**访问 `/api/*` (next.config.ts rewrites → `127.0.0.1:8000`,可用 `BACKEND_API_URL` 覆盖), 任意 IP 访问 `:3000` 都不需要 CORS 或硬编码后端地址;后端 CORS 开发期为 `*`。 - 若 API 返回 500 且日志出现 `database is locked`,多半是正在跑全市场数据同步 - (长写事务),同步结束后自动恢复(引擎已加 busy_timeout 等待)。 -- **数据库被改动想重置**:删除 `data/quant.db` 后 `uv run alembic upgrade head` 重建 - 表结构(行情需重新同步)。 -- **想切换 MySQL**:`.env` 设 `DATABASE_URL=mysql+pymysql://user:pass@host/db`, - 业务层无需改动(Repository 已隔离)。 + 若使用 SQLite 兜底库且日志出现 `database is locked`,多半是正在跑全市场数据 + 同步(长写事务),同步结束后自动恢复(SQLite 连接已加 busy_timeout); + 默认 MySQL 库无此问题。 +- **数据库被改动想重置**:默认 MySQL(qlib@192.168.1.10)时在远端重建后 + `uv run alembic upgrade head`(行情需重新同步或从备份恢复);若切回 SQLite 兜底库则删除 + `data/quant.db` 后重建。 +- **默认库已是 MySQL**(config.yaml `database.mysql`,密码在 `.env` 的 `MYSQL_PASSWORD`); + **想临时切回 SQLite**:`.env` 设 `DATABASE_URL=sqlite:///./data/quant.db`。 + 业务层均无需改动(Repository / SQLAlchemy 已隔离方言差异)。