4. 估值因子(下):市销率(PS)、企业价值倍数(EV/EBITDA)、综合估值因子构建、回测框架搭建
各位好,我们接着聊估值因子。上一节我们把市盈率和市净率扒了个底朝天,这一节咱们把剩下的几个硬骨头啃下来。说实话,在实盘里,光看PE和PB是远远不够的,尤其是碰到那些亏损企业或者重资产公司,这两个指标基本就失灵了。这时候,PS和EV/EBITDA就派上用场了。
4.1 市销率(PS):营收为王
市销率,全称Price-to-Sales,公式很简单:PS = 总市值 / 营业收入。或者用每股价格除以每股营收也行。
为什么要有PS?我举个例子。你想想看,一家初创的互联网公司,年年亏损,PE是负的,根本没法看。但它的营收可能在翻倍增长。这时候PS就能帮你给它估值。说白了,PS看的是你的“地盘”有多大,而不是你赚了多少钱。
我个人习惯把PS用在以下几个场景:
- 周期性行业:比如钢铁、化工,利润波动大,但营收相对稳定。
- 亏损企业:PE失效时,PS是救命稻草。
- 高增长行业:比如早期的电商、SaaS公司。
但这里有个坑,我必须提醒你。PS有个致命弱点——它完全不考虑成本。一家公司营收100亿,成本99亿,净利1亿;另一家营收100亿,成本50亿,净利50亿。两家PS一样,但盈利能力天差地别。所以,PS必须结合毛利率或净利率一起看。
4.2 企业价值倍数(EV/EBITDA):真正的“全貌”估值
接下来这个因子,我个人认为是估值因子里的“王者”——EV/EBITDA。为什么这么说?因为它考虑了公司的资本结构。
先看公式:
EV(企业价值)= 总市值 + 总负债 - 现金及现金等价物
EBITDA = 息税前利润 + 折旧 + 摊销
你想想看,PE只看了股权部分,但一家公司还有大量债务啊。比如两家公司市值都是100亿,但A公司负债50亿,B公司负债10亿,你觉得谁更贵?显然是A公司。EV/EBITDA就把这个差异体现出来了。
EBITDA这个指标,说白了就是“经营活动产生的现金流”的近似值。它剔除了折旧摊销这些非现金支出,也剔除了利息和税的影响。这样不同资本结构、不同税率、不同折旧政策的公司,就可以放在一起比较了。
使用EV/EBITDA时,有几个要点:
- 行业中性化:不同行业的EV/EBITDA中枢差异很大,必须做行业调整。
- 注意非经常性损益:EBITDA要剔除一次性收益或损失。
- 结合负债率:EV/EBITDA低不一定好,如果公司负债率过高,风险也大。
4.3 综合估值因子构建
好了,单因子讲完了。但实战中,我从来不会只用一个估值指标。为什么?因为每个因子都有盲区。PE忽略了负债,PS忽略了成本,PB忽略了无形资产。所以,我们要做的是——综合估值因子。
我的做法是这样的:
- 因子标准化:把PE、PB、PS、EV/EBITDA分别做Z-score标准化,让它们都在同一个量级上。
- 方向统一:确保所有因子都是“越小越好”的方向。比如PE是越小越便宜,那就直接用。如果某个因子是“越大越好”,就取负号。
- 等权或加权合成:最简单的做法是等权相加。我个人习惯给EV/EBITDA和PE各30%权重,PB和PS各20%。
- 行业中性化:在每个行业内,对综合得分再做一次标准化,消除行业偏好。
下面是一个简单的Python实现框架:
import pandas as pd
import numpy as np
def build_composite_valuation_factor(df):
"""
df: 包含PE, PB, PS, EV_EBITDA列的DataFrame
返回综合估值因子得分
"""
# 1. 定义因子列表
factors = ['PE', 'PB', 'PS', 'EV_EBITDA']
# 2. 标准化(Z-score)
for f in factors:
mean = df[f].mean()
std = df[f].std()
df[f + '_z'] = (df[f] - mean) / std
# 3. 合成(等权)
df['composite_score'] = (df['PE_z'] + df['PB_z'] +
df['PS_z'] + df['EV_EBITDA_z']) / 4
# 4. 行业中性化(假设有industry列)
df['composite_score_neutral'] = df.groupby('industry')['composite_score'].transform(
lambda x: (x - x.mean()) / x.std()
)
return df
4.4 回测框架搭建
因子构建好了,怎么知道它有没有用?回测。下面我给大家搭一个最精简的回测框架。嗯,这里要注意,回测不是跑个代码就完事了,里面的坑多着呢。
先看核心逻辑:
class ValuationBacktest:
def __init__(self, data, factor_col, rebalance_freq='M'):
self.data = data # 包含日期、股票代码、因子值、收益率
self.factor_col = factor_col
self.rebalance_freq = rebalance_freq
def run(self):
# 1. 分组
self.data['group'] = self.data.groupby('date')[self.factor_col].transform(
lambda x: pd.qcut(x, 5, labels=False)
)
# 2. 计算每组收益
results = self.data.groupby(['date', 'group'])['return'].mean().reset_index()
# 3. 多空组合收益(组5 - 组1)
long_short = results.pivot(index='date', columns='group', values='return')
long_short['long_short'] = long_short[4] - long_short[0]
return long_short['long_short']
这个框架虽然简单,但已经包含了回测的核心要素:
- 定期调仓:每月或每季度重新计算因子值,调整持仓。
- 分组测试:把股票按因子值分成5组或10组,看多空收益。
- 收益计算:等权或市值加权计算每组收益。
最后,给大家展示一下综合估值因子的知识体系:
好了,这一节的内容就到这里。综合估值因子不是万能药,但它能帮你避开很多单一因子的大坑。下一节我们会聊到成长因子,到时候再给大家分享一些有意思的东西。
公众号:蓝海资料掘金营,微信deep3321