4、财务数据获取:常用数据源(Wind、Choice、Tushare、AkShare等)、财务报表数据结构与清洗要点

财务因子选股的起点是可靠、完整、口径一致的财务数据。无论是构建多因子模型、进行单因子测试,还是执行量化回测,数据质量直接决定研究结论的可信度。本章围绕“从哪里拿数据”和“拿到数据后如何处理”两个核心问题展开,系统梳理国内主流财务数据源的特点、财务报表的数据结构,以及量化场景下必须掌握的数据清洗方法论。

财务数据获取与清洗链路 数据源层 Wind / Choice Tushare Pro AkShare 巨潮资讯/交易所 (原始公告/定期报告) 数据结构层 资产负债表(BS) 利润表(IS) 现金流量表(CF) 报表附注/财务指标 (宽表 vs 长表) 清洗加工层 去重 / 去极值 缺失值处理 口径统一(合并/母公司) 行业中性化 / 标准化 (避免未来函数) 因子库 回测 选股 关键问题: ① 数据源覆盖度与更新频率是否满足日频/月频调仓? ② 报表类型(合并/母公司)与报告期(Q1/H1/Q3/年报)如何对齐? ③ 历史数据是否存在回溯调整(如会计准则变更、重述)? 清洗五步法: 1. 去重(股票代码+报告期唯一) → 2. 缺失值处理(剔除/填充) → 3. 极值处理(MAD/分位数) 4. 口径统一(合并报表优先) → 5. 行业中性化与标准化(Z-Score / 百分位)

4.1 常用数据源对比与选型

国内量化研究可用的财务数据源大致分为四类:商业终端(Wind、Choice)、开源Python接口(Tushare Pro、AkShare)、交易所/监管机构官方披露(巨潮资讯、上交所/深交所官网)、以及专业数据库(如CSMAR、RESSET)。不同数据源在覆盖度、更新时效、字段丰富度、使用成本和稳定性上差异显著。

数据源 覆盖范围 更新频率 接口方式 成本 适用场景
Wind A股/港股/美股/债券/商品 实时/日频 API(Python/Excel) 高(机构年费) 机构级研究、高频因子、多资产配置
Choice A股为主,含港美股 日频/分钟 API(Python/Excel) 中(个人/机构) 中小机构、个人量化
Tushare Pro A股/基金/期货/宏观 日频(积分制) Python SDK 低(积分制) 个人研究、策略原型验证
AkShare A股/港股/美股/宏观/另类 日频(爬虫聚合) Python(requests) 免费 快速原型、教学演示、数据探索
巨潮资讯 A股定期报告/临时公告 公告日同步 PDF/网页 免费 原始报表核对、PDF解析
选型建议:若用于正式策略回测,优先选择Wind或Tushare Pro(需较高积分),因为其字段标准化程度高、历史数据完整。AkShare适合快速验证想法,但需注意其数据源为公开网页爬取,可能存在字段缺失或更新延迟。个人学习阶段建议从Tushare Pro + AkShare组合入手,兼顾数据质量与成本。

4.2 财务报表数据结构:宽表与长表

财务数据在数据库中通常以两种形式存储:宽表(Wide Format)和长表(Long Format)。理解这两种结构是数据清洗的前提。

宽表:每一行代表一个“股票-报告期”组合,每一列代表一个财务字段(如总资产、净利润、ROE)。这种结构便于直接计算因子,但新增字段时需要修改表结构。

# 宽表示例(DataFrame)
   ts_code   end_date   total_assets   net_profit   roe
0  000001.SZ 20231231   5,320,000,000  138,000,000  12.5
1  000001.SZ 20230930   5,180,000,000  105,000,000  10.2
2  600000.SH 20231231   8,450,000,000  210,000,000  11.8

长表:每一行代表一个“股票-报告期-字段名-字段值”的记录。这种结构便于存储和扩展,但计算因子时需要进行透视(pivot)操作。

# 长表示例
   ts_code   end_date   field_name    field_value
0  000001.SZ 20231231   total_assets  5,320,000,000
1  000001.SZ 20231231   net_profit    138,000,000
2  000001.SZ 20231231   roe           12.5

在量化回测中,通常需要将长表转换为宽表。使用Pandas的pivot_table或groupby+unstack即可完成转换。需要注意:转换前必须确保ts_code和end_date的组合唯一,否则透视结果会出现重复列。

4.3 数据清洗核心要点

财务数据清洗是因子构建中最耗时、最易出错的环节。以下是五个必须处理的要点:

4.3.1 去重与唯一性校验

同一股票同一报告期可能出现多条记录(例如数据源重复推送、不同报表类型并存)。清洗第一步是确保(ts_code, end_date)唯一。若出现重复,需根据报表类型优先级(合并报表优先于母公司报表)或公告日期(取最新公告)进行去重。

# 去重示例:保留每个股票每个报告期最后一条记录
df = df.sort_values(['ts_code', 'end_date', 'ann_date'])
df = df.drop_duplicates(subset=['ts_code', 'end_date'], keep='last')

4.3.2 缺失值处理

财务数据缺失常见原因:新股上市不足一年、停牌期间未披露、数据源采集遗漏。处理策略需结合因子计算逻辑:

  • 剔除:若某股票在因子计算区间内关键字段缺失,直接剔除该股票(适用于截面回归)。
  • 填充:对于非关键字段,可使用行业均值或前值填充(需谨慎,避免引入偏差)。
  • 标记:在因子值中增加缺失标记列,供后续模型处理。

4.3.3 极值处理(去极值)

财务数据常存在极端值(如净利润为负导致的ROE异常大、资产重组导致的资产规模突变)。常用方法:

  • MAD法(中位数偏差):将超出中位数±3倍MAD的观测值替换为边界值。
  • 分位数法:将超出1%和99%分位数的值截断。
  • 标准差法:将超出均值±3倍标准差的观测值替换为边界值(对正态分布假设敏感)。
注意:去极值应在行业分组内进行,避免因行业特性(如银行业与科技业ROE分布差异)导致误杀。同时,去极值只影响因子值,不应修改原始财务数据。

4.3.4 口径统一:合并报表 vs 母公司报表

同一公司可能同时披露合并报表和母公司报表。量化因子计算应统一使用合并报表数据,因为其反映了公司整体经营成果。若数据源同时提供两种口径,需设置筛选条件:report_type = '合并'。此外,还需注意新旧会计准则切换(如2007年新准则、2019年金融工具准则修订)导致的历史数据口径不一致,需进行可比性调整。

4.3.5 行业中性化与标准化

财务因子(如ROE、毛利率)受行业属性影响显著。在截面选股时,需进行行业中性化处理:将因子值对行业虚拟变量回归,取残差作为中性化后的因子值。标准化则常用Z-Score或百分位排名,使不同量纲的因子可比较。

# 行业中性化示例(使用statsmodels)
import statsmodels.api as sm
# 假设df包含因子值factor、行业代码industry
dummies = pd.get_dummies(df['industry'], prefix='ind')
X = sm.add_constant(dummies)
model = sm.OLS(df['factor'], X).fit()
df['factor_neutral'] = model.resid

4.4 实战案例:构建ROE因子数据管道

以下案例演示从Tushare Pro获取财务数据、清洗并构建ROE因子的完整流程。假设我们需要构建一个基于“扣非ROE”的选股因子,用于月度调仓回测。

背景与约束:回测区间为2018年1月至2023年12月,股票池为沪深300成分股。数据源使用Tushare Pro(积分2000+),日频更新。关键约束:避免未来函数——只能使用截至调仓日已公告的财务数据。

操作步骤:

  1. 获取股票列表:从Tushare获取沪深300成分股列表(index_weight接口)。
  2. 获取财务数据:调用fina_indicator接口,获取roe_dt(扣非ROE)字段,同时获取end_date、ann_date(公告日期)。
  3. 数据对齐:将财务数据与调仓日对齐——仅保留ann_date <= 调仓日且end_date为最近报告期的记录。
  4. 清洗处理:去重、剔除缺失值、按行业(申万一级)进行MAD去极值、行业中性化、Z-Score标准化。
  5. 因子输出:生成月度因子值表,供回测框架调用。
# 关键代码片段(Tushare Pro)
import tushare as ts
pro = ts.pro_api('your_')

# 获取财务指标
df = pro.fina_indicator(ts_code='000001.SZ', 
                        start_date='20180101', 
                        end_date='20231231',
                        fields='ts_code,end_date,ann_date,roe_dt')

# 过滤:仅保留公告日期在调仓日之前的数据
df = df[df['ann_date'] <= '20230630']

结果与复盘:清洗后的ROE因子IC均值约为0.04,ICIR为0.35,分层回测显示多空组合年化收益差约8%。但发现两个问题:一是部分股票在年报披露前使用三季报数据,导致因子更新滞后;二是金融行业ROE普遍高于非金融行业,行业中性化后金融股因子值被压缩,需结合行业配置权重调整。

经验总结:财务因子回测中,公告日期(ann_date)比报告期(end_date)更重要——使用报告期数据但未考虑公告延迟,会引入严重的前视偏差。建议在数据管道中始终保留ann_date字段,并在调仓日进行动态过滤。

4.5 常见误区与注意事项

  • 误区一:直接使用报告期数据而不考虑公告延迟。年报披露截止日为次年4月30日,若在1月调仓时使用上年年报数据,实际上该数据尚未公开,属于未来函数。
  • 误区二:忽略报表类型差异。部分数据源默认返回母公司报表,若直接用于因子计算,会低估合并范围内的子公司贡献。
  • 误区三:对财务数据直接进行标准化而不做行业中性化。这会导致因子值主要反映行业属性而非公司经营差异。
  • 误区四:数据更新时未处理历史修正。上市公司可能对历史报表进行追溯调整(如会计差错更正),若数据源已更新,需同步刷新历史因子值。

财务数据获取与清洗是量化研究的“地基工程”。建议在正式回测前,建立一套自动化的数据质量校验流程,包括:字段完整性检查、报告期连续性检查、与原始公告抽样核对等。只有确保数据管道稳定可靠,后续的因子分析和策略回测才有意义。