🏠
AI量化交易 · 第14章:基本面数据
目录
Chapter 14

基本面数据

PE、PB、ROE如何影响股价——让财务报表成为你的量化燃料

本章学习目标

你将学会

  1. 理解PE、PB、ROE、PEG等核心估值指标的含义和计算方式
  2. 掌握从财务报表中提取量化因子的方法
  3. 学会构建基于基本面数据的股票筛选策略
  4. 了解基本面因子在不同市场环境下的表现差异
  5. 能够用AI辅助完成基本面数据的获取、清洗和分析

如果说价格数据是量化交易的面包,那么基本面数据就是肉和蔬菜——它让你的策略有了营养和厚度。很多新手量化交易者只盯着K线图,却忽略了企业背后真实的经营状况。本章,我们将一起打开财务报表这个"黑箱",把那些枯燥的数字变成有生产力的交易信号。

故事引入:老王的价值投资困惑

老王是个有二十年股龄的老股民。2020年疫情期间,他看着航空股跌到了历史低位,心想:"这么便宜,捡漏的时候到了!"于是重仓买入。结果?航空股继续跌了40%,老王的"便宜货"变成了"烫手山芋"。

与此同时,他的邻居小李——一个刚入门的量化新手——用AI写了一个简单的筛选器:PE低于行业均值 + ROE连续三年大于15% + 负债率低于50%。小李用这个筛子在A股里扫了一圈,挑出了十几只股票,组合跑了一年,收益居然比老王高了30%。

老王不服气:"我看财务报表看了二十年,难道还不如一个代码?"

其实老王的问题不在于经验,而在于没有系统化地把经验变成规则。他知道PE低可能代表便宜,但不知道PE低到多少才算"真的便宜";他知道ROE很重要,但从来没有量化过ROE和股价回报之间的统计关系。

量化交易的核心优势不是比人更聪明,而是比人更系统、更一致、更不容易被情绪带偏。基本面数据的价值,恰恰在于它提供了一套超越短期价格波动的"锚"。

核心知识:基本面数据的量化密码

2.1 为什么价格数据不够?

在前面的章节里,我们学了K线、均线、MACD、RSI——这些都是价格数据的衍生品。它们回答的问题是:"市场正在做什么?"

但价格数据有一个致命的盲区:它不关心企业本身是否健康。一只垃圾股可以因为游资炒作而连拉十个涨停板,一只优质白马股也可以因为大盘恐慌而被错杀。只看价格,就像只看一个人的表情来判断他的性格——容易被表象欺骗。

基本面数据回答的是另一个问题:"这家企业值多少钱?" 它来自企业的三大财务报表:

量化交易者不关心某家公司的具体故事,而是关心这些财务指标在统计上是否能预测未来的股价回报。这就是基本面量化的本质。

2.2 核心估值指标 decoded

让我们逐个拆解最常用的基本面指标。别担心,没有复杂的会计公式,只有直觉化的理解。

📊 PE(市盈率)——你愿意为每1元利润付多少钱?

点击展开详细解释与计算

公式:PE = 股价 / 每股收益(EPS)

PE的本质是"回本周期"。PE为10,意味着如果利润不变,你需要10年回本;PE为50,意味着需要50年。所以PE越低,理论上越"便宜"。

但PE有陷阱:

  • 周期股陷阱:钢铁、航运等周期行业在景气高点时利润暴增,PE反而很低,但这恰恰是卖出的信号。
  • 成长股溢价:科技公司PE常年50倍以上,不是因为贵,而是因为市场预期它未来利润会爆发。
  • 亏损股失效:亏损企业没有正的EPS,PE为负数或无限大,无法比较。

量化用法:不要单独使用PE,而是与行业均值、历史分位数结合。比如"PE低于行业均值30%"作为一个因子。

📘 PB(市净率)——资产打折了吗?

点击展开详细解释与计算

公式:PB = 股价 / 每股净资产

PB衡量的是市场对企业净资产的溢价程度。PB小于1意味着"破净"——市场认为这家企业的资产连账面价值都不值。银行股常年破净,不是因为便宜,而是因为资产质量存疑(坏账风险)。

PB比PE更稳定,因为净资产不会像利润那样大幅波动。所以PB更适合周期行业和重资产行业(银行、地产、制造业)。

量化用法:"低PB + 高ROE"是一个经典组合——便宜但赚钱能力强。

📈 ROE(净资产收益率)——企业赚钱效率高吗?

点击展开详细解释与计算

公式:ROE = 净利润 / 净资产

ROE是巴菲特最看重的指标。它回答的问题是:股东投入的每1块钱,企业能赚回多少利润?

ROE的拆解(杜邦分析法)告诉我们,高ROE来自三个渠道:

  1. 高利润率:产品定价权强(如茅台)
  2. 高周转率:资产运转快(如零售业)
  3. 高杠杆:借钱生钱(如银行业)

量化交易者更喜欢高利润率驱动的ROE,因为它更可持续。高杠杆驱动的ROE在市场紧缩时容易崩盘。

量化用法:ROE连续三年大于15%,或ROE处于历史前20%分位。

⚖ PEG——给成长估值的尺子

点击展开详细解释与计算

公式:PEG = PE / 盈利增长率(%)

彼得·林奇发明的指标。PEG为1,表示估值与增速匹配;小于1,可能被低估;大于2,可能被高估。

PEG解决了纯PE的盲区:一只PE为30的股票,如果利润年增长30%,PEG就是1,其实并不贵;而一只PE为10的股票,如果利润年增长为0%,PEG反而无穷大。

量化用法:PEG < 1 且盈利增速 > 20%,是成长股的经典筛选条件。

2.3 基本面分析的量化流程

把基本面数据变成量化策略,需要经历一个标准的流水线。下面这个Mermaid流程图展示了整个过程:

flowchart LR A[获取财报数据] --> B[计算估值指标] B --> C[横向对比行业] C --> D[历史分位数定位] D --> E[构建多因子] E --> F[回测验证] F --> G[实盘筛选] style A fill:#1e3a5f,color:#fff style B fill:#1e3a5f,color:#fff style C fill:#1e3a5f,color:#fff style D fill:#1e3a5f,color:#fff style E fill:#f97316,color:#fff style F fill:#06b6d4,color:#fff style G fill:#10b981,color:#fff

2.4 基本面因子在中国A股的真实表现

很多人以为价值投资在A股不灵。这是真的吗?我们用数据说话。

某知名券商做过一个回测:从2010年到2023年,每月初按低PE + 高ROE选出A股前50只股票,等权重持有,月度调仓。结果如何?

结论:价值投资在A股长期有效,但短期可能很痛苦。2013-2015年创业板疯涨时,低PE策略连续三年跑输市场。如果你能忍受这种"孤独",长期回报是显著的。

科学争议:价值因子是否已经失效?

2010年后,全球多个市场的价值因子(低PE/PB)表现不佳,引发"价值已死"的讨论。

正方:价值因子只是暂时蛰伏。历史上它经历过多次10年以上的低谷期(如1990年代的美国),每次回归后都有超额收益。当前的低利率环境不利于价值股,但环境会变。

反方:经济结构变了。科技巨头靠无形资产赚钱,传统估值指标无法衡量它们的价值。低PE筛选会系统性地排除最优秀的公司。

量化交易者的务实态度:不站队,而是把价值因子作为多因子模型中的一个维度,赋予它合理的权重,而不是孤注一掷。

2.5 一个实用的基本面筛选框架

不要被指标迷花了眼。真正有效的基本面策略往往很简单。这里给你一个经过回测验证的"三层筛选法"

  1. 第一层:财务健康筛查——ROE > 10%,负债率 < 60%,经营现金流为正。剔除明显有财务风险的公司。
  2. 第二层:估值安全边际——PE处于历史后30%分位,或PB处于行业后20%分位。找到"相对便宜"的股票。
  3. 第三层:质量加成——净利润增速 > 营收增速(说明利润率在提升),或连续3年ROE > 15%。

三层筛完,A股5000只股票可能只剩50-100只。这时候再配合技术指标(如均线趋势)决定入场时机,就是一个完整的多因子策略了。

2.6 基本面数据获取实战

对于A股,获取基本面数据最简单的方式是使用AKShareTushare。下面是获取全市场PE、PB、ROE的代码示例:

PYTHON
import akshare as ak
import pandas as pd

# 获取A股全市场估值指标
df = ak.stock_zh_a_spot_em()

# 选取关键列
cols = ['代码', '名称', '最新价', '市盈率-动态', '市净率', 
        '净资产收益率', '所属行业', '总市值']
df = df[cols].copy()

# 重命名列(让名字更友好)
df.columns = ['code', 'name', 'price', 'pe', 'pb', 
              'roe', 'industry', 'market_cap']

# 数据清洗:剔除异常值
df = df[df['pe'] > 0]      # 剔除亏损
df = df[df['pe'] < 500]    # 剔除极端异常
df = df[df['pb'] > 0]

# 查看数据样例
print(f"全市场共 {len(df)} 只股票")
print(df.head(10))

# 按PE从低到高排序,看最"便宜"的10只
cheapest = df.nsmallest(10, 'pe')[['name', 'pe', 'pb', 'roe', 'industry']]
print("\n=== PE最低的10只股票 ===")
print(cheapest)

2.7 行业对比:为什么苹果不能和银行比PE?

新手最常犯的错误,就是把不同行业的股票放在同一个PE尺度下比较。这相当于拿橘子和西瓜比甜度——没有意义。

不同行业的合理估值区间差异巨大:

所以量化策略中,行业内比较比全市场比较更可靠。比如"PE处于行业后20%"比"PE < 15"更有意义。

PYTHON
# 行业内分位数筛选示例
def filter_by_industry_percentile(df, factor='pe', percentile=0.3):
    """
    在每个行业内,选出某因子处于最低 percentile 的股票
    """
    selected = []
    for industry, group in df.groupby('industry'):
        if len(group) < 5:  # 行业股票太少就跳过
            continue
        threshold = group[factor].quantile(percentile)
        picked = group[group[factor] <= threshold]
        selected.append(picked)
    return pd.concat(selected, ignore_index=True)

# 使用:选出各行业PE最低的30%
value_stocks = filter_by_industry_percentile(df, factor='pe', percentile=0.3)
print(f"筛选后剩余 {len(value_stocks)} 只股票")
print(value_stocks[['name', 'pe', 'industry']].head(15))

与AI一起练习

练习1:让AI解释估值指标的适用场景

Prompt模板

PROMPT
请用通俗易懂的语言解释PE、PB、ROE、PEG这四个指标的区别。
要求:
1. 每个指标用一句话定义
2. 各举一个最适合使用它的行业例子
3. 指出每个指标最容易踩的坑
4. 用表格形式输出

你需要做的:把AI的回答与你本章学到的内容对比,看看有没有遗漏的陷阱。特别关注AI是否提到了周期股陷阱和行业内比较的重要性。

练习2:让AI写一个完整的基本面筛选器

Prompt模板

PROMPT
请用Python和AKShare写一个A股基本面筛选器,要求:
1. 获取全市场股票的PE、PB、ROE、负债率、营收增速
2. 筛选条件:PE>0且<100, PB>0且<10, ROE>8%, 负债率<70%
3. 按行业内PE分位数排名,选出各行业最便宜的前3只
4. 输出一个DataFrame,包含股票名称、代码、行业、PE、PB、ROE
5. 添加注释说明每一步在做什么

你需要做的:运行AI生成的代码,检查是否能正常执行。如果遇到AKShare接口报错,让AI帮你调试。

练习3:让AI分析"价值投资在中国是否有效"

Prompt模板

PROMPT
请基于2010-2023年的A股历史数据,分析以下问题:
1. 每月按低PE+高ROE选股,等权重持有一月,年化收益是多少?
2. 这个策略在哪几年跑输大盘?原因可能是什么?
3. 加入PEG<1.5作为额外条件,结果有什么变化?
4. 如果改成季度调仓而非月度调仓,收益和换手率如何变化?

请给出Python回测代码和结果分析。

你需要做的:把这段代码保存下来,作为你后续开发多因子策略的基础模板。特别注意AI如何处理调仓日期和再平衡逻辑。

动手实践:构建你的第一个基本面策略

光看不练假把式。下面是本章的核心实践环节——一个完整的、可运行的低PE+高ROE筛选器,大约80行代码。

PYTHON
"""
基本面策略:低PE + 高ROE 选股器
数据来源:AKShare(免费,无需注册)
运行环境:Python 3.8+,需安装 akshare、pandas
"""
import akshare as ak
import pandas as pd
import numpy as np

def get_fundamental_data():
    """获取A股全市场基本面数据"""
    print("正在获取市场数据...")
    df = ak.stock_zh_a_spot_em()
    
    # 选取并重命名关键列
    df = df[[
        '代码', '名称', '最新价', '市盈率-动态', '市净率',
        '净资产收益率', '所属行业', '总市值', '换手率'
    ]].copy()
    df.columns = [
        'code', 'name', 'price', 'pe', 'pb',
        'roe', 'industry', 'market_cap', 'turnover'
    ]
    return df

def clean_data(df):
    """清洗异常数据"""
    # 转换为数值类型
    numeric_cols = ['pe', 'pb', 'roe', 'market_cap', 'turnover']
    for col in numeric_cols:
        df[col] = pd.to_numeric(df[col], errors='coerce')
    
    # 剔除异常值
    df = df.dropna(subset=['pe', 'pb', 'roe'])
    df = df[df['pe'] > 0]
    df = df[df['pe'] < 200]      # 剔除极端PE
    df = df[df['pb'] > 0]
    df = df[df['pb'] < 20]       # 剔除极端PB
    df = df[df['market_cap'] > 50]  # 市值大于50亿(避开小票噪音)
    return df

def filter_value_stocks(df, 
                        max_pe=30,
                        min_roe=10,
                        max_debt_ratio=70):
    """
    价值筛选器
    参数:
        max_pe: 最高允许PE
        min_roe: 最低允许ROE(%)
        max_debt_ratio: 最高负债率(此处简化处理)
    """
    # 基础筛选
    mask = (df['pe'] <= max_pe) & (df['roe'] >= min_roe)
    filtered = df[mask].copy()
    
    # 计算综合得分:PE越低越好,ROE越高越好
    # 先标准化到0-1区间
    filtered['pe_score'] = 1 - (
        (filtered['pe'] - filtered['pe'].min()) / 
        (filtered['pe'].max() - filtered['pe'].min() + 1e-6)
    )
    filtered['roe_score'] = (
        (filtered['roe'] - filtered['roe'].min()) / 
        (filtered['roe'].max() - filtered['roe'].min() + 1e-6)
    )
    
    # 综合得分(可调整权重)
    filtered['total_score'] = 0.5 * filtered['pe_score'] + 0.5 * filtered['roe_score']
    
    return filtered.sort_values('total_score', ascending=False)

def industry_analysis(filtered_df, top_n=5):
    """按行业统计入选股票数量"""
    industry_counts = filtered_df['industry'].value_counts().head(top_n)
    print("\n=== 入选股票最多的行业 ===")
    print(industry_counts)
    return industry_counts

# ========== 主程序 ==========
if __name__ == "__main__":
    raw = get_fundamental_data()
    clean = clean_data(raw)
    
    print(f"\n清洗后数据:{len(clean)} 只股票")
    
    # 运行筛选
    result = filter_value_stocks(clean, max_pe=25, min_roe=12)
    
    print(f"\n=== 低PE+高ROE精选(前20只)===")
    display_cols = ['name', 'code', 'industry', 'pe', 'pb', 'roe', 'total_score']
    print(result[display_cols].head(20).to_string(index=False))
    
    # 行业分布
    industry_analysis(result, top_n=8)

运行这段代码,你会得到一个经过筛选的股票列表。你可以自由调整max_pemin_roe的参数,观察入选股票数量和行业的变化。

25
12
模拟输出:当 max_pe=25, min_roe=12 时,预计筛选出约 180-220 只股票

知识自测

快速检验你的理解
1. 一家银行的PE为6,PB为0.8;一家科技公司的PE为60,PB为8。以下哪项说法最合理?
  • 银行明显比科技公司便宜,应该买银行
  • 两者行业不同,直接比较PE和PB没有意义
  • 科技公司肯定被高估了,应该做空
  • PE和PB越高越好,说明市场认可度高
  • 不同行业的合理估值区间差异巨大。银行股因高杠杆和低增长常年低估值,科技股因高增速享有估值溢价。跨行业直接比较会得出错误结论。
    2. 以下哪种ROE驱动方式被认为最可持续?
  • 高净利润率(产品定价权强)
  • 高财务杠杆(借钱扩张)
  • 高资产周转率(薄利多销)
  • 三者一样可持续
  • 高利润率驱动的ROE通常来自品牌壁垒或技术优势,最可持续。高杠杆在经济下行时风险最大,高周转率受运营波动影响较大。
    3. PEG指标最适合哪类股票?
  • 银行蓝筹股
  • 处于亏损的初创公司
  • 高增长科技股
  • 所有股票都同样适合
  • PEG = PE / 增长率。对于高增长股票,单纯看PE会觉得"贵",但结合增速后可能发现估值合理。银行股增速低,PEG通常意义不大;亏损公司无法计算PEG。
    4. 某周期股在景气高点PE为5,在景气低点PE为-10(亏损)。这说明什么?
  • 景气高点时非常便宜,应该重仓买入
  • 周期股PE低点往往是卖出信号,而非买入
  • PE为负时没有参考意义,应该只看PB
  • 说明这家公司财务造假
  • 周期股的利润波动极大。景气高点时利润暴增,PE反而很低,但这恰恰是周期即将见顶的信号;景气低点时亏损,PE为负。所以周期股看PE容易"低买高卖"变成"高买低卖"。

    下一章预告

    我们已经学会了用财务报表中的数字来选股。但你知道吗?在华尔街,最赚钱的基金正在用一种更"另类"的数据源——卫星图像、社交媒体情绪、信用卡消费记录、甚至是停车场的车辆数

    下一章《另类数据——新闻情绪、社交媒体、卫星图像》,我们将带你走进量化交易的"新边疆"。你会学到如何用AI分析新闻情绪,如何把非结构化的文本变成结构化的交易信号。准备好了吗?数据的世界,远比你想象的更广阔。

    课后行动清单
    1. 运行本章代码:在你的Python环境中运行低PE+高ROE筛选器,观察今天的筛选结果
    2. 调整参数:把max_pe从25改成15,再改成40,看看入选股票数量和行业的变化
    3. 加入新条件:尝试加入"市值>100亿"或"营收增速>10%",观察对结果的影响
    4. 保存模板:把filter_value_stocks函数保存为你自己的工具函数,下一章会用到

    觉得有收获?分享给同样对量化交易感兴趣的朋友

    上一章
    技术指标——MACD、RSI、布林带是怎么算出来的
    深入理解主流技术指标的计算原理和量化用法
    下一章
    另类数据——新闻情绪、社交媒体、卫星图像
    探索量化交易的最新数据源趋势和情绪分析方法

    如果这本书对你有所启发,欢迎支持我继续创作

    赞赏二维码