> Source: https://ashareapi.com/docs/guides/factor-screening/  ·  Markdown version for LLMs / AI agents

教程

# A 股因子选股怎么做：用一个接口筛出低估高 ROE 的股票
 因子选股 = 把财务与行情指标写成条件，让接口全市场筛一遍。本文讲清三件事：表达式怎么写、结果怎么读、为什么筛完必须做一次数据体检（否则很容易把异常值当机会）。

## 一、因子选股是什么（30 秒版）
 **因子**就是一个可计算的指标：`PE_TTM`（市盈率）、`ROETTM`（净资产收益率）、`DividendRatioTTM`（股息率）、`DebtAssetsRatio`（资产负债率）……**因子选股**就是把这些指标写成条件，让接口从全市场 5000 多只里筛出同时满足的股票。
 它替代的是"手动翻财报"——把「低估值 + 高盈利 + 低负债」这类想法，变成一条可复现、可回测的规则。

-
 **可复现**：同一条件任何时候跑，结果口径一致（人工翻财报做不到）

-
 **可回测**：配合 `--date` 可以拿历史某天的因子值筛，验证"当时选出来会怎样"

-
 **不构成投资建议**：因子选股只做"客观条件筛选"，选出来不等于该买（本文只讲怎么取数与验证）

## 二、最快上手：一行请求筛出低估高 ROE
 `/v1/screen` 是付费端点（需要 Key）。最常用的写法是 `expr` 多因子交集——`intersect([...])` 表示**同时满足**括号内所有条件。

 Python：筛「PE 在 0~20 之间 + ROE 大于 15%」，按 ROE 降序取前 5
```
import requests

BASE = "https://api.ashareapi.com/v1"
H = {"Authorization": "Bearer ct-你的Key"} # Key 见 https://ashareapi.com/pricing

r = requests.get(
 f"{BASE}/screen",
 params={
 "expr": "intersect([PE_TTM > 0, PE_TTM < 20, ROETTM > 15])",
 "orderby": "ROETTM",
 "desc": "1",
 "limit": 5,
 },
 headers=H,
 timeout=30,
)
body = r.json()
if not body.get("ok"):
 raise RuntimeError(body) # 上游取数失败会返回 ok=false（不扣次数）

for row in body["structured"]: # structured = 给代码用的结构化结果
 print(row["code"], row["name"], row["PE_TTM"], row["ROETTM"])
```

-
 **`PE_TTM > 0` 是必须的前置条件** —— 亏损公司的 PE 为负，不加会把它们当"低 PE"筛进来

-
 **百分数字段直接写数字**：`ROETTM > 15` 就是"大于 15%"（不要写 `0.15`）

-
 **字段值是字符串**（`"14.33"`）—— 要算数先 `float()`，否则 `+` 会拼字符串

## 三、结果长这样（2026-09-29 实跑）
 下面是上面那条请求的真实返回（`limit=5`），`structured` 字段直接就是二维结果：
 |
| | code | name | PE_TTM | ROETTM | ClosePrice | ChangePCT

| | sz000656 | 金科股份 | 0.36 | 883.0546 | 1.25 | 3.31

| | sz300972 | 万辰集团 | 14.33 | 77.2634 | 151.22 | -2.81

| | sh600132 | 重庆啤酒 | 15.54 | 73.3501 | 37.30 | -0.80

| | sh688331 | 荣昌生物 | 11.28 | 70.0198 | 116.31 | 0.84

| | sz001309 | 德明利 | 12.32 | 69.7845 | 370.69 | -0.16

 sz000656
 金科股份
 0.36
 883.0546
 1.25
 3.31

 sz300972
 万辰集团
 14.33
 77.2634
 151.22
 -2.81

 sh600132
 重庆啤酒
 15.54
 73.3501
 37.30
 -0.80

 sh688331
 荣昌生物
 11.28
 70.0198
 116.31
 0.84

 sz001309
 德明利
 12.32
 69.7845
 370.69
 -0.16

 **先别急着高兴** —— 第一行那个 `PE_TTM=0.36`、`ROETTM=883%` 就是典型的需要体检的对象。下一节讲怎么查。

## 四、筛完必做的 3 步数据体检（重要）
 **因子值是"机器算出来的数字"，不等于"公司真实经营状况"。**遇到极端值（PE 极低 / ROE 极高 / 增速极大）时，先用同一份财报里的其他字段做交叉验证——三步就能筛掉大部分干扰：

 Python：对筛出的股票做体检（查扣非 / 多口径 / 杠杆）
```
def health_check(code: str) -> None:
 # 对单只股票做因子体检：看多口径 ROE + 扣非 + 杠杆 + 当期净利
 r = requests.get(f"{BASE}/finance", params={"code": code, "limit": 1},
 headers=H, timeout=30)
 tables = r.json()["data"] # finance 返回 3 张表（利润表/资产负债表/现金流）
 income, balance = tables[0][0], tables[1][0]

 print(f"{code}")
 print(" ROE 多口径:", income.get("ROE"), "/", income.get("ROETTM"),
 "/", income.get("ROEWeighted"), "/ 扣非", income.get("ROECut"))
 print(" 归母净利 当期:", income.get("NPParentCompanyOwners"),
 " TTM:", income.get("NPParentCompanyOwnersTTM"))
 print(" 负债率:", balance.get("DebtAssetsRatio"), "% 股东权益:",
 balance.get("TotalShareholderEquity"))

 # 体检规则（示例，非投资建议）
 try:
 if float(income.get("ROECut") or 0) < 0:
 print(" [!] 扣非 ROE 为负 —— 利润可能来自非经常性损益")
 if float(balance.get("DebtAssetsRatio") or 0) > 70:
 print(" [!] 负债率高于 70% —— ROE 可能被杠杆放大")
 except (TypeError, ValueError):
 pass

health_check("sz000656")
```

-
 **① 看扣非（最重要）**：`ROECut`（扣非 ROE）与 `NPDeductNonRecurringPL`（扣非归母净利）。若 `ROETTM` 很高但 `ROECut` 为负 → 利润主要来自**非经常性损益**（卖资产/债务重组等），不代表主营赚钱

-
 **② 看多口径是否一致**：同一份财报有 `ROE` / `ROETTM` / `ROEWeighted` / `ROECut` 多个口径。若它们**量级差很多**（如 0.44 vs 883）→ 说明其中一个口径失真，别直接采用

-
 **③ 看杠杆与当期**：`DebtEquityRatio` / `DebtAssetsRatio`（高杠杆会放大 ROE）+ `NPParentCompanyOwners`（当期归母净利）。若 TTM 净利与当期净利**严重脱节**，TTM 值可疑

## 五、真实案例：那个 883% 的 ROE 是怎么回事
 上面筛出的 `sz000656 金科股份`（PE_TTM 0.36 / ROETTM 883%）——跑一遍体检就能看出问题。同一份财报里：

-
 **判据不是"我觉得"**：同一份报表里 `ROE=0.44` 与 `ROETTM=883` 相差 2000 倍、扣非为负、TTM 与当期净利脱节 —— **多口径互相矛盾**，说明 `ROETTM` 这个值不可直接采用

-
 **这类情况的正确处理**：不是"这只股票有问题"，而是"**这个因子值不可靠**" —— 换口径（用 `ROECut`）或直接跳过

-
 **这正是为什么体检是必须的**：接口如实返回了各口径数值，是否需要剔除由使用者按规则判断（我们不给买卖结论）

 |
| | 字段 | 值 | 含义

| | ROETTM | 883.05 | 选股用的那个（极高）

| | ROE | 0.44 | 同一指标另一口径（相差约 2000 倍）

| | ROECut | -1.71 | 扣非 ROE（负 = 扣非亏损）

| | NPParentCompanyOwnersTTM | 368.7 亿 | 归母净利 TTM

| | NPParentCompanyOwners | 0.19 亿 | 当期归母净利（与 TTM 严重脱节）

| | DebtEquityRatio | 200.67% | 产权比率（高杠杆）

| | ClosePrice | 1.25 元 | 股价（面值附近）

 ROETTM
 883.05
 选股用的那个（极高）

 ROE
 0.44
 同一指标另一口径（相差约 2000 倍）

 ROECut
 -1.71
 扣非 ROE（负 = 扣非亏损）

 NPParentCompanyOwnersTTM
 368.7 亿
 归母净利 TTM

 NPParentCompanyOwners
 0.19 亿
 当期归母净利（与 TTM 严重脱节）

 DebtEquityRatio
 200.67%
 产权比率（高杠杆）

 ClosePrice
 1.25 元
 股价（面值附近）

## 六、常用因子速查（写表达式最常用的那些）
 |
| | 类别 | 因子 | 含义 | 单位

| | 估值 | PE_TTM | 市盈率 TTM（需 > 0） | 倍

| | 估值 | PB | 市净率（无 PB_TTM） | 倍

| | 估值 | PS_TTM | 市销率 TTM | 倍

| | 估值 | TotalMV | 总市值 | 元

| | 估值 | DividendRatioTTM | 股息率 TTM | %

| | 盈利 | ROETTM | 净资产收益率 TTM | 百分数

| | 盈利 | ROECut | 扣非 ROE（体检用） | 百分数

| | 盈利 | GrossIncomeRatioTTM | 毛利率 TTM | 百分数

| | 成长 | NPParentCompanyYOY | 归母净利同比 | %

| | 成长 | OperatingRevenueGrowRate | 营收增速 | %

| | 杠杆 | DebtAssetsRatio | 资产负债率 | %

| | 杠杆 | DebtEquityRatio | 产权比率 | %

| | 现金流 | NetOperateCashFlowTTM | 经营现金流 TTM | 元

 估值
 PE_TTM
 市盈率 TTM（需 > 0）
 倍

 估值
 PB
 市净率（无 PB_TTM）
 倍

 估值
 PS_TTM
 市销率 TTM
 倍

 估值
 TotalMV
 总市值
 元

 估值
 DividendRatioTTM
 股息率 TTM
 %

 盈利
 ROETTM
 净资产收益率 TTM
 百分数

 盈利
 ROECut
 扣非 ROE（体检用）
 百分数

 盈利
 GrossIncomeRatioTTM
 毛利率 TTM
 百分数

 成长
 NPParentCompanyYOY
 归母净利同比
 %

 成长
 OperatingRevenueGrowRate
 营收增速
 %

 杠杆
 DebtAssetsRatio
 资产负债率
 %

 杠杆
 DebtEquityRatio
 产权比率
 %

 现金流
 NetOperateCashFlowTTM
 经营现金流 TTM
 元

 完整 85 个因子（估值 6 / 行情 2 / 盈利 38 / 资产负债 32 / 现金流 14）与 14 个组合模板 → 见《因子表达式语法与因子字典》。

## 常见问题
 因子选股的结果能直接用来买吗？
 不能。因子筛选只是"按客观条件过滤全市场"，选出来的是"满足条件"而不是"值得买"。它替代的是人工翻财报的第一步（初筛），后续仍需自己判断。本文只讲怎么取数与验证，不构成投资建议。

 为什么筛出来的 PE 那么低、ROE 那么高，看着像捡钱？
 极端值往往来自口径问题。用同一份财报里 `ROECut`（扣非）、`ROE`/`ROEWeighted`（其他口径）、`DebtEquityRatio`（杠杆）交叉验证——多口径互相矛盾时，该因子值不可采用。第 4、5 节给了完整方法与真实案例。

 免费能用因子选股吗？
 不能，`/v1/screen` 属付费端点（需 Key）。免费的 5 个端点是 quote / kline / hot / market-overview / changedist——可以先拿免费端点验证"怎么调通"，再决定是否用选股。

 字段为什么是字符串？
 接口为了让精度不丢失，数值型字段以字符串返回（如 "14.33"）。比较、排序、计算前请先 float()——直接相加会变成字符串拼接。

 最后更新: 2026-09-29
 字段与方案清单取自 ashareapi /v1/screen 与 /v1/finance 的线上真实返回（2026-09-29 实测）；因子释义与体检方法来自实测验证记录（85 因子，2026-08-31）。

 相关

-
[因子表达式语法与因子字典](/docs/guides/screen-expressions)

-
[22 个现成的选股方案（低估值/高股息/高ROE）](/docs/guides/stock-screening-strategies)

-
[实战：价值投资筛选与异常值排查](/docs/guides/value-stock-screening)

-
[端点参考（screen）](/docs/endpoints/screen)

 [← 全部教程](/docs/guides)
