ZVT 项目研究站 · 取数与查询契约

ZVT 取数与查询:record_data 写进本地,query_data 从本地查出来

ZVT 和「每次调一次接口」的工具最本质的差别,就在这两个方法上:所有数据先通过 record_data 落到本地库(默认增量更新),再由 query_data 在同一套参数下查出来。理解这个顺序,才能理解为什么它适合做可复现的因子研究,也才能避开「不传 code 就是全市场」这类默认行为。

写入:record_data查询:query_data依据 README 与 src/zvt/contract 源码(2026-09-18)

先写库,再查询

providerem / joinquant / sina…
record_data落盘 + 增量更新
本地库zvt_home/data
query_datafilters / order / limit
据 README 的数据章节与 src/zvt/contract/ 的接口命名自绘(非官方架构图);参数名逐字取自 README 示例。
record_data

ZVT 的 record_data:写进本地库的统一入口

所有 schema 共用这一个方法。下表把 README 里出现过的参数与行为集中起来——官方是分散在示例里的。

参数 / 行为含义适用场景注意点
provider选择用哪个数据源写入某个源不可用、或想换一家口径时同一个 schema 可能注册多个 provider,不传时用第一个;各家数据口径不完全一致
code / codes写单只 / 多只标的小范围验证、补单只数据不传就写全市场,第一次跑全市场耗时长且容易触发限流
entity_ids用实体 id 指定标的(如 stock_sz_000001标的来自其它表的查询结果时与 code 二选一,混用时以实体 id 为准
sleeping_time每次请求之间的间隔秒数批量写库时降速,避免被数据源限流README 的策略示例里用的是 1 秒;全市场建议适当放大
增量更新已存在的数据不重复拉取,只补新增部分日常定时更新想强制重写需要另行处理;不要靠重复运行来「修复」脏数据
本地落盘数据写入 zvt_home/data 等路径下想在另一台机器复用、或做大范围备份时路径由 zvt_env 决定,可配置;换机器后要迁移整个数据目录
from zvt.domain import Stock, Stock1dHfqKdata Stock.record_data(provider="em") Stock1dHfqKdata.record_data(provider="em", entity_ids=["stock_sz_000001", "stock_sz_000338"], sleeping_time=1)
这段代码的两层含义:第一行不带任何标的参数,写的是全市场标的清单;第二行只写两只股票的后复权日线,并显式降速。示例代码取自官方 README 的「Behold, the power of zvt」小节。
query_data

ZVT 的 query_data:把条件交给本地库,而不是交给网络

查询全部打在本地数据上,所以可以放心写复杂条件与排序。

参数含义用法示例注意点
filters条件列表,元素为「字段 比较 值」的表达式[FinanceFactor.roe>0.08, FinanceFactor.report_period=='year']多个条件是并列关系;字段名以 schema 定义为准,Schema.help() 可以看到全部列
start_timestamp起始时间,支持字符串日期start_timestamp='2019-01-01'对财报类数据,这里筛的是「公告/记录时间」而不是报告期,口径差异会直接改变结果
order排序,支持升序与降序order=FinanceFactor.roe.desc()limit 搭配就是「取前 N」;不排序时取前 N 没有意义
limit限制返回行数limit=20只影响返回,不影响库里的数据
columns指定返回列columns=["code"] + FinanceFactor.important_cols()important_cols() 可以只取关键列,避免宽表拖慢分析
index把某一列设为返回 DataFrame 的索引index='code'index='timestamp'不同 index 会影响后续与价格表对齐的方式,做因子前先想清楚
provider按数据来源过滤查询时限定某一家的记录同一张表可能混有多个 provider 写入的数据,做一致性检查时要用它拆开
df = FinanceFactor.query_data( filters=[FinanceFactor.roe > 0.08, FinanceFactor.report_period == 'year', FinanceFactor.op_income_growth_yoy > 0.08], start_timestamp='2019-01-01', order=FinanceFactor.roe.desc(), limit=20, columns=["code"] + FinanceFactor.important_cols(), index='code')
上面这条是 README 的「市场级选股」示例:ROE 大于 8% 且营收增速大于 8% 的年度报告,按 ROE 倒序取前 20 只。它同时演示了 filters、排序、limit、columns 与 index 五个参数,是理解 query_data 最完整的一个例子。示例结果里的 ROE 数值出现了明显异常值(个别标的 ROE 大于 1),官方未做解释——做横截面排序时建议自己先做极值检查。
Data home

ZVT 的数据落在哪里:zvt_env 的五个路径

README 的 Env settings 章节给出了一台示例机器上的五个路径设置。换机器、换盘符、做备份时都要先看它。

含义会用到的场景注意点
zvt_home框架的工作根目录迁移数据、做整目录备份它是下面四个路径的父级,迁移时整个目录一起搬
data_path数据文件目录确认数据是否真的落盘写库后最直接的验证点;空目录说明写库没成功
tmp_path临时文件目录排查磁盘占用异常中断后可能留残留文件
ui_path界面相关资源目录界面起不来时检查zvt 命令有关,只用 API 时可忽略
log_path日志目录取数失败时看真实报错排错第一现场:报错原文通常在这里而不是在控制台
配置写入方式:README 给出 init_config(current_config=zvt_config, jq_username='xxx', jq_password='yyy') 这样的写法,可写聚宽账号、SMTP、微信相关配置。这些值最终落在框架自己的配置文件里,不要把真实密钥提交到代码仓库
Workflow

一次完整取数:从空库到可查询的五步

按这个顺序走,可以把「网络问题」和「代码问题」分开定位。

  1. 先看配置:代理与 provider 凭据

    打开框架的 config.json,确认 http_proxy / https_proxy 是否符合本机情况(默认值是本地代理 127.0.0.1:1087),以及要用到的 provider 是否需要账号。预期:明确知道这次取数走的是哪家源、需不需要登录信息。

  2. 写标的清单:Stock.record_data(provider="em")

    先拿小样本验证:给 codeentity_ids 传一只标的,确认链路通,再考虑全市场。预期:data_path 下出现数据文件,日志目录没有异常堆栈。

  3. 写行情:Stock1dHfqKdata.record_data(...)

    周期与复权在表名里选(1dhfq 的含义见 K 线与复权页)。预期:同一只标的可以查到连续日期序列。

  4. 查询验证:query_data 加条件与排序

    filters 加一个明确条件(例如某只标的),再用 index='timestamp' 看时间轴是否连续。预期:返回行数与预期交易日数接近;缺口说明写库没完成。

  5. 再放大到全市场

    确认链路与时间成本后,再去掉 code 参数写全市场,并在批量写库时设置 sleeping_time。预期:耗时显著变长;期间应关注日志里的限流与重试信息。

Decision

什么时候该重跑、什么时候不该重跑

增量更新是这套设计的优点,也是误解最多的地方。

情形建议动作理由注意点
每天新增一个交易日的数据直接重跑同一行 record_data增量更新会只补新增部分建议交给定时任务,见标签与任务页的 runner
发现某段历史数据缺失先用 query_data 确认缺口范围,再针对性补写盲目全量重跑既慢又可能覆盖其它来源的数据同一张表可能混有多个 provider 的记录
换了一家 provider按 provider 分别查询后再对比两家口径不同,混在一起做因子会失真provider 参数拆分,不要直接看平均值
升级了框架版本先备份整个 zvt_home 再升级README 明确不保证向后兼容,表结构可能变化仓库里的 sql/ 目录有几个手工维护脚本,说明存在需要人工干预的库变更
数据源接口变更等 provider 侧修复或自行改 recorder取数依赖第三方站点结构这类问题不在框架可控范围内,先看 issue 再动手
FAQ

取数与查询常见问题

答案以 README 与 src/zvt/contract/ 的接口定义为准;本站未运行过这些代码。

不传 code 会怎样?

按 README 的说明,不传 codecodes 时会记录全市场。这在标的清单这类小表上没问题,但在日线这类大表上会跑很久并可能触发数据源限流。建议先传单只标的验证链路,确认无误后再放开。

数据存在哪?能换盘吗?

默认在框架自己的 zvt_home 下,数据文件放在 data_path。README 的 Env settings 展示了这些路径是可配置的,换盘或迁移时把整个目录一起搬走即可。具体配置方式以源码为准。

为什么查出来的行数和交易日对不上?

常见三种原因:写库没跑完(增量只补了一部分)、该标的期间停牌、或者 start_timestamp 筛的是记录时间而不是行情日期。先按 index='timestamp' 看时间轴的缺口位置,再决定补数据还是改查询条件。

能像数据库一样直接写 SQL 吗?

框架暴露的是 record_data / query_data 这一层,底层是 SQLAlchemy 2.0。理论上可以绕过框架直接用引擎,但那样会失去增量更新与统一口径,升级时也更容易踩到表结构变化。sql/ 目录里的脚本是官方自己用于库维护的,不是给用户日常使用的接口。

取数很慢怎么办?

三件事按顺序做:①确认不是默认代理导致的重试;②给批量写库加 sleeping_time 降速,避免被限流后反复重试;③把「全市场」拆成多个小批次。框架本身没有分布式抓取能力,性能上限取决于数据源。

这套流程和 EasyClaw 的取数技能是什么关系?

两条并列路线。技能(akshare-finance、tushare-finance、mx-data 等)是即取即用、结果一次性返回,不建本地库;ZVT 是先落库再查询,适合反复跑因子与回测。两者无已证实集成,具体取舍见「对比」页。

数据在本地之后,下一步是把它变成因子

取到数据只是原料。ZVT 的真正设计点在于把多标的数据组织成二维索引的 DataFrame,再在同一套结构上算因子、出选股结果。