【量化系统从零构建 #12】选股引擎(上):因子库
系列:《量化系统从零构建》|连载项目 · 纯 GET 取数 · 仅依赖 requests
适用:想在落库数据上搭建选股因子的读者;本篇定义估值 / 盈利 / 成长 / 质量 / 资金流 / 动量六类因子的「取值 + 方向」,为 #13 打分排序准备原材料,不依赖任何行情终端,也不发起网络请求。
1. 你将得到什么
- FACTORS 因子注册表:每个因子 =(取值函数,方向)。方向 1 越高越好,-1 越低越好。
- raw_factors:从一只股票的快照(来自 #06 基本面 / #07 资金流 / 动量计算)取出原始因子值,缺值记 None。
- 本篇交付:因子层原材料,#13 在其上做行业中性化与综合打分、生成股票池。
2. 本篇取数约定
本篇是策略层,不发起网络请求。因子原始值来自已落库的数据:估值/盈利/成长/质量来自 #06 基本面表,资金流来自 #07 money_flow,动量由 #10 收益序列算得。本篇只定义「怎么取、方向如何」。
3. 因子一览
| pe | 估值(基本面) | 低好(-1) |
| roe | 盈利(基本面) | 高好(1) |
| rev_yoy | 营收增速(成长) | 高好(1) |
| gross_margin | 毛利率(质量) | 高好(1) |
| net_inflow | 主力净流入(资金流) | 高好(1) |
| mom_ret | 动量收益率(指标) | 高好(1) |
方向统一为「综合分越高越看好」,低好因子在 #13 打分时会反向。
4. 核心模板函数
import sys
# 因子注册表:(取值函数, 方向) 方向 1=越高越好, -1=越低越好
FACTORS = {
"pe": (lambda r: r.get("pe"), –1),
"roe": (lambda r: r.get("roe"), 1),
"rev_yoy": (lambda r: r.get("rev_yoy"), 1),
"gross_margin": (lambda r: r.get("gross_margin"), 1),
"net_inflow": (lambda r: r.get("net_inflow"), 1),
"mom_ret": (lambda r: r.get("mom_ret"), 1),
}
def raw_factors(snapshot):
"""取某股票快照的原始因子值;缺值记 None。"""
return {name: fn(snapshot) for name, (fn, _) in FACTORS.items()}
def run_check():
# 合成快照,非真实行情
snap = {"code": "000001.SZ", "pe": 8, "roe": 0.15, "rev_yoy": 0.2,
"gross_margin": 0.4, "net_inflow": 1.2e8, "mom_ret": 0.1}
f = raw_factors(snap)
assert f["pe"] == 8 and f["roe"] == 0.15 and f["mom_ret"] == 0.1
assert f["net_inflow"] == 1.2e8
# 方向校验:低好因子方向为 -1
assert FACTORS["pe"][1] == –1 and FACTORS["roe"][1] == 1
# 缺值记 None
assert raw_factors({"code": "X"})["pe"] is None
print("校验通过")
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "–check":
run_check()
else:
print("本篇定义因子库,不发起网络;因子原始值来自 #06/#07 落库与 #10 动量计算。")
5. 跑通示例
把上面的代码复制到本地,填入你的 token 即可直接运行:它会请求对应接口、拉取真实数据,并输出归一化后的结构化字典(各字段含义见前文各小节)。
6. 坑与注意事项
7. 小结与下篇预告
本篇把六类因子收口成注册表,统一「取值 + 方向」。原材料齐了。
下一篇计划写 #13《选股引擎(下):因子打分·排序·分位 + 股票池日更 + 行业中性》:在 raw_factors 上做行业组内分位、综合打分、选 TopN,产出每日股票池。
8. 免责声明
本文仅演示因子定义的用法,所有代码示例均以合成数据校验,未含任何真实数据;文中示例数据仅作演示用途,不构成投资建议,亦不承诺收益。



