面向 LightGBM 训练、回测、模拟盘交易的 A 股多因子实现。10 大类、96 个因子,
每个因子都带公式、方向和证据等级。
架构说明见 docs/ARCHITECTURE.md —— 包含与华泰研报原文的
差异核对、关键设计决策、以及开发中实际发现并修掉的问题清单。
需要 Python 3.10+(代码使用 X | None 等现代注解语法,
且 FastAPI 会主动解析路由签名,from __future__ import annotations 无法绕过)。
完整步骤见 docs/SETUP.md。速览:
py -3.12 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
cd showcase\frontend
npm installPython 依赖只有四个:fastapi / uvicorn[standard] / pandas / numpy。
不需要 sqlalchemy、pyarrow、matplotlib、scipy、scikit-learn、lightgbm ——
requirements.txt 里逐条写明了不引入的原因。
python run_showcase.py然后打开 http://localhost:3000 —— 一条命令拉起 FastAPI 后端 + Next.js 前端。
每个因子都有自己的页面,展示六段内容:
| 段落 | 内容 |
|---|---|
| ① 它在算什么 | 通俗解释这个因子在问什么问题 |
| ② 公式与所需数据 | 公式、方向、证据等级、需要哪些数据字段 |
| ③ 真实输入 | 因子函数实际读到的数据表 |
| ④ 计算过程 | 逐步展示:原始值 → 去极值 → Z-score → 填 0 → 方向翻转 |
| ⑤ 源码 | 从定义文件读取的真正运行的那段代码 |
| ⑥ 为什么这样写 | 证据来源与易错点 |
页面上每个数字都由后端真实计算产出,不是前端写死的示例。
手写的示例数字会在代码变更时悄悄脱节 —— 这个项目里凡是能推导的一律推导。
例如 EP 页能看到 424 个 NaN、从 2015-01-01 一路 NaN 到 2016-03 才出值 ——
暖机期是可见的,而不是被 dropna 藏起来。
| 页面 | 说明 |
|---|---|
/ |
总览:10 大类、证据等级分布 |
/factors |
96 个因子,可按大类/证据等级筛选 |
/concepts |
核心概念:面板方向、safe_div、处理链顺序…… |
/docs |
原有 Markdown 文档(含 10 篇 ADR)已按标题拆节,可在浏览器翻阅 |
技术栈:Next.js 15 (App Router) + FastAPI + SQLite。
因子定义以 REGISTRY 为唯一事实来源,SQLite 只存讲解层,与量化库解耦。
⚠️ 回测、LightGBM 训练、实盘已按需求解耦,不在展示范围内。
不要从头读代码。 按目的选入口 —— 完整地图见 docs/READING_MAP.md。
| 我想… | 看这个 |
|---|---|
| 快速搞懂这个库在干什么 | 检视指南 §1(五个核心概念) |
| 看架构图 / 时序图 / 类图 | 架构图(9 张 Mermaid 图) |
| 学 pandas / 搞清变量长什么样 | pandas 速成(含真实输出) |
| 查某个因子的定义和公式 | 因子速查表 |
| 知道为什么这样设计 | ADR 决策记录(11 条) |
| 知道现状离目标多远 | 差距分析 |
| 接自己的数据 | 数据契约 |
⚠️ 接真实数据之前请先读 GAP_ANALYSIS.md 的 P1#1:财报 PIT 对齐在
disclosure缺失时会回退用报告期,实测泄漏 29 天未来信息。用
python tests/test_pit_leakage.py可复现(当前 3/4 失败,是已知缺陷)。
# 端到端自检(35 项,不需要 LightGBM)
python scripts/smoke_test.py
# 生成因子清单(CSV + Markdown)
python scripts/report_factors.pyfrom quant.data import make_synthetic_panel
from quant.factors import REGISTRY
from quant.pipeline import FactorPipeline
from quant.datasets import build_dataset
from quant.models import train_model
panel = make_synthetic_panel() # 真实数据见下
specs = REGISTRY.select(categories=["valuation", "growth", "quality"],
min_confidence="strong_infer")
run = FactorPipeline().run(panel, specs) # 算因子
ds = build_dataset(run, panel, horizon_days=21) # 建数据集
model = train_model(ds) # 训练 LightGBMquant/
├── config.py 处理口径、股票池、数据源配置
├── ops/ 算子层:去极值/标准化/中性化 + 时序算子 + PIT 对齐
├── factors/
│ ├── registry.py FactorSpec / REGISTRY —— 因子库的唯一事实来源
│ └── defs/ 10 个大类的因子定义
├── data/ Panel 组装、PIT 对齐、parquet 缓存、合成数据
├── pipeline/ 因子计算引擎
├── datasets/ 数据集构建(标签、掩码、防未来函数、切分)
├── models/ LightGBM 训练与 IC 评估
├── backtest/ 组合回测 + 单因子 IC 检验
└── live/ 模拟盘/实盘循环、下单、Broker 接口
| 大类 | 数量 | 代表因子 |
|---|---|---|
| valuation 估值 | 16 | EP BP SP OCFP PEG EV2EBITDA |
| turnover 换手率 | 13 | turn_1m/3m/6m bias_turn_* std_turn_* |
| quality 财务质量 | 12 | ROE ROA GrossMargin Accrual AssetTurn |
| growth 成长 | 12 | Sales_G_{q,ttm,3y} Profit_G_* OCF_G_* ROE_G_* |
| technical 技术 | 12 | MACD(10/30/15) DIF DEA PVCorr_* Alpha3/13/15/16/44/50/55 |
| volatility 波动率 | 10 | std_4m id1_std_3m id2_std_* high_r_std_4m Beta |
| momentum_modified 改进动量 | 8 | wgt_return_* exp_wgt_return_* |
| leverage 杠杆 | 5 | DebtToAssets FinLeverage DebtToEquity CashRatio |
| momentum 动量 | 5 | Return_1m/3m/6m/12m HAlpha |
| size 规模 | 3 | LnMktcap |
每个因子带 confidence 等级(研报确证 / 强推断 / 社区推断 / 存疑)。
按证据强度筛选:
REGISTRY.select(min_confidence="confirmed") # 只要原文逐字确证的
REGISTRY.select(categories=["momentum"], live_safe_only=True)
REGISTRY.manifest() # 导出完整清单完整清单见 reports/factor_inventory.csv。
因子层只认 Panel,不认任何厂商 API。两种接入方式:
A. 导出 parquet(推荐,之后研究全离线可复现)
data/raw/close.parquet # date x asset,后复权
data/raw/turnover.parquet
data/raw/mktcap_total.parquet
data/raw/fundamentals/ocf_ttm.parquet
data/raw/disclosure.parquet # 报告期 x asset 的披露日期
data/raw/benchmark.parquet
所需字段不必猜 —— 从因子声明反推:
specs = REGISTRY.select(categories=["growth"])
print(REGISTRY.required_fundamentals(specs))B. 实现 DataLoader,接 Wind / Tushare / 自建数仓:
from quant.data import DataLoader, DataRequest
class MyLoader(DataLoader):
def load_raw(self, req): ...
def load_fundamentals(self, req): ...固定顺序,写在 config.ProcessingConfig:
去极值(MAD, k=5,不乘 1.4826) → Z-score → 缺失值填 0
三个易错点已在代码中显式化并注释:
- MAD 不乘 1.4826(乘了阈值放宽约 48%)
- 缺失值在标准化之后填 0
- 市值中性化只在算 IC 时做,回归法只加行业哑变量
必需:numpy、pandas(仅此二者即可完成因子计算、数据集构建、回测)。
可选:lightgbm(惰性导入,训练时才需要)
python -m pip install lightgbm -i https://pypi.tuna.tsinghua.edu.cn/simple刻意不依赖 scipy:RankIC 用 numpy 的「秩上 Pearson」实现,避免核心指标受可选包影响。
见 docs/ARCHITECTURE.md §7,要点:
原清单「72」与图片小计 70 差 2 的来源;创业板/科创板涨跌停幅度需按板块配置;
杠杆类 5 个中 4 个无研报原文确证。