- AGENT.md / docs/ARCHITECTURE.md 约束与架构文档入库(README 覆盖远端模板) - 根级配置:.gitignore / .env.example / config.yaml(密钥只走 .env,不入库) - 目录占位:data(parquet/qlib/raw/normalized) / experiments / docker / frontend/web / scripts
1010 lines
16 KiB
Markdown
1010 lines
16 KiB
Markdown
# A股个人量化选股与回测平台架构
|
||
|
||
> 版本:v1.0
|
||
> 定位:个人开发、A股、中低频选股/回测、AI Agent 二次开发
|
||
> 数据源优先级:Tushare > 新浪财经
|
||
> 当前数据库:SQLite
|
||
> 未来数据库:MySQL
|
||
> 核心量化引擎:Qlib
|
||
> 前端:React / Next.js
|
||
> 后端:FastAPI + Python
|
||
|
||
---
|
||
|
||
## 1. 项目目标
|
||
|
||
本项目不是简单给 Qlib 套一个 Web UI,而是构建一个独立的个人量化研究平台:
|
||
|
||
```text
|
||
Web 前端
|
||
↓
|
||
FastAPI
|
||
↓
|
||
Research Specification
|
||
↓
|
||
业务服务层
|
||
├── 数据服务
|
||
├── 股票池服务
|
||
├── 因子服务
|
||
├── 选股服务
|
||
├── 模型服务
|
||
├── 回测服务
|
||
└── 实验服务
|
||
↓
|
||
Qlib / ML
|
||
↓
|
||
标准化研究结果
|
||
↓
|
||
SQLite / Parquet
|
||
↓
|
||
Web 前端
|
||
```
|
||
|
||
AI Agent 作为独立能力层,通过受控 Tool 调用上述服务,而不是直接操作数据库或随意修改 Qlib 内部代码。
|
||
|
||
---
|
||
|
||
# 2. 总体架构
|
||
|
||
```text
|
||
┌──────────────────────┐
|
||
│ React / Next │
|
||
│ │
|
||
│ Dashboard │
|
||
│ 股票池 │
|
||
│ 因子研究 │
|
||
│ 选股 │
|
||
│ 回测 │
|
||
│ Experiment │
|
||
│ AI Research │
|
||
└──────────┬───────────┘
|
||
│
|
||
REST / SSE
|
||
│
|
||
▼
|
||
┌──────────────────────┐
|
||
│ FastAPI │
|
||
│ │
|
||
│ API / Auth / DTO │
|
||
│ Job / Validation │
|
||
└──────────┬───────────┘
|
||
│
|
||
┌────────────┼────────────┐
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
Data Service Research Agent Service
|
||
│ Service │
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
DAO / Repo Qlib / ML Agent Tools
|
||
│ │ │
|
||
└────────────┼──────────────┘
|
||
▼
|
||
┌──────────────────────┐
|
||
│ Persistence │
|
||
│ │
|
||
│ SQLite │
|
||
│ Parquet │
|
||
│ Qlib Dataset │
|
||
└──────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
# 3. 核心设计原则
|
||
|
||
## 3.1 Qlib 不是系统数据库
|
||
|
||
Qlib 负责:
|
||
|
||
- Feature / Dataset
|
||
- 模型训练
|
||
- Prediction
|
||
- Portfolio
|
||
- Backtest
|
||
- 部分分析工具
|
||
|
||
Qlib 不作为业务系统唯一数据存储。
|
||
|
||
系统数据分为:
|
||
|
||
```text
|
||
业务数据 → SQLite
|
||
历史/分析型大数据 → Parquet
|
||
Qlib计算数据 → Qlib Dataset
|
||
实验结果 → SQLite
|
||
```
|
||
|
||
## 3.2 前端不能直接调用 Qlib
|
||
|
||
必须经过:
|
||
|
||
```text
|
||
Frontend
|
||
↓
|
||
FastAPI
|
||
↓
|
||
Application Service
|
||
↓
|
||
Qlib Adapter
|
||
↓
|
||
Qlib
|
||
```
|
||
|
||
这样未来可以替换 Qlib,而不影响前端。
|
||
|
||
## 3.3 DAO 必须与数据库实现解耦
|
||
|
||
业务代码禁止直接:
|
||
|
||
```python
|
||
sqlite3.connect(...)
|
||
```
|
||
|
||
也禁止在 Service 中写 SQL。
|
||
|
||
必须:
|
||
|
||
```text
|
||
Service
|
||
↓
|
||
Repository / DAO Interface
|
||
↓
|
||
SQLite Repository
|
||
```
|
||
|
||
未来:
|
||
|
||
```text
|
||
Service
|
||
↓
|
||
Repository Interface
|
||
├── SQLite Repository
|
||
└── MySQL Repository
|
||
```
|
||
|
||
业务层不感知底层数据库。
|
||
|
||
---
|
||
|
||
# 4. 数据源架构
|
||
|
||
## 4.1 数据源优先级
|
||
|
||
```text
|
||
Data Service
|
||
│
|
||
┌────────┴────────┐
|
||
▼ ▼
|
||
Tushare Sina
|
||
Primary Secondary
|
||
│ │
|
||
└────────┬────────┘
|
||
▼
|
||
Normalization
|
||
│
|
||
Validation
|
||
│
|
||
Storage
|
||
```
|
||
|
||
### 第一优先级:Tushare
|
||
|
||
用于:
|
||
|
||
- 股票基本信息
|
||
- 日线行情
|
||
- 复权因子
|
||
- 指数
|
||
- 财务数据
|
||
- 分红送转
|
||
- 停复牌
|
||
- 行业/概念
|
||
- 其他可用数据
|
||
|
||
### 第二优先级:新浪财经
|
||
|
||
主要作为:
|
||
|
||
- Tushare 数据缺失补充
|
||
- 行情数据校验
|
||
- 特定实时/准实时数据补充
|
||
|
||
新浪数据必须经过统一 Adapter,禁止业务代码直接调用新浪接口。
|
||
|
||
---
|
||
|
||
# 5. 数据采集流水线
|
||
|
||
```text
|
||
Scheduler
|
||
↓
|
||
Data Source Adapter
|
||
↓
|
||
Raw Response
|
||
↓
|
||
Schema Validation
|
||
↓
|
||
Normalization
|
||
↓
|
||
Deduplication
|
||
↓
|
||
Business Validation
|
||
↓
|
||
DAO
|
||
↓
|
||
SQLite
|
||
↓
|
||
Parquet Export
|
||
↓
|
||
Qlib Dataset
|
||
```
|
||
|
||
每个数据源都必须有独立 Adapter:
|
||
|
||
```text
|
||
data/
|
||
├── sources/
|
||
│ ├── base.py
|
||
│ ├── tushare.py
|
||
│ └── sina.py
|
||
├── normalizers/
|
||
├── validators/
|
||
└── service.py
|
||
```
|
||
|
||
业务层只依赖:
|
||
|
||
```python
|
||
MarketDataProvider
|
||
```
|
||
|
||
而不是:
|
||
|
||
```python
|
||
TushareClient
|
||
```
|
||
|
||
---
|
||
|
||
# 6. 数据库设计
|
||
|
||
## 6.1 SQLite 定位
|
||
|
||
SQLite 用于:
|
||
|
||
- 股票基础信息
|
||
- 数据源状态
|
||
- 数据同步记录
|
||
- 因子定义
|
||
- 策略定义
|
||
- 回测配置
|
||
- Experiment metadata
|
||
- Job
|
||
- 用户配置
|
||
- 系统配置
|
||
|
||
SQLite 不建议长期承载超大规模原始行情明细。
|
||
|
||
历史行情和大量 Feature 优先使用 Parquet。
|
||
|
||
---
|
||
|
||
# 7. DAO / Repository 设计
|
||
|
||
推荐使用:
|
||
|
||
```text
|
||
SQLAlchemy 2.x
|
||
+
|
||
Repository Pattern
|
||
+
|
||
Pydantic DTO
|
||
```
|
||
|
||
目录:
|
||
|
||
```text
|
||
backend/
|
||
├── domain/
|
||
│ ├── entities/
|
||
│ └── repositories/
|
||
│
|
||
├── infrastructure/
|
||
│ └── persistence/
|
||
│ ├── sqlalchemy/
|
||
│ │ ├── models/
|
||
│ │ ├── repositories/
|
||
│ │ └── session.py
|
||
│ └── migrations/
|
||
│
|
||
└── application/
|
||
└── services/
|
||
```
|
||
|
||
Repository 接口示例:
|
||
|
||
```python
|
||
class StockRepository(Protocol):
|
||
def get_by_symbol(self, symbol: str) -> Stock | None: ...
|
||
def list(self, filters: StockFilter) -> list[Stock]: ...
|
||
def save(self, stock: Stock) -> Stock: ...
|
||
```
|
||
|
||
SQLite:
|
||
|
||
```python
|
||
class SqlAlchemyStockRepository(StockRepository):
|
||
...
|
||
```
|
||
|
||
未来 MySQL:
|
||
|
||
```python
|
||
class MySqlStockRepository(StockRepository):
|
||
...
|
||
```
|
||
|
||
推荐实际上让两者都基于 SQLAlchemy,而不是分别维护两套 SQL。
|
||
|
||
这样数据库切换主要通过:
|
||
|
||
```text
|
||
DATABASE_URL
|
||
```
|
||
|
||
完成。
|
||
|
||
例如:
|
||
|
||
```text
|
||
SQLite:
|
||
sqlite:///./data/quant.db
|
||
|
||
MySQL:
|
||
mysql+pymysql://user:password@host/quant
|
||
```
|
||
|
||
业务 Service 不需要修改。
|
||
|
||
---
|
||
|
||
# 8. 建议的核心数据库表
|
||
|
||
第一阶段至少包括:
|
||
|
||
```text
|
||
stock
|
||
stock_daily
|
||
stock_adjust_factor
|
||
|
||
industry
|
||
stock_industry
|
||
|
||
index
|
||
index_daily
|
||
|
||
trading_calendar
|
||
suspend_data
|
||
stock_st_data
|
||
|
||
financial_indicator
|
||
income_statement
|
||
balance_sheet
|
||
cashflow_statement
|
||
|
||
factor_definition
|
||
factor_test
|
||
|
||
strategy
|
||
strategy_factor
|
||
|
||
backtest
|
||
backtest_trade
|
||
backtest_position
|
||
backtest_metric
|
||
|
||
experiment
|
||
experiment_artifact
|
||
|
||
job
|
||
data_sync_log
|
||
```
|
||
|
||
注意:
|
||
|
||
`stock_daily` 等大量时序数据如果规模变大,应逐渐迁移到:
|
||
|
||
```text
|
||
Parquet
|
||
```
|
||
|
||
SQLite 保留元数据和索引。
|
||
|
||
---
|
||
|
||
# 9. 时间与未来函数防护
|
||
|
||
所有研究数据必须明确:
|
||
|
||
```text
|
||
trade_date
|
||
report_date
|
||
announce_date
|
||
effective_date
|
||
```
|
||
|
||
财务数据必须按照 `announce_date` 控制可见性。
|
||
|
||
禁止:
|
||
|
||
```text
|
||
使用未来公告的财务数据回测过去
|
||
```
|
||
|
||
所有数据查询必须明确:
|
||
|
||
```text
|
||
as_of_date
|
||
```
|
||
|
||
例如:
|
||
|
||
```python
|
||
get_financial_data(
|
||
symbol="600519.SH",
|
||
as_of_date="2024-06-30"
|
||
)
|
||
```
|
||
|
||
只允许返回当时市场已经知道的数据。
|
||
|
||
---
|
||
|
||
# 10. Domain 层
|
||
|
||
建议核心领域对象:
|
||
|
||
```text
|
||
Stock
|
||
Universe
|
||
Factor
|
||
FactorTest
|
||
Model
|
||
Strategy
|
||
Portfolio
|
||
Backtest
|
||
Experiment
|
||
Job
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
Universe
|
||
↓
|
||
Factor
|
||
↓
|
||
Model
|
||
↓
|
||
Strategy
|
||
↓
|
||
Backtest
|
||
↓
|
||
Experiment
|
||
```
|
||
|
||
---
|
||
|
||
# 11. Research Specification
|
||
|
||
前端、AI Agent 和后端统一使用自己的研究描述对象。
|
||
|
||
示例:
|
||
|
||
```json
|
||
{
|
||
"type": "backtest",
|
||
"universe": {
|
||
"market": "CN_A",
|
||
"exclude_st": true,
|
||
"exclude_suspended": true,
|
||
"min_listing_days": 250
|
||
},
|
||
"factors": [
|
||
{
|
||
"name": "momentum_60",
|
||
"weight": 0.3
|
||
},
|
||
{
|
||
"name": "roe_ttm",
|
||
"weight": 0.3
|
||
},
|
||
{
|
||
"name": "low_volatility_60",
|
||
"weight": 0.4
|
||
}
|
||
],
|
||
"selection": {
|
||
"top_n": 30
|
||
},
|
||
"rebalance": "monthly",
|
||
"period": {
|
||
"start": "2015-01-01",
|
||
"end": "2026-08-31"
|
||
}
|
||
}
|
||
```
|
||
|
||
然后:
|
||
|
||
```text
|
||
Research Specification
|
||
↓
|
||
Validator
|
||
↓
|
||
Strategy Builder
|
||
↓
|
||
Qlib Adapter
|
||
```
|
||
|
||
---
|
||
|
||
# 12. 前端结构
|
||
|
||
第一阶段:
|
||
|
||
```text
|
||
Dashboard
|
||
股票池
|
||
因子研究
|
||
选股
|
||
回测
|
||
Experiment
|
||
```
|
||
|
||
第二阶段:
|
||
|
||
```text
|
||
模型研究
|
||
Portfolio
|
||
AI Research
|
||
数据管理
|
||
```
|
||
|
||
---
|
||
|
||
# 13. 前端输入
|
||
|
||
## 股票池
|
||
|
||
支持:
|
||
|
||
- 市场
|
||
- 股票类型
|
||
- 行业
|
||
- 市值
|
||
- 流动性
|
||
- 上市时间
|
||
- ST
|
||
- 停牌
|
||
- 指数成分
|
||
|
||
## 因子
|
||
|
||
支持:
|
||
|
||
- 内置因子
|
||
- 自定义表达式
|
||
- 因子组合
|
||
- 权重
|
||
- 标准化
|
||
- 中性化
|
||
|
||
## 选股
|
||
|
||
支持:
|
||
|
||
- Top N
|
||
- Top %
|
||
- Score
|
||
- 等权
|
||
- Score 加权
|
||
- 行业约束
|
||
- 单股权重上限
|
||
|
||
## 回测
|
||
|
||
支持:
|
||
|
||
- 起止日期
|
||
- 调仓频率
|
||
- 初始资金
|
||
- 手续费
|
||
- 印花税
|
||
- 滑点
|
||
- 涨跌停限制
|
||
- 停牌限制
|
||
|
||
---
|
||
|
||
# 14. 前端输出
|
||
|
||
回测结果统一转换为:
|
||
|
||
```text
|
||
BacktestResult
|
||
├── summary
|
||
├── equity_curve
|
||
├── drawdown
|
||
├── monthly_returns
|
||
├── yearly_returns
|
||
├── positions
|
||
├── trades
|
||
├── turnover
|
||
├── risk_metrics
|
||
└── factor_exposure
|
||
```
|
||
|
||
这样前端完全不需要理解 Qlib 内部对象。
|
||
|
||
---
|
||
|
||
# 15. 异步 Job
|
||
|
||
所有耗时任务必须异步:
|
||
|
||
```text
|
||
POST /api/backtests
|
||
↓
|
||
Job Created
|
||
↓
|
||
Worker
|
||
↓
|
||
Qlib
|
||
↓
|
||
Result
|
||
```
|
||
|
||
前端通过:
|
||
|
||
```text
|
||
SSE
|
||
```
|
||
|
||
接收:
|
||
|
||
```text
|
||
queued
|
||
running
|
||
factor_calculation
|
||
model_training
|
||
backtesting
|
||
analysis
|
||
completed
|
||
failed
|
||
```
|
||
|
||
第一阶段可以使用:
|
||
|
||
```text
|
||
FastAPI BackgroundTasks
|
||
```
|
||
|
||
或简单本地 Worker。
|
||
|
||
系统复杂后再引入:
|
||
|
||
```text
|
||
Redis + Celery/RQ
|
||
```
|
||
|
||
不要第一版过度工程化。
|
||
|
||
---
|
||
|
||
# 16. Qlib Adapter
|
||
|
||
Qlib 必须被封装:
|
||
|
||
```text
|
||
quant/
|
||
├── qlib_adapter/
|
||
│ ├── dataset.py
|
||
│ ├── feature.py
|
||
│ ├── model.py
|
||
│ ├── backtest.py
|
||
│ └── provider.py
|
||
```
|
||
|
||
业务代码:
|
||
|
||
```python
|
||
backtest_service.run(spec)
|
||
```
|
||
|
||
而不是:
|
||
|
||
```python
|
||
qlib.init(...)
|
||
qlib.workflow(...)
|
||
```
|
||
|
||
散落在项目各处。
|
||
|
||
---
|
||
|
||
# 17. AI Agent 架构
|
||
|
||
Agent 只能调用 Tool:
|
||
|
||
```text
|
||
Agent
|
||
├── search_stocks
|
||
├── inspect_factor
|
||
├── test_factor
|
||
├── create_strategy
|
||
├── run_backtest
|
||
├── compare_experiments
|
||
├── get_backtest_result
|
||
└── create_experiment
|
||
```
|
||
|
||
Agent 禁止:
|
||
|
||
```text
|
||
直接 SQL
|
||
直接修改数据库
|
||
直接删除数据
|
||
直接执行任意 shell
|
||
直接修改生产策略
|
||
```
|
||
|
||
Agent:
|
||
|
||
```text
|
||
自然语言
|
||
↓
|
||
Research Plan
|
||
↓
|
||
Tool Calls
|
||
↓
|
||
Research Specification
|
||
↓
|
||
执行
|
||
↓
|
||
Experiment
|
||
↓
|
||
分析
|
||
```
|
||
|
||
---
|
||
|
||
# 18. AI Research 示例
|
||
|
||
用户:
|
||
|
||
> 帮我寻找适合A股月度调仓的低频选股因子。
|
||
|
||
Agent:
|
||
|
||
```text
|
||
1. 获取A股股票池
|
||
2. 创建候选因子
|
||
3. IC测试
|
||
4. RankIC测试
|
||
5. 分层测试
|
||
6. 相关性分析
|
||
7. 删除冗余因子
|
||
8. 组合因子
|
||
9. Walk Forward
|
||
10. 回测
|
||
11. 保存 Experiment
|
||
12. 输出结论
|
||
```
|
||
|
||
Agent 不能因为一次回测表现好就直接认为策略有效。
|
||
|
||
必须关注:
|
||
|
||
```text
|
||
样本外
|
||
Walk Forward
|
||
不同市场阶段
|
||
交易成本
|
||
换手率
|
||
因子稳定性
|
||
```
|
||
|
||
---
|
||
|
||
# 19. 推荐的开发目录
|
||
|
||
```text
|
||
quant-platform/
|
||
│
|
||
├── frontend/
|
||
│ └── web/
|
||
│
|
||
├── backend/
|
||
│ ├── app/
|
||
│ │ ├── api/
|
||
│ │ ├── application/
|
||
│ │ ├── domain/
|
||
│ │ ├── infrastructure/
|
||
│ │ ├── quant/
|
||
│ │ ├── agent/
|
||
│ │ └── core/
|
||
│ │
|
||
│ └── tests/
|
||
│
|
||
├── data/
|
||
│ ├── raw/
|
||
│ ├── normalized/
|
||
│ ├── parquet/
|
||
│ └── qlib/
|
||
│
|
||
├── experiments/
|
||
│
|
||
├── scripts/
|
||
│
|
||
├── docker/
|
||
│
|
||
├── docs/
|
||
│
|
||
├── ARCHITECTURE.md
|
||
├── AGENT.md
|
||
└── README.md
|
||
```
|
||
|
||
---
|
||
|
||
# 20. 第一阶段 MVP
|
||
|
||
不要一开始做所有功能。
|
||
|
||
### Phase 1:数据
|
||
|
||
```text
|
||
Tushare
|
||
↓
|
||
标准化
|
||
↓
|
||
SQLite
|
||
↓
|
||
Parquet
|
||
```
|
||
|
||
完成:
|
||
|
||
- 股票列表
|
||
- 交易日历
|
||
- 日线
|
||
- 复权因子
|
||
- 基本财务指标
|
||
|
||
### Phase 2:Qlib
|
||
|
||
```text
|
||
Parquet
|
||
↓
|
||
Qlib Dataset
|
||
↓
|
||
Alpha158 / 自定义因子
|
||
↓
|
||
LightGBM
|
||
↓
|
||
Backtest
|
||
```
|
||
|
||
### Phase 3:Web
|
||
|
||
完成:
|
||
|
||
```text
|
||
股票池
|
||
因子
|
||
选股
|
||
回测
|
||
结果
|
||
```
|
||
|
||
### Phase 4:Experiment
|
||
|
||
所有研究自动保存。
|
||
|
||
### Phase 5:AI Agent
|
||
|
||
最后再接:
|
||
|
||
```text
|
||
自然语言
|
||
↓
|
||
Research Plan
|
||
↓
|
||
Tool
|
||
↓
|
||
Experiment
|
||
```
|
||
|
||
---
|
||
|
||
# 21. 数据流总图
|
||
|
||
```text
|
||
Tushare
|
||
│
|
||
▼
|
||
Tushare Adapter
|
||
│
|
||
│ 失败/缺失
|
||
▼
|
||
Sina Adapter
|
||
│
|
||
▼
|
||
Raw Data
|
||
│
|
||
▼
|
||
Data Validator
|
||
│
|
||
▼
|
||
Normalization
|
||
│
|
||
┌─────┴──────┐
|
||
▼ ▼
|
||
SQLite Parquet
|
||
│ │
|
||
│ ▼
|
||
│ Qlib Dataset
|
||
│ │
|
||
│ ┌─────┴─────┐
|
||
│ ▼ ▼
|
||
│ Factor Model
|
||
│ │ │
|
||
│ └─────┬─────┘
|
||
│ ▼
|
||
│ Strategy
|
||
│ │
|
||
│ ▼
|
||
│ Backtest
|
||
│ │
|
||
└────────────┤
|
||
▼
|
||
Experiment
|
||
│
|
||
┌─────────┴─────────┐
|
||
▼ ▼
|
||
Web Output AI Analysis
|
||
```
|
||
|
||
---
|
||
|
||
# 22. 最终原则
|
||
|
||
这个系统必须坚持:
|
||
|
||
1. **Tushare 第一,新浪第二**
|
||
2. **数据源通过 Adapter 隔离**
|
||
3. **DAO / Repository 隔离数据库**
|
||
4. **SQLite 当前使用,SQLAlchemy 保证未来 MySQL 可切换**
|
||
5. **大量历史时序数据逐渐转 Parquet**
|
||
6. **Qlib 是计算引擎,不是整个系统**
|
||
7. **前端只面对自己的 Domain API**
|
||
8. **前后端通过 Research Specification 解耦**
|
||
9. **所有耗时任务异步化**
|
||
10. **所有研究产生 Experiment**
|
||
11. **严格防止未来函数**
|
||
12. **AI Agent 只能通过 Tool 使用研究能力**
|
||
13. **第一阶段不做实盘**
|
||
14. **第一阶段不做复杂分布式架构**
|
||
15. **每个模块都必须可以被单元测试**
|
||
|
||
最终目标不是:
|
||
|
||
> Qlib + Web
|
||
|
||
而是:
|
||
|
||
> **一个以 Qlib 为量化研究引擎、以 Tushare 为主要数据源、具备清晰 DAO 抽象和 AI Agent 接口的个人 A 股量化研究平台。**
|
||
|