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因子的核心逻辑和实战要点:
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倍的股票,先别急着买——问问自己:它是真的便宜,还是价值陷阱?