1293 lines
19 KiB
Markdown
1293 lines
19 KiB
Markdown
# AGENT.md
|
||
|
||
## AI Agent 开发约束
|
||
|
||
本文件是本项目所有 AI Coding Agent(Claude Code、DSH、REASONIX等)的开发约束。
|
||
|
||
**在修改代码前必须阅读本文件和 `ARCHITECTURE.md`。**
|
||
|
||
**AI 回答必须使用中文**
|
||
|
||
---
|
||
|
||
# 0. 网络下载与代理规则
|
||
|
||
当网络下载(git clone、pip / uv 安装、wget、数据下载、Docker pull 等)出现**困难或超时**时,
|
||
统一使用本机局域网 HTTP 代理:
|
||
|
||
```text
|
||
192.168.1.160:3128
|
||
```
|
||
|
||
示例(仅在下载失败 / 超时时启用,不把代理写入代码或 Git):
|
||
|
||
```bash
|
||
# git clone(仅对 GitHub 等外网 https 目标使用)
|
||
git -c http.proxy=http://192.168.1.160:3128 -c https.proxy=http://192.168.1.160:3128 clone <url>
|
||
|
||
# pip / uv 安装
|
||
export http_proxy=http://192.168.1.160:3128
|
||
export https_proxy=http://192.168.1.160:3128
|
||
uv pip install <pkg>
|
||
|
||
# wget / curl
|
||
wget -e use_proxy=yes -e http_proxy=http://192.168.1.160:3128 <url>
|
||
```
|
||
|
||
注意事项:
|
||
|
||
- 代理只用于**外网**下载;内网资源(如 ssh git@192.168.1.10、局域网服务)不要走代理。
|
||
- 代理 IP 属于局域网配置,不写入 `.env` / `config.yaml` / 任何提交进 Git 的文件。
|
||
- 直接下载成功时不要多此一举走代理。
|
||
|
||
### 0.1 数据库目标:只允许本机 MariaDB(硬约束)
|
||
|
||
- 数据库一律指向**本机 MariaDB 10.11**(`127.0.0.1:3306/qlib`)。
|
||
- **禁止把 `192.168.1.10` 作为数据库目标**(该地址在本文档中仅作外网代理白名单/SSH 提及,
|
||
不是数据库目标)。远端旧库只作为历史数据来源,已不再写入。
|
||
- 该约束不是「靠自觉」:`app/core/config.py::assert_db_target_allowed` 在 `get_settings()`
|
||
解析出 URL 后立刻校验,命中禁用主机**直接抛错**(应用起不来),不允许「起得来但连错库」。
|
||
禁用列表默认 `192.168.1.10`,可用环境变量 `QLIB_FORBIDDEN_DB_HOSTS` 覆盖(空串=不限制)。
|
||
- 理由:连错库属于最危险的静默错误 —— 不报错、界面正常,回测/归档/策略却写到了另一台机器上。
|
||
- 新增脚本/文档/示例配置时,DB 主机一律写 `127.0.0.1`,禁止出现远端库地址作为**默认值**。
|
||
|
||
---
|
||
|
||
# 1. 项目定位
|
||
|
||
这是一个:
|
||
|
||
- A股量化研究平台
|
||
- 个人开发项目
|
||
- 以选股、因子研究、回测为核心
|
||
- 以低频/中低频交易研究为目标
|
||
- Qlib 为量化研究引擎
|
||
- Tushare 为首选数据源
|
||
- 新浪财经为备用数据源
|
||
- SQLite 为当前数据库
|
||
- MySQL 为未来数据库
|
||
- React / Next.js 为前端
|
||
- FastAPI 为后端
|
||
- AI Agent 为后期研究助手
|
||
|
||
不要把项目开发成高频交易系统。
|
||
|
||
---
|
||
|
||
# 2. 总体开发原则
|
||
|
||
必须遵守:
|
||
|
||
```text
|
||
简单优先
|
||
模块解耦
|
||
接口稳定
|
||
数据可追溯
|
||
实验可复现
|
||
禁止未来函数
|
||
AI 可调用
|
||
SQLite → MySQL 可迁移
|
||
API KEY, 账号密码等敏感信息写入根目录.env文件
|
||
可配置项统一写入根目录config.yaml 文件
|
||
```
|
||
|
||
禁止为了“看起来专业”而过度微服务化。
|
||
|
||
---
|
||
|
||
# 3. 修改代码前
|
||
|
||
AI Agent 必须先:
|
||
|
||
1. 阅读 `ARCHITECTURE.md`
|
||
2. 阅读相关模块代码
|
||
3. 找到现有接口
|
||
4. 判断是否可以复用
|
||
5. 最小范围修改
|
||
6. 修改后运行测试
|
||
|
||
禁止看到需求就直接大量重构。
|
||
|
||
---
|
||
|
||
# 4. 架构边界
|
||
|
||
严格遵守:
|
||
|
||
```text
|
||
Frontend
|
||
↓
|
||
API
|
||
↓
|
||
Application Service
|
||
↓
|
||
Domain
|
||
↓
|
||
Repository / DAO
|
||
↓
|
||
Infrastructure
|
||
```
|
||
|
||
量化:
|
||
|
||
```text
|
||
Application Service
|
||
↓
|
||
Quant Service
|
||
↓
|
||
Qlib Adapter
|
||
↓
|
||
Qlib
|
||
```
|
||
|
||
禁止:
|
||
|
||
```text
|
||
Frontend → Qlib
|
||
Frontend → Database
|
||
Agent → Database
|
||
Agent → Qlib internal API
|
||
Service → sqlite3
|
||
```
|
||
|
||
---
|
||
|
||
# 5. 数据源约束
|
||
|
||
## 5.1 Tushare 是第一数据源
|
||
|
||
所有已有 Tushare 能提供的数据,默认必须优先使用 Tushare。
|
||
|
||
禁止无理由改成新浪。
|
||
|
||
## 5.2 新浪是备用数据源
|
||
|
||
新浪财经只能作为:
|
||
|
||
- Tushare 缺失数据
|
||
- Tushare 暂时不可用
|
||
- 数据交叉验证
|
||
- 特定行情数据补充
|
||
|
||
必须通过:
|
||
|
||
```text
|
||
SinaAdapter
|
||
```
|
||
|
||
接入。
|
||
|
||
业务代码不得直接 import 新浪 API 实现。
|
||
|
||
---
|
||
|
||
# 6. Data Provider 接口
|
||
|
||
业务层只能依赖抽象接口:
|
||
|
||
```python
|
||
class MarketDataProvider(Protocol):
|
||
...
|
||
```
|
||
|
||
例如:
|
||
|
||
```python
|
||
provider.get_daily(...)
|
||
provider.get_stock_basic(...)
|
||
provider.get_financial(...)
|
||
```
|
||
|
||
不能:
|
||
|
||
```python
|
||
from tushare import ...
|
||
```
|
||
|
||
散落在业务代码中。
|
||
|
||
正确:
|
||
|
||
```text
|
||
MarketDataProvider
|
||
│
|
||
├── TushareProvider
|
||
└── SinaProvider
|
||
```
|
||
|
||
---
|
||
|
||
# 7. 数据源 Failover
|
||
|
||
推荐:
|
||
|
||
```text
|
||
Tushare
|
||
│
|
||
├── success → use
|
||
│
|
||
└── failure / missing
|
||
↓
|
||
Sina
|
||
```
|
||
|
||
但必须记录:
|
||
|
||
```text
|
||
source
|
||
request_time
|
||
success
|
||
failure_reason
|
||
row_count
|
||
data_date
|
||
```
|
||
|
||
不能静默切换导致数据来源不可追踪。
|
||
|
||
---
|
||
|
||
# 8. 数据一致性
|
||
|
||
任何行情数据必须至少考虑:
|
||
|
||
```text
|
||
symbol
|
||
trade_date
|
||
open
|
||
high
|
||
low
|
||
close
|
||
volume
|
||
amount
|
||
```
|
||
|
||
需要复权时必须使用明确的复权因子。
|
||
|
||
禁止在代码中偷偷改变:
|
||
|
||
```text
|
||
前复权
|
||
后复权
|
||
不复权
|
||
```
|
||
|
||
必须在 API / Research Specification 中明确。
|
||
|
||
---
|
||
|
||
# 9. 未来函数是最高优先级风险
|
||
|
||
严禁:
|
||
|
||
```text
|
||
回测日期 T
|
||
使用 T 之后才公布的数据
|
||
```
|
||
|
||
财务数据必须区分:
|
||
|
||
```text
|
||
report_date
|
||
announce_date
|
||
```
|
||
|
||
研究查询必须支持:
|
||
|
||
```python
|
||
as_of_date
|
||
```
|
||
|
||
任何新增财务数据功能,都必须回答:
|
||
|
||
> 在回测当天,这条数据是否已经公开?
|
||
|
||
如果无法回答,不得用于回测。
|
||
|
||
---
|
||
|
||
# 10. DAO / Repository 约束
|
||
|
||
业务层禁止直接操作:
|
||
|
||
```python
|
||
sqlite3
|
||
SQLAlchemy Session
|
||
SQL
|
||
```
|
||
|
||
业务层只能调用:
|
||
|
||
```text
|
||
Repository Interface
|
||
```
|
||
|
||
例如:
|
||
|
||
```python
|
||
stock_repo.get_by_symbol(...)
|
||
factor_repo.list(...)
|
||
experiment_repo.save(...)
|
||
```
|
||
|
||
---
|
||
|
||
# 11. SQLite → MySQL 兼容
|
||
|
||
当前使用 SQLite。
|
||
|
||
但是:
|
||
|
||
> 任何业务代码都不能假设数据库是 SQLite。
|
||
|
||
禁止:
|
||
|
||
```python
|
||
SELECT * FROM sqlite_master
|
||
```
|
||
|
||
禁止 SQLite 专有 SQL。
|
||
|
||
禁止:
|
||
|
||
```python
|
||
sqlite3.connect()
|
||
```
|
||
|
||
出现在 Service / Domain 层。
|
||
|
||
数据库访问统一:
|
||
|
||
```text
|
||
SQLAlchemy
|
||
Repository
|
||
Alembic
|
||
```
|
||
|
||
未来切换 MySQL 时,业务层不应该修改。
|
||
|
||
---
|
||
|
||
# 12. Migration
|
||
|
||
数据库结构必须使用:
|
||
|
||
```text
|
||
Alembic
|
||
```
|
||
|
||
禁止手工修改生产数据库结构作为正式方案。
|
||
|
||
新增字段必须:
|
||
|
||
```text
|
||
Model
|
||
↓
|
||
Migration
|
||
↓
|
||
Test
|
||
```
|
||
|
||
---
|
||
|
||
# 13. Parquet 使用原则
|
||
|
||
大量历史时序数据优先:
|
||
|
||
```text
|
||
Parquet
|
||
```
|
||
|
||
SQLite 主要存:
|
||
|
||
- metadata
|
||
- configuration
|
||
- experiment
|
||
- job
|
||
- factor definition
|
||
- strategy
|
||
- 索引/小规模业务数据
|
||
|
||
不要把几十年、全市场、高频明细全部塞入 SQLite。
|
||
|
||
---
|
||
|
||
# 14. Qlib 使用原则
|
||
|
||
Qlib 必须被 Adapter 封装。
|
||
|
||
例如:
|
||
|
||
```text
|
||
QlibDatasetAdapter
|
||
QlibModelAdapter
|
||
QlibBacktestAdapter
|
||
```
|
||
|
||
业务层不要直接依赖 Qlib 内部实现。
|
||
|
||
禁止项目代码到处出现:
|
||
|
||
```python
|
||
import qlib
|
||
```
|
||
|
||
尽量集中在:
|
||
|
||
```text
|
||
quant/qlib_adapter/
|
||
```
|
||
|
||
---
|
||
|
||
# 15. 不要修改 Qlib 源码
|
||
|
||
默认禁止:
|
||
|
||
```text
|
||
修改 site-packages/qlib
|
||
```
|
||
|
||
如果 Qlib 功能不满足要求:
|
||
|
||
优先:
|
||
|
||
```text
|
||
Adapter
|
||
Wrapper
|
||
Extension
|
||
```
|
||
|
||
只有确实无法实现时,才考虑 fork,并必须记录原因。
|
||
|
||
---
|
||
|
||
# 16. Research Specification
|
||
|
||
前端、Agent、后端统一通过:
|
||
|
||
```text
|
||
Research Specification
|
||
```
|
||
|
||
描述研究任务。
|
||
|
||
不要让前端直接构造 Qlib YAML。
|
||
|
||
不要让 Agent 直接生成任意 Qlib 配置。
|
||
|
||
正确流程:
|
||
|
||
```text
|
||
User / Agent
|
||
↓
|
||
Research Specification
|
||
↓
|
||
Schema Validation
|
||
↓
|
||
Strategy Builder
|
||
↓
|
||
Qlib Adapter
|
||
```
|
||
|
||
---
|
||
|
||
# 17. API 设计
|
||
|
||
API 必须面向业务对象:
|
||
|
||
```text
|
||
/api/stocks
|
||
/api/universes
|
||
/api/factors
|
||
/api/factor-tests
|
||
/api/strategies
|
||
/api/backtests
|
||
/api/experiments
|
||
/api/jobs
|
||
/api/agent
|
||
```
|
||
|
||
不要设计成:
|
||
|
||
```text
|
||
/api/qlib/xxx
|
||
```
|
||
|
||
除非是内部 Adapter API。
|
||
|
||
---
|
||
|
||
# 18. API 输入输出
|
||
|
||
API 输入必须使用:
|
||
|
||
```text
|
||
Pydantic Schema
|
||
```
|
||
|
||
不要直接把 SQLAlchemy Model 暴露给前端。
|
||
|
||
例如:
|
||
|
||
```text
|
||
Request DTO
|
||
↓
|
||
Application Service
|
||
↓
|
||
Domain
|
||
↓
|
||
Repository
|
||
```
|
||
|
||
输出:
|
||
|
||
```text
|
||
Domain
|
||
↓
|
||
Response DTO
|
||
↓
|
||
Frontend
|
||
```
|
||
|
||
---
|
||
|
||
# 19. 异步任务
|
||
|
||
以下任务必须考虑异步执行:
|
||
|
||
- 大量数据下载
|
||
- 因子计算
|
||
- 模型训练
|
||
- 回测
|
||
- 大规模参数搜索
|
||
- AI Research
|
||
|
||
API 不应该长时间阻塞 HTTP 请求。
|
||
|
||
返回:
|
||
|
||
```json
|
||
{
|
||
"job_id": "BT-001",
|
||
"status": "queued"
|
||
}
|
||
```
|
||
|
||
前端通过 SSE / WebSocket 获取进度。
|
||
|
||
---
|
||
|
||
# 20. Job 状态
|
||
|
||
统一状态:
|
||
|
||
```text
|
||
queued
|
||
running
|
||
success
|
||
failed
|
||
cancelled
|
||
```
|
||
|
||
可选阶段:
|
||
|
||
```text
|
||
data_loading
|
||
feature_calculation
|
||
model_training
|
||
prediction
|
||
backtesting
|
||
analysis
|
||
saving_result
|
||
```
|
||
|
||
---
|
||
|
||
# 21. Experiment 必须可复现
|
||
|
||
每次研究必须保存:
|
||
|
||
```text
|
||
experiment_id
|
||
data_version
|
||
code_version
|
||
strategy_version
|
||
universe
|
||
factors
|
||
model
|
||
parameters
|
||
backtest_config
|
||
start_date
|
||
end_date
|
||
result
|
||
```
|
||
|
||
最好记录:
|
||
|
||
```text
|
||
git commit
|
||
```
|
||
|
||
目标:
|
||
|
||
> 任何一个历史实验,都应该尽可能可以重新运行。
|
||
|
||
---
|
||
|
||
# 22. 因子开发约束
|
||
|
||
每个因子必须明确:
|
||
|
||
```text
|
||
name
|
||
description
|
||
formula
|
||
input data
|
||
frequency
|
||
lookback
|
||
direction
|
||
normalization
|
||
neutralization
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
momentum_60
|
||
|
||
定义:
|
||
过去60个交易日收益率
|
||
|
||
频率:
|
||
daily
|
||
|
||
方向:
|
||
higher_is_better
|
||
```
|
||
|
||
禁止创建只有:
|
||
|
||
```python
|
||
factor1()
|
||
factor2()
|
||
```
|
||
|
||
而没有定义和文档的因子。
|
||
|
||
---
|
||
|
||
# 23. 因子研究不能只看收益率
|
||
|
||
新增因子测试至少考虑:
|
||
|
||
```text
|
||
IC
|
||
RankIC
|
||
ICIR
|
||
分层收益
|
||
换手率
|
||
稳定性
|
||
行业暴露
|
||
市值暴露
|
||
不同市场阶段
|
||
```
|
||
|
||
不能因为:
|
||
|
||
```text
|
||
Sharpe > 2
|
||
```
|
||
|
||
就直接认为因子有效。
|
||
|
||
---
|
||
|
||
# 24. 回测约束
|
||
|
||
回测必须考虑:
|
||
|
||
```text
|
||
手续费
|
||
印花税
|
||
滑点
|
||
涨跌停
|
||
停牌
|
||
成交约束
|
||
调仓频率
|
||
```
|
||
|
||
如果某项没有实现,必须在 UI 和结果中明确显示。
|
||
|
||
禁止默认假设:
|
||
|
||
```text
|
||
永远可以买入
|
||
永远可以卖出
|
||
没有交易成本
|
||
```
|
||
|
||
---
|
||
|
||
# 25. 选股策略
|
||
|
||
核心目标是:
|
||
|
||
```text
|
||
低频
|
||
选股
|
||
组合
|
||
回测
|
||
```
|
||
|
||
默认优先:
|
||
|
||
```text
|
||
daily data
|
||
weekly rebalance
|
||
monthly rebalance
|
||
```
|
||
|
||
不要默认引入:
|
||
|
||
```text
|
||
tick
|
||
order book
|
||
HFT
|
||
```
|
||
|
||
除非用户明确要求。
|
||
|
||
---
|
||
|
||
# 26. 前端约束
|
||
|
||
前端必须:
|
||
|
||
```text
|
||
业务导向
|
||
参数清晰
|
||
结果可视化
|
||
```
|
||
|
||
不要直接暴露:
|
||
|
||
```text
|
||
Qlib internal config
|
||
Python object
|
||
SQL
|
||
```
|
||
|
||
用户看到:
|
||
|
||
```text
|
||
股票池
|
||
因子
|
||
模型
|
||
策略
|
||
回测
|
||
```
|
||
|
||
而不是:
|
||
|
||
```text
|
||
DatasetH
|
||
HandlerLP
|
||
SignalRecord
|
||
```
|
||
|
||
---
|
||
|
||
# 27. 前端结果统一
|
||
|
||
回测结果必须标准化。
|
||
|
||
推荐:
|
||
|
||
```text
|
||
summary
|
||
equity_curve
|
||
drawdown
|
||
monthly_returns
|
||
yearly_returns
|
||
positions
|
||
trades
|
||
risk_metrics
|
||
factor_exposure
|
||
```
|
||
|
||
前端组件只依赖这个结构。
|
||
|
||
## 27.1 买卖理由:必须能回答「为什么买 / 为什么卖」,且数字来自引擎
|
||
|
||
用户看回测结果时的第一个问题不是「赚了多少」,而是「**为什么在这里买 / 卖**」。
|
||
因此每个买卖点(**成交的与未成交的都要**)必须带结构化理由
|
||
(`TradeReason{code, text, data}`,见 `backend/app/quant/trade_reasons.py`):
|
||
|
||
- **原因分类是封闭词表**:组合引擎(`combo_engine.py`)与单策略引擎(`local_engine.py`)
|
||
共用同一套 code 与文案构造器,禁止各自手写措辞 —— 否则同一件事会出现两种说法。
|
||
- **`data` 里只能是引擎当时的真实数字**(名次 / 候选数 / 综合分 / 各因子**原始值** /
|
||
持有交易日 / 预算 / 涨停比值…)。前端**只展示不推算**;拿不到名次就写「未给出名次」,
|
||
不拿旧名次或其他日期的数据冒充。
|
||
- **把事实说准**:跌出 TopN ≠ 不在候选池(被股票池/条件过滤)≠ 全量换仓
|
||
(策略每次调仓先清仓,被卖的股票可能仍排在前列)≠ Tmin 保护暂留 ≠ 超 Tmax 强制了结
|
||
≠ 涨停/跌停/停牌/现金不足。宁可为一种情况新增一个 code,也不要套一个语义不符的旧 code。
|
||
- **因子曲线**(`result.factor_curves`)= 当日**持仓按市值加权平均的原始值**
|
||
(不做 z-score、不按方向取反,空仓日不落点、不插值、不用 0 填充),
|
||
界面必须同时写出方向与单位(如「股息率 %,越高越好」),否则读者会误判曲线的含义。
|
||
- **每条曲线都要能新页面放大**(`/charts/{归档id}?s=...`):放大页从**归档**读同一份数据
|
||
(URL 可分享、口径不漂移);没有归档 id 时如实说明「未归档,无法放大」,不给坏链接。
|
||
|
||
## 27.2 长任务反馈:点了立刻有字、看得出在动、出事了能自救
|
||
|
||
用户原话:「点了回测没有任何反馈,不清楚是不是已经开始」。因此跑异步 Job 的页面必须:
|
||
|
||
- 提交**瞬间**就有反馈(提交中 → 排队中),不等到后端返回;
|
||
- 显示**真实**作业号 / 阶段 / 逐秒自增的已用时间(用 `lib/jobs.ts` 的 `useJobRunner`
|
||
+ `components/JobProgress.tsx`,不要各页手写一套);
|
||
- 排队/运行中可**取消**;失败显示后端原文;成功给「打开归档 / 去对比」入口;
|
||
- 反馈条出现时**自动滚入视野**,并带 `role="status" aria-live="polite"`;
|
||
- 同步接口(如 `POST /api/signals`)**没有**作业号与阶段时,如实说明「同步请求、无阶段、
|
||
不可取消」,**禁止**合成假作业号或假进度喂给反馈组件。
|
||
|
||
## 27.3 宽表:两端固定 + 按需提示,绝不"右侧被切掉"
|
||
|
||
用户原话:「experiments 页面右侧内容溢出了」。实测原因不是整页溢出,而是**卡片内的横向滚动**:
|
||
在 macOS 上覆盖式滚动条不滚动就不显示,于是看起来就是内容被切掉、也没有滚动条可拉。因此宽表:
|
||
|
||
- 列数 ≥ 9 或列宽会随数据增长的表(实验列表 `.tbl--wide`、对比表 `.tbl--pin-first`、
|
||
月度收益表 `.tbl--monthly`)必须**固定首列**;操作列在右端时用 `.tbl--wide` 固定右端,
|
||
保证任何窗口宽度下「看的是哪一行」和「能点哪里」都在视野内;
|
||
- 固定列必须有不透明底色(卡片是渐变,取近似纯色)并覆盖 `:hover` / `.row-active`,
|
||
否则中间列会从下面透出来、整行高亮在两端断开;
|
||
- 列宽按**实测单行内容宽度**定(用 CDP 量 `scrollWidth` 与单元格内容宽度),不要凭感觉写,
|
||
并且**不要在 JSX 里写行内 `width`**——它会盖掉 CSS(曾让「操作」列多占 88px,整表放不下);
|
||
- 「可横向滚动」提示只在实测 `scrollWidth > clientWidth` 时出现(`useTableScrollHint`),
|
||
窗口够宽时不显示废话;
|
||
- 门禁:`python3 scripts/verify_ui_alignment.py`(160 项)必须 0 失败,含 375px 与 1500px 两档。
|
||
|
||
---
|
||
|
||
# 28. AI Agent 约束
|
||
|
||
Agent 是:
|
||
|
||
```text
|
||
Research Assistant
|
||
```
|
||
|
||
不是:
|
||
|
||
```text
|
||
System Administrator
|
||
```
|
||
|
||
Agent 默认只能通过 Tool:
|
||
|
||
```text
|
||
search_stock
|
||
get_market_data
|
||
test_factor
|
||
create_strategy
|
||
run_backtest
|
||
get_experiment
|
||
compare_experiments
|
||
```
|
||
|
||
Agent 不得:
|
||
|
||
```text
|
||
直接删除数据库
|
||
直接修改数据库
|
||
直接执行 shell
|
||
修改生产配置
|
||
修改数据源凭证
|
||
修改系统安全配置
|
||
```
|
||
|
||
---
|
||
|
||
# 29. AI Agent 的研究行为
|
||
|
||
Agent 应优先:
|
||
|
||
```text
|
||
提出假设
|
||
↓
|
||
建立实验
|
||
↓
|
||
运行测试
|
||
↓
|
||
分析结果
|
||
↓
|
||
提出下一步
|
||
```
|
||
|
||
而不是:
|
||
|
||
```text
|
||
看到高收益
|
||
↓
|
||
宣布策略成功
|
||
```
|
||
|
||
必须主动考虑:
|
||
|
||
```text
|
||
overfitting
|
||
look-ahead bias
|
||
survivorship bias
|
||
data leakage
|
||
transaction cost
|
||
parameter sensitivity
|
||
out-of-sample
|
||
```
|
||
|
||
---
|
||
|
||
# 30. 禁止数据泄露
|
||
|
||
股票池也必须防止:
|
||
|
||
```text
|
||
未来成分股
|
||
未来行业分类
|
||
未来财务数据
|
||
未来退市信息
|
||
```
|
||
|
||
例如不能用“2026年的沪深300成分股”回测2015年。
|
||
|
||
必须使用历史时点对应的成分数据。
|
||
|
||
---
|
||
|
||
# 31. 测试要求
|
||
|
||
每次修改至少运行相关测试。
|
||
|
||
重点测试:
|
||
|
||
```text
|
||
Data Adapter
|
||
DAO
|
||
Repository
|
||
Research Specification
|
||
Future-data protection
|
||
Factor
|
||
Backtest
|
||
API
|
||
```
|
||
|
||
关键金融逻辑必须有单元测试。
|
||
|
||
---
|
||
|
||
# 32. 类型与代码质量
|
||
|
||
Python:
|
||
|
||
```text
|
||
Python 3.11+
|
||
```
|
||
|
||
推荐:
|
||
|
||
```text
|
||
ruff
|
||
pytest
|
||
mypy / pyright
|
||
pydantic
|
||
SQLAlchemy 2.x
|
||
Alembic
|
||
```
|
||
|
||
代码必须尽量:
|
||
|
||
```text
|
||
typed
|
||
small
|
||
testable
|
||
documented
|
||
```
|
||
|
||
---
|
||
|
||
# 33. 配置管理
|
||
|
||
禁止把:
|
||
|
||
```text
|
||
Tushare Token
|
||
数据库密码
|
||
LLM API Key
|
||
```
|
||
|
||
写进 Git。
|
||
|
||
使用:
|
||
|
||
```text
|
||
.env
|
||
.env.example
|
||
```
|
||
|
||
`.env.example` 只提供变量名,不提供真实密钥。
|
||
|
||
---
|
||
|
||
# 34. Git 约束
|
||
|
||
每个功能尽量:
|
||
|
||
```text
|
||
一个清晰 commit
|
||
```
|
||
|
||
Commit message 要表达实际修改:
|
||
|
||
```text
|
||
feat: add tushare daily data provider
|
||
fix: prevent future financial data leakage
|
||
feat: add backtest job API
|
||
```
|
||
|
||
禁止提交:
|
||
|
||
```text
|
||
数据库
|
||
缓存
|
||
大规模行情文件
|
||
模型权重
|
||
.env
|
||
secret
|
||
```
|
||
|
||
---
|
||
|
||
# 35. 修改现有代码的原则
|
||
|
||
优先:
|
||
|
||
```text
|
||
最小修改
|
||
```
|
||
|
||
而不是:
|
||
|
||
```text
|
||
顺手重构整个项目
|
||
```
|
||
|
||
如果确实需要架构调整:
|
||
|
||
1. 说明原因
|
||
2. 明确影响范围
|
||
3. 分阶段修改
|
||
4. 保证每阶段可运行
|
||
|
||
---
|
||
|
||
# 36. 新功能开发流程
|
||
|
||
AI Agent 应按照:
|
||
|
||
```text
|
||
1. 阅读 ARCHITECTURE.md
|
||
2. 理解需求
|
||
3. 定义 Domain
|
||
4. 定义 DTO
|
||
5. 定义 Repository Interface
|
||
6. 实现 Infrastructure
|
||
7. 实现 Application Service
|
||
8. 实现 API
|
||
9. 实现前端
|
||
10. 编写测试
|
||
11. 运行测试
|
||
12. 更新文档
|
||
```
|
||
|
||
不要倒过来从 UI 开始堆代码。
|
||
|
||
---
|
||
|
||
# 37. 数据迁移原则
|
||
|
||
未来 SQLite → MySQL 时:
|
||
|
||
```text
|
||
Domain 不变
|
||
Application 不变
|
||
API 不变
|
||
Frontend 不变
|
||
|
||
只调整:
|
||
Infrastructure / Database Configuration
|
||
```
|
||
|
||
这是 DAO / Repository 抽象必须保证的目标。
|
||
|
||
---
|
||
|
||
# 38. 第一阶段禁止事项
|
||
|
||
MVP 阶段不要主动增加:
|
||
|
||
```text
|
||
Kubernetes
|
||
微服务集群
|
||
Kafka
|
||
复杂消息队列
|
||
实时交易
|
||
高频交易
|
||
复杂权限系统
|
||
多租户
|
||
GPU 集群
|
||
```
|
||
|
||
除非用户明确提出。
|
||
|
||
---
|
||
|
||
# 39. 推荐 MVP 技术栈
|
||
|
||
```text
|
||
Frontend
|
||
React / Next.js
|
||
TypeScript
|
||
ECharts
|
||
|
||
Backend
|
||
FastAPI
|
||
Pydantic
|
||
SQLAlchemy
|
||
Alembic
|
||
|
||
Data
|
||
Tushare
|
||
Sina fallback
|
||
Parquet
|
||
DuckDB
|
||
|
||
Quant
|
||
Qlib
|
||
LightGBM
|
||
|
||
Database
|
||
SQLite
|
||
|
||
Testing
|
||
pytest
|
||
ruff
|
||
|
||
Deployment
|
||
Docker Compose
|
||
```
|
||
|
||
---
|
||
|
||
# 40. 最重要的开发原则
|
||
|
||
如果一个实现:
|
||
|
||
```text
|
||
更简单
|
||
更容易测试
|
||
更容易替换
|
||
更容易解释
|
||
```
|
||
|
||
优先选择它。
|
||
|
||
如果一个实现:
|
||
|
||
```text
|
||
把 Qlib
|
||
数据库
|
||
前端
|
||
Agent
|
||
```
|
||
|
||
强耦合在一起:
|
||
|
||
> 默认拒绝。
|
||
|
||
最终系统必须保持:
|
||
|
||
```text
|
||
Data
|
||
↓
|
||
Domain
|
||
↓
|
||
Research
|
||
↓
|
||
Qlib
|
||
↓
|
||
Experiment
|
||
↓
|
||
API
|
||
↓
|
||
Frontend
|
||
↓
|
||
Agent
|
||
```
|
||
|
||
每一层职责明确、接口稳定、可测试、可替换。
|
||
|
||
---
|
||
|
||
# 41. Agent 每次任务结束前必须检查
|
||
|
||
```text
|
||
□ 是否违反 ARCHITECTURE.md?
|
||
□ 是否绕过 DAO?
|
||
□ 是否把 SQLite 写死?
|
||
□ 是否直接依赖 Qlib 内部实现?
|
||
□ 是否可能引入未来函数?
|
||
□ 是否引入数据泄露?
|
||
□ 是否有测试?
|
||
□ 是否修改了 API 契约?
|
||
□ 是否需要更新文档?
|
||
□ 是否把 secret 提交进 Git?
|
||
□ 是否进行了不必要的大规模重构?
|
||
```
|
||
|
||
如果任一项为“是”,必须先处理或明确向用户报告。
|
||
|
||
---
|
||
|
||
# 42. 最终架构目标
|
||
|
||
本项目最终应该能够做到:
|
||
|
||
```text
|
||
用户
|
||
↓
|
||
Web UI / AI Agent
|
||
↓
|
||
Research Specification
|
||
↓
|
||
Application Service
|
||
↓
|
||
Data / Factor / Strategy / Backtest
|
||
↓
|
||
Qlib
|
||
↓
|
||
Experiment
|
||
↓
|
||
可视化结果
|
||
```
|
||
|
||
并且:
|
||
|
||
```text
|
||
Tushare → Sina
|
||
SQLite → MySQL
|
||
Qlib → 其他量化引擎
|
||
普通研究 → AI Agent
|
||
```
|
||
|
||
都不应该要求推翻整个系统。
|
||
|
||
**这比单纯快速把功能写出来更重要。**
|
||
|