ZVT 项目研究站 · 因子与选股

ZVT 因子与选股:三层 DataFrame 把「多标的一次算完」变成默认写法

大多数框架里,算 3000 只股票的指标要么写循环、要么写向量化技巧;ZVT 把这件事放进了数据结构本身:因子层的 DataFrame 以 (entity_id, timestamp) 为二维索引,多标的与单标的是同一个写法。理解 data_dffactor_dfresult_df 这三层,就理解了它的因子设计。

因子模块:factors 目录 19 个文件结果形态:filter_result 或 score_result依据 README 与因子源码(2026-09-18)

三层结构,各加什么信息

data_df从 schema 读原始行情
factor_df经 Transformer 加指标列
result_dffilter_result / score_result
TargetSelector变成选股结果
据 README 因子章节的输出块自绘(非官方架构图);列名 diff / dea / macd、filter_result、score_result 均逐字取自官方示例。
Three layers

ZVT 的三层 DataFrame:每一层多了什么、该怎么读

官方 README 用一次会话演示了这三层,但没有汇总成表。下表按输出整理。

来源索引与形态关键列怎么用 / 注意点
data_df由因子对象从 schema 读入(query_data 的结果)二维索引 (entity_id, timestamp)open / close / high / low / volume / level / id只读检查:确认标的数与时间范围符合预期,再往下算
factor_df对 data_df 施加 Transformer(如 MacdTransformer(count_live_dead=True)同样二维索引,列被扩充MACD 例:diff / dea / macd中间层:想复用中间量(例如只用 DIF)时直接取这一层,避免重算
result_df由 factor_df(或 data_df)按规则计算二维索引,通常单列或多列布尔/评分filter_result(True/False)或 score_result(0 到 1)两种结局二选一:筛选型给布尔,评分型给 0–1 分数;文档未统一,按具体因子的实现为准
TargetSelector消费 result_df按时间切片选取标的——把「哪些标的在哪些时点命中」变成每期选股集合;README 的选股与回测流程图就停在这里
为什么这样设计值得注意:二维索引意味着你不需要为「多标的」单独写循环——同一段 ZVT 代码传 1 只或 3000 只标的,结构不变。这也是它和「单标的指标库」最本质的区别;代价是内存占用随标的数×时间跨度线性增长,全市场长周期要提前估算。
Modules

ZVT 的 factors 模块里到底有什么(29 个 Python 文件、4 个子包)

官方文档没有因子清单。下表按 src/zvt/factors/ 的实际目录结构归类(共 29 个 .py,分在 ma / macd / zen / fundamental 四个子包与根目录),方便你判断有没有现成可用的东西。

类别文件作用什么时候直接用它注意点
基础设施base_factorfactor_modelsfactor_servicecommon因子基类、数据模型与服务封装写自定义因子时继承基类扩展前先读基类对 data_df / factor_df / result_df 的约定
技术指标technical_factormacd_factorma_factorma_stats_factorshapeMACD 类、均线类、均线统计类与形态类因子做常见技术面选股参数含义以源码为准,文档未逐一说明
基本面finance_factor基于财务指标构造因子ROE、增长率一类的基本面筛选要处理财报的时间轴口径,见 K 线页的时间对齐提醒
特定标的因子stock_1d_ma_factorstock_1d_ma_stats_factorstock_1d_zen_factorstock_1wk_zen_factorindex_1d_zen_factorzen_factor已绑定到具体标的类型与周期的成品种子因子想要「开箱可用」的日线/周线因子时绑定死周期与标的类型,换场景要自己改
选股与排序target_selectortop_stockstop_bottom_factor把因子结果变成标的集合或头部/尾部标的构造股票池、做多空分组与 result_df 的两种形态配合使用,注意哪一类因子给的是评分
变换器transformers把 data_df 变成 factor_df 的计算器集合只想加一个附加列而不建完整因子时示例里 MACD 变换器带 count_live_dead 参数(统计金叉死叉)
算法algorithm横向算法类因子需要跨标的排序类因子时依赖全市场数据,数据不全时结果会失真
Workflow

从原始数据到一个选股条件的五步

下面用 README 的 MACD 示例串一次完整链路,每一步都写清输入、输出与判断点。

  1. 确认数据已在本地

    ZVT 的因子层不会替你取数:先按取数页的步骤把标的清单与对应周期的 K 线写进本地。预期:查询能返回连续序列;缺数据时因子计算结果里会出现大片空值。

  2. 构造因子对象

    from zvt.factors import BullFactor
    factor = BullFactor(codes=['000338', '601318'],
                        start_timestamp='2019-01-01',
                        end_timestamp='2019-06-10',
                        transformer=MacdTransformer(count_live_dead=True))

    说明:codes 决定标的范围,时间范围决定计算窗口。预期:对象内部完成 data_df 的读取。

  3. 检查 data_df 与 factor_df

    先看 factor.data_df 的行数是否等于「标的数 × 交易日数」,再看 factor.factor_df 是否多出指标列(MACD 例为 diff / dea / macd)。预期:前几行指标列为 NaN(窗口不足),这是正常现象。

  4. 读取 result_df,确认结果形态

    示例里 factor.result_df 只有一列 filter_result,取值 True/False。若使用评分型因子,则会看到 0 到 1 的 score_result。预期:布尔列里 True 的占比决定选出多少标的。

  5. 交给 TargetSelector 或策略

    选股场景把 result_df 交给 selector 得到每期标的集合;回测场景在 StockTrader.on_time 里调用 trade_the_targets。预期:得到可复现的选股记录,而不是手工挑出来的名单。

提醒:上例的 BullFactor 与两个代码都来自 ZVT 官方 README 的示例,不是收益证明。本站不提供任何因子有效性、胜率或绩效数字(见证据台账 X-01)。
Decision

用现成因子还是自己写:四种情形的取舍

框架给了成品种子因子,但它们的绑定很具体,先看表再决定要不要自己写。

你的需求建议做法理由注意点
快速验证一条技术面选股逻辑technical_factor 一族已有 MACD、均线、形态类实现,省去重复造轮子参数与阈值以源码为准,先用小样本跑通
标准基本面筛选finance_factor + query_data 条件财务表的 important_cols 能直接拼出常用口径注意财报时间轴选择(公告日 vs 报告期)
自定义因子(举例:量价背离)继承 ZVT 的因子基类,自己实现 Transformer 与 result_dfZVT 的扩展点在 factors/base_factor.py保持二维索引结构,不要退回 pandas 单标的口径
只想加一个中间列直接用 transformers 里的计算器比建完整因子对象轻注意变换器是否接收并保留 count_live_dead 这类参数
需要横截面排序类因子algorithm 或自行按时间分组排序横截面因子必须依赖全市场数据数据不全时排序结果会系统性失真
Pitfalls

四个容易踩在因子层的坑

这四条都能从 README 的示例输出里看出来,但官方没有集中提示。

用错复权口径,因子会整体偏移

ZVT 的因子建立在 K 线表上,而默认表与后复权表的价格量级不同(README 示例中同一日期差异接近一个数量级)。技术指标对价格绝对量级敏感,混用口径会让因子值失去可比性。

前几行永远是 NaN,不是数据缺失

MACD 这类指标需要预热窗口,示例输出里前五个交易日的 diff / dea / macd 都是 NaN。把 NaN 当作「数据没取到」而重复写库,是常见的无效劳动。

result_df 的形态不止一种

README 说明 filter_result 是 True/False,score_result 是 0 到 1。写下游代码前先打印一列看一眼形态,否则拿布尔列当评分排序会得到无意义的结果。

横截面因子的样本要足够宽

做排序类因子时如果只取了几十只标的,排名几乎没有统计意义。官方示例本身只用两三只标的做演示——那是为了说明写法,不是推荐的研究规模。

证据边界:本页描述的三层结构、列名与参数均来自 README 的输出与 src/zvt/factors/ 的文件清单;本站未运行因子计算,不提供任何数值结果。
FAQ

因子与选股常见问题

答案以 README 与因子源码为准;涉及效果的问题本站一律不答。

为什么 ZVT 的因子要按二维索引组织?

因为它要一次处理多个标的:以 (entity_id, timestamp) 为索引后,「一只股票的一段行情」和「三千只股票的同一段行情」在结构上没有区别,选股与回测可以直接在这张表上做切片。代价是内存占用随规模增长。

result_df 到底是布尔还是评分?

两者都有。README 明确写 filter_result 为 True 或 False,score_result 为 0 到 1。具体某个因子给哪一种取决于它的实现;写下游逻辑前先打印一列确认。

可以用 ta-lib 或其他指标库吗?

ZVT 自带 transformers 与一组技术因子,但没有把 ta-lib 列为依赖(19 项依赖里没有它)。想引入外部指标库需要自己在 transformer 层做适配,并保持二维索引的输出结构。

因子算完怎么变成选股名单?

README 的流程是:result_df 交给 TargetSelector 得到标的集合,再由策略按其交易。TargetSelector 的职责就是「按时间切片把命中的标的选出来」,这也是它和普通 DataFrame 操作的区别。

只有几十只股票的样本能算横截面因子吗?

可以算,但排序类因子的意义不大:排名需要足够宽的横截面才有统计意义。官方示例用两三只标的只是演示写法。做真实研究时先确认样本范围,再决定用时序因子还是横截面因子。

这套因子流程和 EasyClaw 技能能配合吗?

没有已证实集成。本机的 quant-analyst 技能提供的是量化研究方法论与风险指标口径(夏普、最大回撤、组合优化等),它不实现 ZVT 的三层结构,也不能调用 ZVT 的因子类。两者是并列路线,取舍见「对比」页。

因子之后就是策略与结果呈现

因子给出「哪些标的在哪些时点命中」,策略决定「怎么买卖」,而 ZVT 自带两个界面供你看结果——注意它们的现状与 README 截图里的印象并不完全一致。