Files
qlib/AGENT.md
T
Simon a3055eff5d docs(agent): 新增「买卖理由词表」与「长任务反馈」两条硬约束(§27.1 / §27.2)
把这两次踩过的坑固化成约束,避免后续 agent 各自发挥:
- §27.1:买卖点必须带结构化理由(成交与未成交都要),原因分类封闭词表、两个引擎共用;
  data 只放引擎当时的真实数字,前端不推算;跌出 TopN / 不在候选池 / 全量换仓 / Tmin /
  Tmax / 涨跌停 / 现金不足必须区分,宁可新增 code 也不套语义不符的旧 code;
  因子曲线口径(持仓市值加权原始值、空仓不落点)+ 方向/单位必须写明;
  每条曲线可新页面放大且放大页从归档读。
- §27.2:长任务点了立刻有字、显示真实作业号/阶段/逐秒已用、可取消、失败给后端原文、
  成功给归档入口、自动滚入视野;同步接口没有作业号时如实说明,禁止伪造。
2026-10-01 18:13:37 +08:00

18 KiB
Raw Blame History

AGENT.md

AI Agent 开发约束

本文件是本项目所有 AI Coding Agent(Claude Code、DSH、REASONIX等)的开发约束。

在修改代码前必须阅读本文件和 ARCHITECTURE.md。

AI 回答必须使用中文


0. 网络下载与代理规则

当网络下载(git clone、pip / uv 安装、wget、数据下载、Docker pull 等)出现困难或超时时, 统一使用本机局域网 HTTP 代理:

192.168.1.160:3128

示例(仅在下载失败 / 超时时启用,不把代理写入代码或 Git):

# 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. 总体开发原则

必须遵守:

简单优先
模块解耦
接口稳定
数据可追溯
实验可复现
禁止未来函数
AI 可调用
SQLite → MySQL 可迁移
API KEY, 账号密码等敏感信息写入根目录.env文件
可配置项统一写入根目录config.yaml 文件

禁止为了“看起来专业”而过度微服务化。


3. 修改代码前

AI Agent 必须先:

  1. 阅读 ARCHITECTURE.md
  2. 阅读相关模块代码
  3. 找到现有接口
  4. 判断是否可以复用
  5. 最小范围修改
  6. 修改后运行测试

禁止看到需求就直接大量重构。


4. 架构边界

严格遵守:

Frontend
   ↓
API
   ↓
Application Service
   ↓
Domain
   ↓
Repository / DAO
   ↓
Infrastructure

量化:

Application Service
   ↓
Quant Service
   ↓
Qlib Adapter
   ↓
Qlib

禁止:

Frontend → Qlib
Frontend → Database
Agent → Database
Agent → Qlib internal API
Service → sqlite3

5. 数据源约束

5.1 Tushare 是第一数据源

所有已有 Tushare 能提供的数据,默认必须优先使用 Tushare。

禁止无理由改成新浪。

5.2 新浪是备用数据源

新浪财经只能作为:

  • Tushare 缺失数据
  • Tushare 暂时不可用
  • 数据交叉验证
  • 特定行情数据补充

必须通过:

SinaAdapter

接入。

业务代码不得直接 import 新浪 API 实现。


6. Data Provider 接口

业务层只能依赖抽象接口:

class MarketDataProvider(Protocol):
    ...

例如:

provider.get_daily(...)
provider.get_stock_basic(...)
provider.get_financial(...)

不能:

from tushare import ...

散落在业务代码中。

正确:

MarketDataProvider
       │
       ├── TushareProvider
       └── SinaProvider

7. 数据源 Failover

推荐:

Tushare
   │
   ├── success → use
   │
   └── failure / missing
             ↓
           Sina

但必须记录:

source
request_time
success
failure_reason
row_count
data_date

不能静默切换导致数据来源不可追踪。


8. 数据一致性

任何行情数据必须至少考虑:

symbol
trade_date
open
high
low
close
volume
amount

需要复权时必须使用明确的复权因子。

禁止在代码中偷偷改变:

前复权
后复权
不复权

必须在 API / Research Specification 中明确。


9. 未来函数是最高优先级风险

严禁:

回测日期 T
使用 T 之后才公布的数据

财务数据必须区分:

report_date
announce_date

研究查询必须支持:

as_of_date

任何新增财务数据功能,都必须回答:

在回测当天,这条数据是否已经公开?

如果无法回答,不得用于回测。


10. DAO / Repository 约束

业务层禁止直接操作:

sqlite3
SQLAlchemy Session
SQL

业务层只能调用:

Repository Interface

例如:

stock_repo.get_by_symbol(...)
factor_repo.list(...)
experiment_repo.save(...)

11. SQLite → MySQL 兼容

当前使用 SQLite。

但是:

任何业务代码都不能假设数据库是 SQLite。

禁止:

SELECT * FROM sqlite_master

禁止 SQLite 专有 SQL。

禁止:

sqlite3.connect()

出现在 Service / Domain 层。

数据库访问统一:

SQLAlchemy
Repository
Alembic

未来切换 MySQL 时,业务层不应该修改。


12. Migration

数据库结构必须使用:

Alembic

禁止手工修改生产数据库结构作为正式方案。

新增字段必须:

Model
 ↓
Migration
 ↓
Test

13. Parquet 使用原则

大量历史时序数据优先:

Parquet

SQLite 主要存:

  • metadata
  • configuration
  • experiment
  • job
  • factor definition
  • strategy
  • 索引/小规模业务数据

不要把几十年、全市场、高频明细全部塞入 SQLite。


14. Qlib 使用原则

Qlib 必须被 Adapter 封装。

例如:

QlibDatasetAdapter
QlibModelAdapter
QlibBacktestAdapter

业务层不要直接依赖 Qlib 内部实现。

禁止项目代码到处出现:

import qlib

尽量集中在:

quant/qlib_adapter/

15. 不要修改 Qlib 源码

默认禁止:

修改 site-packages/qlib

如果 Qlib 功能不满足要求:

优先:

Adapter
Wrapper
Extension

只有确实无法实现时,才考虑 fork,并必须记录原因。


16. Research Specification

前端、Agent、后端统一通过:

Research Specification

描述研究任务。

不要让前端直接构造 Qlib YAML。

不要让 Agent 直接生成任意 Qlib 配置。

正确流程:

User / Agent
    ↓
Research Specification
    ↓
Schema Validation
    ↓
Strategy Builder
    ↓
Qlib Adapter

17. API 设计

API 必须面向业务对象:

/api/stocks
/api/universes
/api/factors
/api/factor-tests
/api/strategies
/api/backtests
/api/experiments
/api/jobs
/api/agent

不要设计成:

/api/qlib/xxx

除非是内部 Adapter API。


18. API 输入输出

API 输入必须使用:

Pydantic Schema

不要直接把 SQLAlchemy Model 暴露给前端。

例如:

Request DTO
    ↓
Application Service
    ↓
Domain
    ↓
Repository

输出:

Domain
    ↓
Response DTO
    ↓
Frontend

19. 异步任务

以下任务必须考虑异步执行:

  • 大量数据下载
  • 因子计算
  • 模型训练
  • 回测
  • 大规模参数搜索
  • AI Research

API 不应该长时间阻塞 HTTP 请求。

返回:

{
  "job_id": "BT-001",
  "status": "queued"
}

前端通过 SSE / WebSocket 获取进度。


20. Job 状态

统一状态:

queued
running
success
failed
cancelled

可选阶段:

data_loading
feature_calculation
model_training
prediction
backtesting
analysis
saving_result

21. Experiment 必须可复现

每次研究必须保存:

experiment_id
data_version
code_version
strategy_version
universe
factors
model
parameters
backtest_config
start_date
end_date
result

最好记录:

git commit

目标:

任何一个历史实验,都应该尽可能可以重新运行。


22. 因子开发约束

每个因子必须明确:

name
description
formula
input data
frequency
lookback
direction
normalization
neutralization

例如:

momentum_60

定义:
过去60个交易日收益率

频率:
daily

方向:
higher_is_better

禁止创建只有:

factor1()
factor2()

而没有定义和文档的因子。


23. 因子研究不能只看收益率

新增因子测试至少考虑:

IC
RankIC
ICIR
分层收益
换手率
稳定性
行业暴露
市值暴露
不同市场阶段

不能因为:

Sharpe > 2

就直接认为因子有效。


24. 回测约束

回测必须考虑:

手续费
印花税
滑点
涨跌停
停牌
成交约束
调仓频率

如果某项没有实现,必须在 UI 和结果中明确显示。

禁止默认假设:

永远可以买入
永远可以卖出
没有交易成本

25. 选股策略

核心目标是:

低频
选股
组合
回测

默认优先:

daily data
weekly rebalance
monthly rebalance

不要默认引入:

tick
order book
HFT

除非用户明确要求。


26. 前端约束

前端必须:

业务导向
参数清晰
结果可视化

不要直接暴露:

Qlib internal config
Python object
SQL

用户看到:

股票池
因子
模型
策略
回测

而不是:

DatasetH
HandlerLP
SignalRecord

27. 前端结果统一

回测结果必须标准化。

推荐:

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)没有作业号与阶段时,如实说明「同步请求、无阶段、 不可取消」,禁止合成假作业号或假进度喂给反馈组件。

28. AI Agent 约束

Agent 是:

Research Assistant

不是:

System Administrator

Agent 默认只能通过 Tool:

search_stock
get_market_data
test_factor
create_strategy
run_backtest
get_experiment
compare_experiments

Agent 不得:

直接删除数据库
直接修改数据库
直接执行 shell
修改生产配置
修改数据源凭证
修改系统安全配置

29. AI Agent 的研究行为

Agent 应优先:

提出假设
 ↓
建立实验
 ↓
运行测试
 ↓
分析结果
 ↓
提出下一步

而不是:

看到高收益
 ↓
宣布策略成功

必须主动考虑:

overfitting
look-ahead bias
survivorship bias
data leakage
transaction cost
parameter sensitivity
out-of-sample

30. 禁止数据泄露

股票池也必须防止:

未来成分股
未来行业分类
未来财务数据
未来退市信息

例如不能用“2026年的沪深300成分股”回测2015年。

必须使用历史时点对应的成分数据。


31. 测试要求

每次修改至少运行相关测试。

重点测试:

Data Adapter
DAO
Repository
Research Specification
Future-data protection
Factor
Backtest
API

关键金融逻辑必须有单元测试。


32. 类型与代码质量

Python:

Python 3.11+

推荐:

ruff
pytest
mypy / pyright
pydantic
SQLAlchemy 2.x
Alembic

代码必须尽量:

typed
small
testable
documented

33. 配置管理

禁止把:

Tushare Token
数据库密码
LLM API Key

写进 Git。

使用:

.env
.env.example

.env.example 只提供变量名,不提供真实密钥。


34. Git 约束

每个功能尽量:

一个清晰 commit

Commit message 要表达实际修改:

feat: add tushare daily data provider
fix: prevent future financial data leakage
feat: add backtest job API

禁止提交:

数据库
缓存
大规模行情文件
模型权重
.env
secret

35. 修改现有代码的原则

优先:

最小修改

而不是:

顺手重构整个项目

如果确实需要架构调整:

  1. 说明原因
  2. 明确影响范围
  3. 分阶段修改
  4. 保证每阶段可运行

36. 新功能开发流程

AI Agent 应按照:

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 时:

Domain 不变
Application 不变
API 不变
Frontend 不变

只调整:
Infrastructure / Database Configuration

这是 DAO / Repository 抽象必须保证的目标。


38. 第一阶段禁止事项

MVP 阶段不要主动增加:

Kubernetes
微服务集群
Kafka
复杂消息队列
实时交易
高频交易
复杂权限系统
多租户
GPU 集群

除非用户明确提出。


39. 推荐 MVP 技术栈

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. 最重要的开发原则

如果一个实现:

更简单
更容易测试
更容易替换
更容易解释

优先选择它。

如果一个实现:

把 Qlib
数据库
前端
Agent

强耦合在一起:

默认拒绝。

最终系统必须保持:

Data
 ↓
Domain
 ↓
Research
 ↓
Qlib
 ↓
Experiment
 ↓
API
 ↓
Frontend
 ↓
Agent

每一层职责明确、接口稳定、可测试、可替换。


41. Agent 每次任务结束前必须检查

□ 是否违反 ARCHITECTURE.md?
□ 是否绕过 DAO?
□ 是否把 SQLite 写死?
□ 是否直接依赖 Qlib 内部实现?
□ 是否可能引入未来函数?
□ 是否引入数据泄露?
□ 是否有测试?
□ 是否修改了 API 契约?
□ 是否需要更新文档?
□ 是否把 secret 提交进 Git?
□ 是否进行了不必要的大规模重构?

如果任一项为“是”,必须先处理或明确向用户报告。


42. 最终架构目标

本项目最终应该能够做到:

用户
 ↓
Web UI / AI Agent
 ↓
Research Specification
 ↓
Application Service
 ↓
Data / Factor / Strategy / Backtest
 ↓
Qlib
 ↓
Experiment
 ↓
可视化结果

并且:

Tushare → Sina
SQLite → MySQL
Qlib → 其他量化引擎
普通研究 → AI Agent

都不应该要求推翻整个系统。

这比单纯快速把功能写出来更重要。