3、核心因子解析:市净率(PB)因子的计算逻辑、选股逻辑与实战陷阱

3.1 PB因子到底是什么?

市净率,英文叫Price-to-Book Ratio,简称PB。说白了,就是拿公司的市值跟它的净资产比一比。

公式很简单:

PB = 股价 / 每股净资产

或者换个写法:

PB = 总市值 / 净资产

净资产是什么?就是公司总资产减去总负债。你想想看,如果一家公司明天就破产清算,把所有资产卖掉还完债,剩下的钱就是净资产。所以PB衡量的是:你愿意花多少钱,去买这1块钱的净资产。

我个人习惯把PB理解成「溢价倍数」。PB=1,说明市场对这家公司的定价刚好等于它的净资产。PB=2,说明市场愿意花2块钱买它1块钱的家底。PB<1,那就是折价——市场觉得它的资产不值那个数。

核心要点:PB因子是价值投资中最经典的指标之一。格雷厄姆当年就是靠低PB策略赚了大钱。它的逻辑很朴素——买得便宜,风险就小。

3.2 PB因子的计算细节

嗯,这里要注意。PB的计算看起来简单,但实际做量化的时候,坑不少。

第一,净资产用什么口径?

财报上的净资产是「账面净资产」,用的是历史成本法。比如10年前买的厂房,现在可能已经翻了好几倍,但账上还是按买入价减去折旧算的。这就导致一个问题——账面净资产可能严重偏离真实价值。

我在项目中遇到过一家地产公司,它的土地储备是十几年前拿的,账面价值极低。但实际上那些地皮已经涨了5倍。用账面净资产算出来的PB高达3倍,看起来不便宜。可如果用重估后的净资产算,PB可能只有0.6倍。这就是典型的「假高PB」陷阱。

第二,负净资产怎么处理?

有些公司连年亏损,净资产变成负数。这时候PB就是负的。负PB怎么选股?没法选。我一般直接剔除这类股票,或者用百分位排名来处理。

第三,行业差异要命。

银行的PB常年低于1,科技股的PB动不动就5倍以上。你拿银行的PB跟科技股比,那完全没意义。所以做因子测试时,一定要做行业中性化处理。

来看一段Python代码,展示PB因子的计算和行业中性化:

import pandas as pd
import numpy as np

# 假设df包含股票数据
# 列:'code', 'date', 'market_cap', 'net_asset', 'industry'

# 计算PB
df['pb'] = df['market_cap'] / df['net_asset']

# 剔除负净资产
df = df[df['net_asset'] > 0]

# 行业中性化:在每个行业内计算PB的百分位排名
df['pb_rank'] = df.groupby(['date', 'industry'])['pb'].rank(pct=True)

# 最终因子值:排名越小越好(低PB)
df['pb_factor'] = df['pb_rank']

# 检查极端值
print(f"PB范围: {df['pb'].min():.2f} - {df['pb'].max():.2f}")
print(f"负PB数量: {(df['pb'] < 0).sum()}")

实战技巧:我建议用「对数PB」代替原始PB。因为PB的分布严重右偏,取对数后更接近正态分布,因子效果更稳定。

3.3 PB因子的选股逻辑

低PB选股的核心逻辑就一句话:买便宜货

但「便宜」不等于「好」。你想想看,一家公司PB只有0.3倍,可能不是市场错了,而是它真的只值那个价——比如夕阳行业、资产质量差、负债率过高。

所以PB因子很少单独使用。我一般把它跟其他因子组合:

  • PB + ROE:低PB + 高ROE,说明公司盈利能力好,但市场低估了它。这是经典的「便宜的好公司」策略。
  • PB + 负债率:低PB + 低负债,说明资产质量扎实,破产风险小。
  • PB + 股息率:低PB + 高股息,说明公司愿意分红,管理层对股东友好。

来看一个简单的多因子打分模型:

def score_stocks(df):
    """
    多因子打分:PB + ROE + 负债率
    """
    # 因子标准化
    df['pb_score'] = df['pb_rank']  # 低PB得分高
    df['roe_score'] = 1 - df.groupby('date')['roe'].rank(pct=True)  # 高ROE得分高
    df['debt_score'] = 1 - df.groupby('date')['debt_ratio'].rank(pct=True)  # 低负债得分高
    
    # 综合得分(等权)
    df['total_score'] = (df['pb_score'] + df['roe_score'] + df['debt_score']) / 3
    
    # 选前20%
    top_stocks = df[df['total_score'] >= df['total_score'].quantile(0.8)]
    
    return top_stocks

3.4 实战陷阱:我踩过的那些坑

做PB因子策略,我交过不少学费。下面这几个坑,你一定要注意。

陷阱一:价值陷阱

我曾经重仓过一只PB只有0.2倍的钢铁股。当时觉得便宜得离谱,结果呢?公司连续亏损,净资产每年都在缩水。PB从0.2变成了0.3——不是涨了,是净资产跌得比股价还快。这就是典型的「价值陷阱」:看起来便宜,实际上在毁灭价值。

避坑方法:一定要结合盈利指标。我后来加了一条硬性条件:过去3年ROE不低于8%。

陷阱二:周期股的PB失真

周期股在行业低谷时,净资产往往被大幅减值,PB看起来很高。等周期反转,净资产回升,PB又变得很低。你按低PB买入,可能正好买在周期顶点。

避坑方法:对周期股,我改用「周期调整PB」——用过去5年的平均净资产代替当期净资产。

陷阱三:金融股的PB特殊性

银行、保险这类金融股,PB天然就低。因为它们的高杠杆特性,净资产对风险的覆盖能力有限。我见过有人用PB选银行股,结果选了一堆坏账率最高的银行。

避坑方法:金融股单独处理,或者直接用「PB-坏账率」的复合指标。

3.5 一张图看懂PB因子

下面这张SVG图,总结了PB因子的核心逻辑和实战要点:

PB因子核心逻辑框架 PB = 市值 / 净资产 计算逻辑 • 净资产 = 总资产 - 总负债 • 注意:账面 vs 重估价值 • 剔除负净资产股票 • 行业中性化处理 选股逻辑 • 低PB = 便宜货 • 多因子组合: PB + ROE + 负债率 PB + 股息率 实战陷阱 ⚠ 价值陷阱 低PB + 低ROE → 净资产在缩水,越便宜越危险 ⚠ 周期股失真 周期低谷PB高,周期顶点PB低 → 用5年平均净资产 ⚠ 金融股特殊性 高杠杆行业,PB天然低 → 单独处理或复合指标 核心:便宜 + 好公司 + 避坑

3.6 实战中的PB因子优化

讲完陷阱,说说我怎么优化PB因子的。

第一,动态PB阈值。不同市场环境下,PB的合理区间不一样。牛市里PB普遍高,熊市里PB普遍低。我一般用滚动5年的PB百分位来判断当前是否处于低位。

第二,PB与市值结合。小市值公司的PB波动更大,更容易出现极端值。我习惯把PB因子跟市值因子做正交化处理,剔除市值的影响。

第三,PB的动量效应。低PB本身不够,还要看PB的变化趋势。如果一家公司PB从0.5涨到0.8,说明市场开始认可它了。我倾向于选「PB在低位但开始回升」的股票。

来看一段完整的因子测试代码:

def pb_factor_backtest(df, lookback=252):
    """
    PB因子回测框架
    """
    # 计算PB
    df['pb'] = df['market_cap'] / df['net_asset']
    
    # 剔除极端值(上下1%)
    lower = df['pb'].quantile(0.01)
    upper = df['pb'].quantile(0.99)
    df['pb_clipped'] = df['pb'].clip(lower, upper)
    
    # 滚动5年百分位
    df['pb_percentile'] = df.groupby('code')['pb_clipped'].transform(
        lambda x: x.rolling(lookback, min_periods=60).rank(pct=True)
    )
    
    # 选股条件
    cond1 = df['pb_percentile'] < 0.2  # PB处于历史低位
    cond2 = df['roe'] > 0.08           # ROE不低于8%
    cond3 = df['debt_ratio'] < 0.6     # 负债率不超过60%
    
    selected = df[cond1 & cond2 & cond3]
    
    return selected

我的经验:PB因子在A股市场的有效性周期大约是3-5年。2016-2018年低PB策略表现很好,2019-2020年成长股崛起时就不太行。所以不要死守一个因子,要动态调整权重。

好了,PB因子就讲到这里。记住三个关键词:便宜、好公司、避坑。下次你看到一只PB只有0.3倍的股票,先别急着买——问问自己:它是真的便宜,还是价值陷阱?