ZVT 项目研究站 · 财务数据
ZVT 财务数据:四个对象、两套时间字段,怎么取怎么读
ZVT 的财务面由四个 schema 承载:财务指标 FinanceFactor、资产负债表 BalanceSheet、利润表 IncomeStatement、现金流量表 CashFlowStatement。它们的取数方式与行情完全一样(record_data / query_data),真正的难点在时间字段:同一张表里同时有 report_period 与 report_date,用错哪一个,回测就会出现未来函数。
四个财务对象与两套时间字段
src/zvt/domain/fundamental/finance.py(非官方架构图);report_period 为 String 列、report_date 为 DateTime 列。ZVT 的四个财务对象:各自装什么、怎么用
下表按 ZVT 源码里的类定义整理。四个类的用法完全一致,差别只在字段覆盖面。
| 对象 | 内容 | 典型用法 | 适用场景 | 注意点 |
|---|---|---|---|---|
FinanceFactor | 财务指标:每股收益、营业总收入、净利润、营收/净利同比增速、ROE、ROTA、毛利率、净利率等 | FinanceFactor.record_data(code='000338') 后 query_data(columns=FinanceFactor.important_cols()) | 做基本面筛选与财务因子 | 表名 finance_factor;important_cols() 返回 9 列,日常分析先取这 9 列 |
BalanceSheet | 资产负债表 | 与指标表同样的 record_data / query_data | 看资产负债结构、杠杆 | 字段多、口径细,建议按 important_cols() 收窄再分析 |
IncomeStatement | 利润表 | 同上 | 看收入与利润构成 | 与指标表的数值可能来自不同 provider,注意混用 |
CashFlowStatement | 现金流量表 | 同上 | 看经营/投资/筹资现金流 | 季度与年度记录的可得时间不同 |
Schema.record_data(provider=..., code(s)=...) 写库、Schema.query_data(filters=..., order=..., limit=..., columns=..., index=...) 查询。也就是说你在「取数与查询」页学到的参数在这里原样适用。ZVT 财务字段怎么选:先 important_cols(),再去看全字段
ZVT 的财务表字段很多,官方为每个财务 schema 都提供了 important_cols(),这是最省事的入口。
先取关键列
FinanceFactor.record_data(code='000338') df = FinanceFactor.query_data(code='000338', columns=FinanceFactor.important_cols(), index='timestamp')说明:README 的市场级选股示例就是这么写的。预期输出:以时间为索引、9 列指标的表格。
important_cols() 到底是哪 9 列
按源码
FinanceFactor.important_cols()的定义,依次是:basic_eps(每股收益)、total_op_income(营业总收入)、net_profit(净利润)、op_income_growth_yoy(营收同比)、net_profit_growth_yoy(净利同比)、roe、rota、gross_profit_margin(毛利率)、net_margin(净利率)。要看全字段就 help()
在交互式环境里对 schema 调
FinanceFactor.help()可以列出全部列;也可以直接读源码src/zvt/domain/fundamental/finance.py(约 991 行,四个类都在这个文件里)。横截面选股怎么写
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。预期输出:20 行候选。注意示例结果里出现了 ROE 数值明显异常的行,做排序前建议先做极值检查。
report_period 与 report_date:两套时间字段别混用
这是财务数据最容易出错的地方。源码里这两个字段是并存的,含义不同。
| 字段 | 源码里的类型 | 怎么理解 | 用它做什么 | 用错的后果 |
|---|---|---|---|---|
report_period | Column(String(32)) | 报告期类别,README 示例里按 'year' 过滤 | 筛选「只要年报」「只要季报」 | 它不是日期。拿它当时间轴排序会得到毫无意义的顺序 |
report_date | Column(DateTime) | 该条记录对应的报告日期字段 | 按时间对齐财务数据与行情 | 若实际是「报告期末」而非「公告日」,会提前使用尚未公布的数据 |
timestamp | ZVT 通用时间列 | 查询输出里作为索引出现的时间 | 与其它表做时间对齐时用 | 与 report_date 的关系未在官方文档说明,必须自己核验 |
provider / code | Column(String(32)) | 数据来源与标的代码 | 区分同一张表里来自不同源、不同标的的记录 | 多源混写时不拆开,会出现同一天多条冲突记录 |
report_date / timestamp 究竟等于「报告期末」还是「公告日」,README 全文没有说明,不同 provider 也可能不同。正确做法是自己做一次对照:挑一家你熟悉的公司,把它的年报公告日与库里的 report_date 并排打印出来——对得上就能当公告日用,对不上就必须按公告日重排。这一步做完,才谈得上「没有未来函数」。财务数据能拿到什么、拿不到什么
把 ZVT 财务数据的边界写清楚,比多列几个字段更有用。
| 你想要 | ZVT 的现状 | 依据 | 替代做法 | 注意点 |
|---|---|---|---|---|
| 三大报表 + 常用财务指标 | 四个 schema 都在,且都提供 important_cols() | domain/fundamental/finance.py(约 991 行) | 直接用 | 字段口径以选定 provider 为准 |
| 报告中「本期/上期」等明细行 | 未在 README 中体现 | README 只演示了指标与三张表的取数 | 看源码字段或换 provider 试 | 不要按财务软件的表样预期它的字段 |
| 公告原文、PDF | 不提供 | ZVT 面向结构化数据 | 另找公告数据源 | —— |
| 实时/盘中财务数据 | 不存在这种数据 | 财报本身是定期披露 | —— | 任何声称「实时财报」的说法都要警惕 |
| 自动处理财务数据的可获得性 | 框架不做这件事 | 没有看到自动按公告日重排的说明 | 自己按 report_date / 公告日做 as-of 对齐 | 这是回测能否可信的分水岭,见「点时数据」页 |
ZVT 的财务数据由谁抓:先看 provider 目录里有没有对应 recorder
财务数据能不能取到,取决于某个 provider 是否实现了对应 schema 的 recorder。用之前先看文件在不在。
| 财务对象 | 东财侧的 recorder 文件 | 这意味着什么 | 注意点 |
|---|---|---|---|
FinanceFactor | recorders/eastmoney/finance/eastmoney_finance_factor_recorder.py | 东财侧实现了财务指标抓取 | 财务指标字段多,先取 important_cols() |
BalanceSheet | .../eastmoney_balance_sheet_recorder.py | 资产负债表有独立 recorder | 与利润表字段口径可能不同源 |
IncomeStatement | .../eastmoney_income_statement_recorder.py | 利润表有独立 recorder | 同一公司同日多条时按 provider 拆开 |
CashFlowStatement | .../eastmoney_cash_flow_recorder.py | 现金流量表有独立 recorder | 季度与年度记录的可得时间不同 |
| 三张表共用的基类 | .../base_china_stock_finance_recorder.py | 说明四类财务抓取共享一套基类逻辑 | 改口径时影响面较大 |
| 其它 provider | 聚宽一侧另有财务与估值相关 recorder(如 jq_stock_valuation_recorder) | 换源可以拿到不同口径的财务/估值数据 | 换源会换口径,研究里要固定一家 |
FinanceFactor.provider_map_recorder。列表里没有的对象,就是这家源抓不了——不要靠猜。财务数据常见问题
字段与用法以源码与 README 为准;口径类问题会说明官方是否明确。
财务指标和三大报表要分别取吗?
是。它们是四个独立 schema,各自 record_data;想要哪个就先写哪个。日常筛选用 FinanceFactor 的 9 个关键列基本够,需要拆结构时再取三张表。
为什么 ROE 会出现大于 1 的数值?
README 的示例输出里确实出现了 ROE 明显异常的行(例如大于 1 甚至更大的值)。这类情况通常与公司净资产为负或数据口径有关,但官方未做说明。做横截面排序前建议先做极值检查与 Winsorize,并在研究记录里写明处理规则。
能按公告日期筛选吗?
可以按 report_date(DateTime)加条件,但它是否等于公告日需要你自己核验——官方文档没有给出定义。核验方法见上一节的对照实验。
季报和年报怎么区分?
用 report_period(String 类型)过滤,README 示例里用的是 report_period == 'year'。具体的取值枚举建议打印一次该列的全部唯一值来确认。
同一家公司同一天出现多条记录怎么办?
先按 provider 拆开看是哪家源写的,再确认 code 是否一致。多源混写是「重复记录」最常见的来源;研究里应固定一个源作为基准。
这些财务数据可以直接拿来选股吗?
可以,但要加上两个前提:①极值处理;②时间对齐——用「当时已经公布的」财报而不是「后来才知道的」财报。第二点没做好,选股结果会系统性偏乐观,见「点时数据与未来函数」页。