⚠️ 免责声明:本章仅为教育与研究方法展示,不构成任何投资建议。 所有回测结果来自历史数据,不代表未来表现。投资有风险,决策需独立判断。
第二课:PE 分位数策略——便宜还是贵,要看和谁比¶
本章代码:
code/lesson2_pe_quantile.py运行方式:python lesson2_pe_quantile.py
2.0 策略概述¶
这是什么策略¶
PE 分位数策略是价值投资思想的量化实现。核心逻辑一句话:不关心 PE 的绝对值是多少,只关心当前的 PE 在历史上排第几。
PE=30 意味着什么?对于深证红利(历史 PE 9—42),PE=30 排在前 20%,偏贵。对于中证 500(历史 PE 17—97),PE=30 排在后 20%,偏便宜。同一个数字,不同指数,含义完全相反。
它回答什么问题¶
| 问题 | 白话 |
|---|---|
| 这个指数现在是贵了还是便宜了? | 看当前 PE 在历史 PE 分布中的位置 |
| 该不该买? | PE 在历史低位 → 买;PE 在历史高位 → 卖 |
| 仓位多大? | 极低 = 满仓;偏低 = 半仓;正常 = 持仓;偏高 = 减仓;极高 = 空仓 |
核心算法¶
每周末做一次判断: 1. 当前 PE = 近 5 周 PE 的均值(平滑,避免单周噪音) 2. 只用过去的 PE 计算四个分位阈值(20% / 30% / 60% / 80%) 3. 当前 PE 落在哪个区间,就对应哪个仓位(满仓 / 半仓 / 不动 / 减仓 / 空仓) 4. 空仓期间资金按无风险利率 3.5% 计息
完整代码见 2.2 节和 code/lesson2_pe_quantile.py。
输入和输出¶
输入:一个指数的周度 PE 序列 + 周度收益率
输出:每周的仓位信号(1 = 满仓, 0.5 = 半仓, 0 = 空仓)+ 净值曲线
代码量:约 40 行 Python
数据要求:必须有 PE 数据(免费源:akshare)
先记住一个数字¶
这个策略在 2008—2017 年的回测上,深证红利跑出了 +44.5% 的期间总超额。听起来很厉害——但请先记住:同一套参数,换到沪深300 上,同一时期是 -7.5%。
+44.5% 不是"策略被证明有效",是"你恰好选了深证红利 + 2008—2017 这段数据"。请带着这个认知往下读。
2.1 一个真实的问题¶
2018 年 1 月,上证 50 连涨 19 天,PE 从 11 飙升到 14。我的朋友老李给我打电话:"现在还能买吗?"
我没有回答。我说:"你先别看 PE=14 高不高。你看看上证 50 过去 20 年的 PE 历史——它在 2007 年到过 48,在 2014 年到过 7。14 在它的历史上排第几?"
老李沉默了。他不知道。大多数人也不知道。
"PE 贵不贵"不是一个绝对问题——是一个相对问题。PE 分位数策略就是用来回答这个问题的。
2.2 核心代码¶
先看最简单的部分。假设你已经有了每周的 PE 数据和收益率:
# pe_data: 每周 PE 序列,如 [38.8, 42.5, 41.5, ...]
# returns: 每周收益率,如 [0.046, -0.047, -0.045, ...]
n = len(pe_data)
WARMUP = 96 # 前 96 周不交易,用来建立历史分布
position = [0] # 初始空仓
for i in range(WARMUP, n):
# 第一步:当前 PE = 近 5 周均值(平滑)
current_pe = pe_data[i-5:i].mean()
# 第二步:历史上到今天为止,所有 PE 排序
history = sorted(pe_data[0:(i-1)])
# 第三步:找到四个分位阈值
q20 = history[int(i * 0.20)] # 排在后 20% 的值 → 便宜
q30 = history[int(i * 0.30)]
q60 = history[int(i * 0.60)]
q80 = history[int(i * 0.80)] # 排在前 20% 的值 → 贵
# 第四步:根据当前 PE 落在哪个区间,决定仓位
if current_pe <= q20:
position.append(1) # 极便宜 → 满仓
elif current_pe <= q30:
if position[-1] >= 0.5:
position.append(1) # 便宜 + 已有仓位 → 满仓
else:
position.append(0.5) # 便宜 + 空仓 → 半仓(谨慎建仓)
elif current_pe < q60:
position.append(position[-1]) # 正常区间 → 不动
elif current_pe < q80:
if position[-1] == 1:
position.append(0.5) # 偏贵 + 满仓 → 减到半仓
elif position[-1] == 0.5:
position.append(0) # 偏贵 + 半仓 → 清仓
else:
position.append(0) # 偏贵 + 空仓 → 继续空仓
else:
position.append(0) # 极贵 → 空仓
为什么是 20%/30%/60%/80% 这四个阈值¶
这四个数字不是拍脑袋选的。它们创造了一个"缓冲区",防止频繁交易:
0% 20% 30% 60% 80% 100%
|--------|--------|---------------|--------|--------|
满仓区 建仓区 持仓不动区 减仓区 空仓区
PE 在 30%—60% 之间 → 不动(这是"正常状态")
PE 穿过 30% → 加仓(不是满仓——留一半现金,确认趋势后再加)
PE 穿过 60% → 减仓(不是清仓——留一半仓位,确认顶部后再清)
空仓期间资金去哪了¶
# 空仓期间,资金按无风险利率 3.5% 年化计算
if position[i] == 0:
period_return = 0.035 / 52 # 一周的无风险收益
else:
period_return = position[i] * weekly_return[i]
net_value *= (1 + period_return)
这不是在赚大钱——是在保护本金。在 PE 高估的时期,你的资金在银行里赚 3.5%,而不是在市场里承受 -30% 的回撤。
2.3 用真实数据跑一遍¶
PE 数据在 data/ 目录,仓库已预打包:
- 旧数据(2008—2017):data/PE_old/深证红利PE策略.csv(原始研究导出)
- 新数据(2006—2026):data/399324_SZ_pe_weekly.csv(akshare 拉取,已清洗)
跑 python code/lesson2_pe_quantile.py,你会看到:
旧窗口(2008—2017)——深证红利:
| 指数 | 买入持有年化 | PE 策略年化 | 期间总超额 |
|---|---|---|---|
| 深证红利 | 4.2% | 9.3% | +44.5% |
| 沪深300(对照) | 1.1% | 0.1% | -7.5% |
同样的策略、同样的参数(WARMUP=96、20/30/60/80 分位、空仓 3.5% 计息): - 在深证红利上:+44.5% - 在沪深300 上:-7.5%
新窗口(2005—2026)——同一套参数:
| 指数 | 买入持有年化 | PE 策略年化 | 期间总超额 |
|---|---|---|---|
| 深证红利 | 0.04% | 10.9% | +553% |
| 沪深300 | 3.0% | 11.4% | +346% |
| 上证50 | 3.0% | 7.5% | +128% |
| 中证500 | 7.6% | 10.5% | +57% |
你看到了什么? 不是"+44.5% 消失了"——是"+44.5% 到底意味着什么"这个问题变了:
- 深证红利买入持有 20 年年化 0.04%——这个指数到今天还没回到 2008 年的高点。不是策略厉害,是买入持有太惨。
- 同一策略、同一窗口,换个指数就从 +44.5% 变成 -7.5%。回测数字首先取决于你选了哪段数据、哪个指数。
- 新窗口里策略年化 7.5%—11.4%,全部为正——不是因为策略"有效",而是因为它在暴跌时空仓,而 A 股许多指数十年不涨。规避暴跌 ≠ 预测能力。
把这三个观察连起来,就是这一课真正的教训:任何单一回测数字都没有意义——你必须问:数据到哪一年?指数是哪个?参数是看完数据后调的吗?
图 2-1:PE 策略 vs 买入持有净值曲线(深证红利,2008—2017 旧数据)。 蓝线 = PE 分位数策略,灰线 = 买入持有。策略在 2013—2015 年大幅跑赢——这正是 PE 从高位回落、策略空仓避过下跌的时期。同样的参数在沪深300 上是亏的(-7.5%)——记住这一点,再来看这张图。

2.4 为什么失效了:三个原因¶
原因一:趋势性上涨¶
深证红利 PE 从 2008 年的 9 涨到 2017 年的 42——这不是波动,是趋势。在趋势面前,"分位数"失去了意义:PE = 30 在 2010 年是高位,在 2016 年是低位。分位数告诉你"放在历史中排第几"——但如果历史本身就是单边上涨的,"排在前面"不等于"会跌回来"。
原因二:结构性断裂¶
2000—2017 年的 A 股是"城镇化 + WTO"驱动的增长故事。2018 年之后呢?贸易战、去杠杆、疫情、AI 革命。PE 和盈利的关系、盈利和经济的关系、经济和指数的关系——全变了。你无法用一个 20 年的历史分布,来预测一个结构性断裂之后的市场。
原因三:策略拥挤¶
2005 年没什么人用 PE 分位数选股。2025 年,每个量化基金、每个 ETF 策略、每个理财 App 都在做同样的事。当所有人都用同一个策略时,策略的机会在被你发现之前,已经被别人交易掉了。
2.5 那这个策略还有用吗¶
有用。但不是你想象的那种用法。
第一,它帮你避免了盲目投资。 2015 年 6 月,上证 50 PE 达到 48——历史 90% 分位以上。PE 策略会说"空仓"。就算后来的超额收益是零,光是"在 2015 年 6 月不买"这件事,就值了。
第二,它可以迁移。 PE 分位数的逻辑——"用历史分布判断当前贵贱"——不只适用于 PE。把它用到大宗商品价格上、用到汇率序列上、用到任何一个有历史数据的资产上。这是思维框架,不是交易策略。
第三,它教会你一个最重要的教训。 你亲眼看到 +44.5% 是怎么变成 -7.5% 的——只换了一个指数。以后有人给你看一个回测结果说"这个策略年化超额 50%",你第一反应不是兴奋——是问:"你的数据到哪一年?指数是哪个?参数是看完数据后调的吗?"
2.6 这一课教了你什么¶
- PE 分位数策略问的不是"PE=15 贵不贵",是"PE=15 在这个指数的历史上排第几"。
- 20%/30%/60%/80% 四个阈值创造了缓冲区——不频繁交易,不追涨杀跌。
- 空仓期间资金去无风险利率——不是赚钱,是保护本金。
- +44.5% 的超额是"数据窗口 + 指数选择"的产物——同一套参数换个指数就是 -7.5%。
- 策略的价值不在"用它来交易"——在"用它来思考"。
2.7 课堂问答¶
问:你刚说 PE 策略的 +44.5% 换个指数就变成 -7.5%。但这是不是说所有量化策略都没用?
不是。是说"回测好看"不能证明策略有效——只能证明在那个特定的数据上,参数碰巧有效。真正有效的策略要经过三个检验:不同的数据窗口、不同的指数、不同的参数。PE 策略在"换指数"这个检验上失败了。但动量策略(第九课)在 12/16 指数上都有正 alpha——它通过了检验。注意两者差别:一个策略的价值不取决于它最好看的那个数字,取决于它在不同数据上表现是否稳定。
问:那 PE 数据能不能自己算?不从 Wind 买?
可以。我们后来发现了 akshare——一个免费的中国金融数据 Python 库。它提供沪深 300、上证 50、中证 500 的 PE 历史数据,从 2005 年到现在,每天更新。这本书里所有更新后的数据都来自 akshare。你不需要花几万块买 Wind。
问:这个策略会不会错过科技股爆发?比如今年 AI 行情?
一定会。PE 策略的底层假设是\"PE 会均值回归\"。但科技股不是均值回归——是结构性跃升。英伟达的 PE 从 40 跳到 80,不是因为\"贵了\",是因为市场在定价一个还没发生的未来。PE 策略会说\"空仓\"——然后错过主升浪。这就是为什么你需要多个框架:PE 管\"便宜还是贵\",六大师(Cathie Wood)管\"这是不是颠覆性创新\",风控日历管\"现在是什么时间窗口\"。没有一个策略能回答所有问题。
动手练习(第 2 课)¶
- 入门:参数扰动。把 WARMUP 从 96 改成 52 和 200,把分位阈值 20/30/60/80 改成 10/25/75/90,重新跑旧窗口。+44.5% 还在吗?如果参数一改数字就变, 说明这个"发现"有多稳?
- 进阶:换指数。把 lesson2 的 CODE 从
399324.SZ(深证红利)换成000300.SH(沪深300),新旧窗口各跑一遍。你会发现"同一个策略的业绩" 完全取决于你选了哪个指数——请把两张表的数字并排放在一起看。 - 挑战:空仓利率敏感性。把空仓期间的 3.5% 改成 0% 和 5%, 观察旧窗口 +44.5% 怎么变。为什么"空仓吃利息"这个看似无害的假设, 会显著影响结论?
下节预告: PE 分位数看排名。均值-标准差策略看距离。一个问\"排第几\",一个问\"偏多远\"。第三课,我们看看后者在什么时候更好——以及在什么时候完全失效。