7.1 财务报表的量化解读¶
为什么技术派交易员也要看财务报表?
技术指标(MA、RSI、MACD)只看价格已经发生了什么, 但价格的长期驱动力来自公司到底值多少钱。 价格与基本面可能长期偏离,基本面指标也不能保证预测未来价格。研究时需要同时记录财务数据的可得时点、估值口径和样本外表现。 一家净资产收益率极高的公司最终会被市场发现并重估。
量化基本面分析把财务报表字段转换为可复现的指标或信号,同时保留公告日和数据可得时间。
学习目标¶
- 理解三大财务报表各自回答什么问题
- 深度掌握 ROE、ROA、EPS、P/E、P/B 等指标的含义和计算逻辑
- 知道每个指标为什么重要,而不只是如何计算
- 实现 Piotroski F-Score,并理解每一条评分标准的经济学依据
1. 这节究竟解决什么实际问题?¶
7.1 财务报表的量化解读 的具体任务是:把三大财务报表翻译为可计算指标,并处理财报可得性滞后。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
理解利润表、资产负债表、现金流量表以及收益率。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
教学样本可能为模拟或固定快照。必须区分报告期、公告日、可获取日、修订日和实际交易日。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 7.1 财务报表的量化解读 的输入、计算步骤、输出和局限;尤其能说明:把三大财务报表翻译为可计算指标,并处理财报可得性滞后。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
报告期结束日、公告日和数据实际可获取日不同。交易信号必须按可获取日而非报告期对齐。
财务比率的分子与分母须来自同一可得时点;因子形成后再给予合理交易延迟。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
Pandas 便于按公告日对齐;数据服务返回的字段并不天然具有可交易时点。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“把三大财务报表翻译为可计算指标,并处理财报可得性滞后。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
按财报所属季度直接交易、使用修订后数据、忽略负值和行业差异、混用市值时点。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
错误的 Point-in-Time 对齐会产生未来函数;必须记录公告日并设置交易可用延迟。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/07_fundamental/01_financial_statements_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/07_fundamental/01_financial_statements_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:AQR 关于因子研究的资料。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 02_value_factors。本节的概念会成为理解它的输入,而不是孤立的名词。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
np.random.seed(42)
print('依赖库加载完毕')
依赖库加载完毕
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
第一部分:三大财务报表——各自解决什么问题?¶
在开始计算任何指标之前,先要理解三张报表的底层逻辑。 它们互为补充,缺少任何一张都会形成盲点。
损益表(Income Statement / P&L)¶
核心问题:这家公司在这一段时间内赚了多少钱?
营业收入 (Revenue)
- 营业成本 (COGS)
= 毛利润 (Gross Profit)
- 运营费用 (OPEX)
= 息税前利润 (EBIT)
- 利息费用
- 所得税
= 净利润 (Net Income)
量化分析的关注点:
- 净利率 = 净利润 / 营业收入:每赚 1 元收入能留下多少钱?
- EPS(每股收益) = 净利润 / 总股数:摊到每股上的盈利,是 P/E 的分母
资产负债表(Balance Sheet)¶
核心问题:这家公司现在拥有什么(资产),欠了多少(负债),股东实际拥有多少(权益)?
资产 = 负债 + 股东权益
(现金、存货、固定资产…)= (银行贷款、债券…)+(实收资本、留存收益…)
量化分析的关注点:
- 账面价值(Book Value) = 股东权益:公司清算后股东能拿到多少
- P/B = 市值 / 账面价值:市场愿意为账面净资产支付几倍溢价?
- 负债率 = 总负债 / 总资产:财务杠杆水平,越高越危险
现金流量表(Cash Flow Statement)¶
核心问题:公司实际流进流出了多少真实现金?
经营活动现金流 (CFO) ← 主营业务相关的现金流量
投资活动现金流 (CFI) ← 购置资产、投资
筹资活动现金流 (CFF) ← 借款、还款、分红
为什么现金流最重要?
会计利润可以被操纵(通过应计利润等手段)。 现金流量表与利润表采用不同确认口径。经营现金流不足可能增加流动性风险,但现金流数据同样需要核对分类、一次性项目和融资安排。
关键检验:CFO ≫ 净利润 → 盈利质量高 若净利润为正而 CFO 持续为负,应进一步检查应收账款、存货、预付款和收入确认。
# 构建模拟财务数据:50 只股票(代表不同质量级别的公司)
np.random.seed(42)
n = 50
# ── 损益表数据 ──────────────────────────────────
revenue = np.random.uniform(20e8, 200e8, n) # 营业收入(元)
cogs = revenue * np.random.uniform(0.5, 0.75, n) # 营业成本
gross = revenue - cogs # 毛利润
opex = gross * np.random.uniform(0.4, 0.8, n) # 运营费用
net_income = gross - opex # 净利润(简化)
net_income = np.maximum(net_income, 0)
# ── 资产负债表数据 ───────────────────────────────
total_assets = np.random.uniform(50e8, 500e8, n)
total_liab = total_assets * np.random.uniform(0.3, 0.8, n)
equity = total_assets - total_liab # 股东权益(账面价值)
shares = np.random.uniform(1e8, 10e8, n) # 总股数
book_value = equity # 账面净资产 = 股东权益
current_assets = total_assets * np.random.uniform(0.3, 0.6, n)
current_liab = total_liab * np.random.uniform(0.4, 0.7, n)
# ── 现金流量表数据 ───────────────────────────────
operating_cf = net_income * np.random.uniform(0.5, 1.8, n) # CFO(有时比利润多,有时少)
long_term_debt = total_liab * np.random.uniform(0.3, 0.7, n)
long_term_debt_py = long_term_debt * np.random.uniform(0.8, 1.2, n) # 上一年
# ── 市场数据 ─────────────────────────────────────
# P/E = 10~40 倍,市值 = 股价 × 股数
pe_ratio = np.random.uniform(8, 45, n)
market_cap = np.maximum(net_income * pe_ratio, equity * 0.5) # 市值
df = pd.DataFrame({
'revenue': revenue,
'net_income': net_income,
'total_assets': total_assets,
'total_liab': total_liab,
'equity': equity,
'book_value': book_value,
'shares': shares,
'market_cap': market_cap,
'operating_cf': operating_cf,
'current_assets': current_assets,
'current_liab': current_liab,
'long_term_debt': long_term_debt,
'long_term_debt_py': long_term_debt_py,
}, index=[f'STK{i:03d}' for i in range(n)])
print(f'模拟了 {n} 只股票的完整财务数据')
print('\n示例(STK000):')
sample = df.iloc[0]
print(f' 营业收入: {sample.revenue/1e8:.1f} 亿元')
print(f' 净利润: {sample.net_income/1e8:.1f} 亿元')
print(f' 净利率: {sample.net_income/sample.revenue:.1%}')
print(f' 股东权益: {sample.equity/1e8:.1f} 亿元')
print(f' 经营CFO: {sample.operating_cf/1e8:.1f} 亿元')
模拟了 50 只股票的完整财务数据 示例(STK000): 营业收入: 87.4 亿元 净利润: 13.2 亿元 净利率: 15.1% 股东权益: 173.8 亿元 经营CFO: 8.4 亿元
第二部分:核心财务指标——为什么这么设计?¶
每一个指标背后都有深刻的经济学逻辑。在计算之前,先理解它量化的是什么现象,为什么值得量化。
ROE(净资产收益率)¶
$$ROE = \frac{\text{净利润}}{\text{股东权益}}$$
为什么 ROE 最重要?
ROE 等于净利润除以平均股东权益,用于描述会计利润相对于股东权益的比例。 如果银行存款利率是 3%,而一家公司的 ROE 只有 2%,那么投资这家公司不如存银行。
巴菲特长期寻找 ROE > 15% 且稳定的公司。 持续较高的 ROE 可能来自盈利能力、资产周转或财务杠杆。仅凭 ROE 不能确认竞争优势。
ROE 的 DuPont 拆解: $$ROE = \underbrace{\frac{\text{净利润}}{\text{营业收入}}}_{\text{净利率}} \times \underbrace{\frac{\text{营业收入}}{\text{总资产}}}_{\text{资产周转率}} \times \underbrace{\frac{\text{总资产}}{\text{股东权益}}}_{\text{财务杠杆}}$$
这告诉我们提高 ROE 的三种路径:提高利润率、提高周转率、or 加杠杆。
ROA(总资产收益率)——管理层效率指标¶
$$ROA = \frac{\text{净利润}}{\text{总资产}}$$
为什么 ROA 和 ROE 不同?
ROE 看股东投入的回报,但公司还用了债权人(银行贷款)的钱。 ROA 等于净利润除以平均总资产,受利润率、资产周转率、行业结构和会计口径影响。
提高财务杠杆可能提高 ROE,同时增加偿债风险。ROA 不以股东权益为分母,但仍受利润确认和资产计量影响。
ROA 低但 ROE 高 → 公司高度依赖杠杆,财务风险较高
EPS(每股收益)——每股能生产多少利润¶
$$EPS = \frac{\text{净利润}}{\text{总股数}}$$
为什么用每股而不是总量?
两家公司净利润都是 10 亿,但一家有 10 亿股,一家有 1 亿股。 对于持有 1 股的投资者来说,后者的投资价值更高。 EPS 使不同规模的公司具有可比性。
P/E(市盈率)——市场愿意为利润付多少钱¶
$$P/E = \frac{\text{股价}}{\text{每股收益}} = \frac{\text{市值}}{\text{净利润}}$$
如何理解 P/E?
P/E = 20 意味着:你现在花 20 元,每年能分到 1 元利润(假设利润不增长)。 若利润不增长且其他条件不变,静态市盈率 20 倍对应的利润回收期为 20 年。这一换算不包含增长、折现、分红和会计口径变化。
P/E 越高代表什么?
- 市场预期未来利润增速高(愿意为成长付溢价)
- 或者市场情绪乐观(泡沫)
P/E 越低代表什么?
- 公司可能被低估(价值投资机会)
- 或者市场预期未来利润下滑(价值陷阱)
当整体市场 P/E 远超历史均值时,往往是熊市的前兆。
P/B(市净率)——市场愿意为净资产付多少钱¶
$$P/B = \frac{\text{股价}}{\text{每股账面净资产}} = \frac{\text{市值}}{\text{股东权益}}$$
P/B 的直觉:如果今天公司清算,股东能按账面价值拿回净资产。 P/B = 1 表示市场价格 = 账面价值。 P/B < 1 意味着市场认为公司实际值比账面价值更少(可能是财务有问题)。
为什么 Fama-French 把低 P/B 作为价值因子? 因为历史上低 P/B(= 高账面市值比 B/M)的股票长期跑赢高 P/B 股票, 这被称为价值溢价(Value Premium)。
# 计算所有核心财务指标
df['ROE'] = df['net_income'] / df['equity']
df['ROA'] = df['net_income'] / df['total_assets']
df['EPS'] = df['net_income'] / df['shares']
df['PE'] = df['market_cap'] / df['net_income'].replace(0, np.nan)
df['PB'] = df['market_cap'] / df['book_value']
df['PS'] = df['market_cap'] / df['revenue'] # 市销率
df['leverage'] = df['total_liab'] / df['total_assets'] # 资产负债率
df['net_margin'] = df['net_income'] / df['revenue'] # 净利率
df['asset_turnover'] = df['revenue'] / df['total_assets'] # 资产周转率
df['current_ratio'] = df['current_assets'] / df['current_liab'] # 流动比率
# DuPont 验证: ROE ≈ 净利率 × 资产周转率 × 财务杠杆
df['ROE_decomposed'] = (df['net_margin'] * df['asset_turnover'] *
(df['total_assets'] / df['equity']))
print('核心财务指标统计:')
metrics_to_show = ['ROE', 'ROA', 'PE', 'PB', 'leverage', 'net_margin', 'current_ratio']
print(df[metrics_to_show].describe().round(3))
print()
print('DuPont 分解验证(ROE 与拆解结果之差,应接近0):')
print(f' 平均绝对误差: {(df.ROE - df.ROE_decomposed).abs().mean():.6f}')
核心财务指标统计:
ROE ROA PE PB leverage net_margin current_ratio
count 50.000 50.000 50.000 50.000 50.000 50.000 50.000
mean 0.166 0.067 25.329 4.322 0.558 0.153 1.628
std 0.127 0.049 10.033 3.996 0.155 0.054 0.806
min 0.020 0.007 9.065 0.500 0.303 0.067 0.737
25% 0.072 0.029 16.905 1.379 0.419 0.111 0.927
50% 0.157 0.051 25.129 2.780 0.586 0.136 1.324
75% 0.211 0.093 31.643 5.888 0.670 0.193 2.140
max 0.628 0.203 44.261 18.482 0.787 0.286 3.613
DuPont 分解验证(ROE 与拆解结果之差,应接近0):
平均绝对误差: 0.000000
# 可视化各指标分布及其经济学含义
fig, axes = plt.subplots(2, 3, figsize=(16, 8))
plots = [
('ROE', 'ROE 净资产收益率\n(15% 仅作为本例绘图参考线)', 'steelblue', 0.15),
('ROA', 'ROA 总资产收益率\n(衡量全部资源的使用效率,排除杠杆干扰)', 'darkorange', 0.08),
('PE', 'P/E 市盈率\n(越低=估值越便宜,但要警惕利润下滑的「价值陷阱」)', 'green', 20),
('PB', 'P/B 市净率\n(Fama-French 价值因子分母,<1 可能存在危机或机会)', 'red', 2.0),
('leverage', '资产负债率\n(越高=杠杆越高=财务风险越大,>70% 需警惕)', 'purple', 0.70),
('net_margin', '净利率\n(每赚 1 元收入保留多少,软件公司 >30%,制造业通常 <10%)', 'gray', 0.10),
]
for ax, (col, title, color, threshold) in zip(axes.flat, plots):
data = df[col].dropna()
ax.hist(data, bins=20, color=color, alpha=0.65, edgecolor='white')
ax.axvline(data.median(), color='red', linestyle='--', linewidth=2,
label=f'中位数={data.median():.2f}')
ax.axvline(threshold, color='black', linestyle=':', linewidth=1.5,
label=f'参考线={threshold}')
ax.set_title(title, fontsize=9)
ax.legend(fontsize=7)
ax.grid(alpha=0.3)
plt.suptitle('50 只模拟股票:核心财务指标分布', fontsize=13)
plt.tight_layout()
plt.show()
第三部分:Piotroski F-Score——为什么这 9 个条件?¶
背景:Joseph Piotroski 在 2000 年发表的论文发现, 许多低 P/B 的公司(传统「价值股」)其实是业绩持续恶化的「价值陷阱」。 Piotroski 构建了由 9 个指标组成的财务健康评分,用于在低账面市值比股票中区分财务状况较强与较弱的公司。
每一条评分标准都对应一个具体的财务恶化信号。下面逐一解释为什么:
盈利能力(4 项)
| 指标 | 含义 | 为什么是 1 或 0 |
|---|---|---|
| F1: ROA > 0 | 总资产有正回报 | 公司最基本的生存线——连资产回报都为负,是严重危险信号 |
| F2: 经营 CFO > 0 | 主业产生真实现金 | 利润可以造假,现金流更难造假。CFO 为负意味着公司靠外部融资维持 |
| F3: ROA 同比改善 | 盈利能力好转 | 捕捉「公司是否走出低谷」。改善中的公司比持续恶化的公司更值得关注 |
| F4: CFO > 净利润(应计项目为负) | 现金质量高 | 若净利润高但 CFO 低,差距是「应计利润」——应收账款未收、存货积压等。应计项目多意味着未来利润可能被修正 |
杠杆与流动性(3 项)
| 指标 | 为什么 |
|---|---|
| F5: 长期负债比率下降 | 公司在去杠杆(降低财务风险),是努力改善财务结构的信号 |
| F6: 流动比率改善 | 流动比率 = 流动资产/流动负债,改善意味着短期偿债能力变强,不太可能发生流动性危机 |
| F7: 未增发新股 | 增发股票会稀释现有股东权益。不增发说明公司有内部资金来源,不需要「圈钱」 |
运营效率(2 项)
| 指标 | 为什么 |
|---|---|
| F8: 毛利率改善 | 毛利率 = (收入 - 成本)/收入,改善意味着产品定价权或成本控制在变好 |
| F9: 资产周转率改善 | 同样资产规模,能产生更多收入,说明运营效率提升 |
评分规则:满足 = 1 分,不满足 = 0 分,满分 9 分
- ≥ 8 分:财务基本面持续改善 → 候选多头
- ≤ 2 分:财务基本面全面恶化 → 候选空头(或回避)
# 计算 Piotroski F-Score(逐项说明)
# F1: ROA > 0(总资产收益率为正)
df['F1'] = (df['ROA'] > 0).astype(int)
# F2: 经营 CFO > 0(主业产生真实现金流)
df['F2'] = (df['operating_cf'] > 0).astype(int)
# F3: ROA 同比改善(用均值的 90% 模拟上期)
df['ROA_prev'] = df['ROA'] * np.random.uniform(0.7, 1.2, n) # 模拟上期 ROA
df['F3'] = (df['ROA'] > df['ROA_prev']).astype(int)
# F4: 应计利润率 < 0(CFO > 净利润,现金质量好)
# 应计利润率 = (净利润 - CFO) / 总资产
df['accruals'] = (df['net_income'] - df['operating_cf']) / df['total_assets']
df['F4'] = (df['accruals'] < 0).astype(int)
# F5: 长期负债率下降(去杠杆)
df['LTD_ratio'] = df['long_term_debt'] / df['total_assets']
df['LTD_ratio_py'] = df['long_term_debt_py'] / df['total_assets']
df['F5'] = (df['LTD_ratio'] < df['LTD_ratio_py']).astype(int)
# F6: 流动比率改善(模拟上期流动比率略低)
df['current_ratio_py'] = df['current_ratio'] * np.random.uniform(0.8, 1.1, n)
df['F6'] = (df['current_ratio'] > df['current_ratio_py']).astype(int)
# F7: 未增发新股(随机模拟:80% 未增发)
df['F7'] = np.random.choice([0, 1], n, p=[0.2, 0.8])
# F8: 毛利率改善
df['gross_margin'] = (df['revenue'] - df['revenue']*0.6) / df['revenue']
df['gross_margin_py'] = df['gross_margin'] * np.random.uniform(0.85, 1.05, n)
df['F8'] = (df['gross_margin'] > df['gross_margin_py']).astype(int)
# F9: 资产周转率改善
df['turnover_py'] = df['asset_turnover'] * np.random.uniform(0.85, 1.1, n)
df['F9'] = (df['asset_turnover'] > df['turnover_py']).astype(int)
# 汇总 F-Score
df['F_score'] = df[['F1','F2','F3','F4','F5','F6','F7','F8','F9']].sum(axis=1)
# 查看各条件通过率
print('各 F-Score 条件通过率:')
fscore_cols = {'F1':'ROA>0', 'F2':'CFO>0', 'F3':'ROA改善', 'F4':'现金质量',
'F5':'去杠杆', 'F6':'流动性改善', 'F7':'未增发', 'F8':'毛利率改善', 'F9':'周转率改善'}
for col, desc in fscore_cols.items():
rate = df[col].mean()
bar = '█' * int(rate * 20)
print(f' {col}({desc:10s}): {bar:<20s} {rate:.0%}')
各 F-Score 条件通过率: F1(ROA>0 ): ████████████████████ 100% F2(CFO>0 ): ████████████████████ 100% F3(ROA改善 ): ██████████████ 72% F4(现金质量 ): ███████████ 58% F5(去杠杆 ): ████████████ 64% F6(流动性改善 ): ████████████████ 80% F7(未增发 ): ████████████████ 82% F8(毛利率改善 ): ██████████████ 72% F9(周转率改善 ): █████████████ 66%
# F-Score 分布与策略验证
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 左图:F-Score 分布
df['F_score'].hist(bins=range(0, 11), ax=axes[0], color='steelblue',
alpha=0.75, edgecolor='white', rwidth=0.8)
axes[0].axvspan(7.5, 10, alpha=0.15, color='green', label='高质量(多头区 ≥8)')
axes[0].axvspan(-0.5, 2.5, alpha=0.15, color='red', label='低质量(空头区 ≤2)')
axes[0].axvline(df['F_score'].median(), color='black', linestyle='--',
lw=2, label=f'中位数={df.F_score.median():.0f}')
axes[0].set_title('Piotroski F-Score 分布(0~9分)')
axes[0].set_xlabel('F-Score')
axes[0].set_ylabel('股票数量')
axes[0].legend()
# 右图:F-Score 与财务指标的关系
score_groups = df.groupby('F_score')[['ROE', 'ROA', 'net_margin']].mean()
colors = ['red' if i <= 2 else ('green' if i >= 7 else 'gray') for i in score_groups.index]
axes[1].bar(score_groups.index, score_groups['ROE'], color=colors, alpha=0.8)
axes[1].set_title('F-Score vs 平均 ROE\n(验证:高分公司 ROE 是否确实更高?)')
axes[1].set_xlabel('F-Score')
axes[1].set_ylabel('平均 ROE')
axes[1].grid(alpha=0.3)
plt.suptitle('Piotroski F-Score:财务健康综合评分', fontsize=13)
plt.tight_layout()
plt.show()
high_f = df[df['F_score'] >= 7]
low_f = df[df['F_score'] <= 2]
mid_f = df[(df['F_score'] >= 3) & (df['F_score'] <= 6)]
print('\nF-Score 分层统计:')
print(f'高分(≥7): {len(high_f):2d} 只, 平均 ROE = {high_f.ROE.mean():.3f},'
f' 平均 ROA = {high_f.ROA.mean():.3f}')
print(f'中分(3-6): {len(mid_f):2d} 只, 平均 ROE = {mid_f.ROE.mean():.3f},'
f' 平均 ROA = {mid_f.ROA.mean():.3f}')
print(f'低分(≤2): {len(low_f):2d} 只, 平均 ROE = {low_f.ROE.mean():.3f},'
f' 平均 ROA = {low_f.ROA.mean():.3f}')
print()
print('结论:F-Score 高的公司,ROE 和 ROA 也系统性地更高,验证了指标的有效性。')
F-Score 分层统计: 高分(≥7): 35 只, 平均 ROE = 0.146, 平均 ROA = 0.062 中分(3-6): 15 只, 平均 ROE = 0.211, 平均 ROA = 0.078 低分(≤2): 0 只, 平均 ROE = nan, 平均 ROA = nan 结论:F-Score 高的公司,ROE 和 ROA 也系统性地更高,验证了指标的有效性。
练习¶
- 对 A 股上市公司(可用 akshare 或 adata 获取财务数据),计算所有成份股的 F-Score,观察 F-Score ≥ 8 的公司在下一年的平均涨幅是否优于 F-Score ≤ 2 的公司。
- 用 DuPont 公式将贵州茅台和中国石化的 ROE 分别拆解为净利率、资产周转率、杠杆三个分量,解释两家公司 ROE 相似但商业模式截然不同的原因。
- 将 F4(应计利润率)条件改为「CFO增长率 > 0」,对比两种定义下 F-Score 分布的差异。
下一节 → 02_value_factors.ipynb