跳转至

⚠️ 免责声明:本章仅为教育与研究方法展示,不构成任何投资建议。 所有回测结果来自历史数据,不代表未来表现。投资有风险,决策需独立判断。

第二课: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% 到底意味着什么"这个问题变了:

  1. 深证红利买入持有 20 年年化 0.04%——这个指数到今天还没回到 2008 年的高点。不是策略厉害,是买入持有太惨。
  2. 同一策略、同一窗口,换个指数就从 +44.5% 变成 -7.5%。回测数字首先取决于你选了哪段数据、哪个指数。
  3. 新窗口里策略年化 7.5%—11.4%,全部为正——不是因为策略"有效",而是因为它在暴跌时空仓,而 A 股许多指数十年不涨。规避暴跌 ≠ 预测能力。

把这三个观察连起来,就是这一课真正的教训:任何单一回测数字都没有意义——你必须问:数据到哪一年?指数是哪个?参数是看完数据后调的吗?

图 2-1:PE 策略 vs 买入持有净值曲线(深证红利,2008—2017 旧数据)。 蓝线 = PE 分位数策略,灰线 = 买入持有。策略在 2013—2015 年大幅跑赢——这正是 PE 从高位回落、策略空仓避过下跌的时期。同样的参数在沪深300 上是亏的(-7.5%)——记住这一点,再来看这张图。

图 2-1

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 这一课教了你什么

  1. PE 分位数策略问的不是"PE=15 贵不贵",是"PE=15 在这个指数的历史上排第几"。
  2. 20%/30%/60%/80% 四个阈值创造了缓冲区——不频繁交易,不追涨杀跌。
  3. 空仓期间资金去无风险利率——不是赚钱,是保护本金。
  4. +44.5% 的超额是"数据窗口 + 指数选择"的产物——同一套参数换个指数就是 -7.5%。
  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 课)

  1. 入门:参数扰动。把 WARMUP 从 96 改成 52 和 200,把分位阈值 20/30/60/80 改成 10/25/75/90,重新跑旧窗口。+44.5% 还在吗?如果参数一改数字就变, 说明这个"发现"有多稳?
  2. 进阶:换指数。把 lesson2 的 CODE 从 399324.SZ(深证红利)换成 000300.SH(沪深300),新旧窗口各跑一遍。你会发现"同一个策略的业绩" 完全取决于你选了哪个指数——请把两张表的数字并排放在一起看。
  3. 挑战:空仓利率敏感性。把空仓期间的 3.5% 改成 0% 和 5%, 观察旧窗口 +44.5% 怎么变。为什么"空仓吃利息"这个看似无害的假设, 会显著影响结论?

下节预告: PE 分位数看排名。均值-标准差策略看距离。一个问\"排第几\",一个问\"偏多远\"。第三课,我们看看后者在什么时候更好——以及在什么时候完全失效。