浅谈估值模型:从估值-收益分布看待市场投资价值

摘要及声明

1:本文从估值的角度,通过深入研究指数估值变化的特征,总结出市场几轮牛熊背后的规律,从而客观理性地判断目前市场的投资价值。技术方面,本文通过ipywidgets交互式控件实现数据的可视化和交互式展示。

2:本文主要为理念的讲解,模型也是笔者自建,文中假设与观点是基于笔者对模型及数据的一孔之见,若有不同见解欢迎随时留言交流;

3:笔者希望搭建出一套交易体系,原则是只做干货的分享。后续将更新更多内容,但工作学习之余的闲暇时间有限,更新速度慢还请谅解;

4:本文主要数据通过Wind金融终端获取;

5:模型实现基于python3.8;


目录

1. 市场估值与预期收益之间的关系

2. 市场的估值中枢变迁

3. 估值高低对未来预期收益的影响

4. 持有时间对未来预期收益的影响

5. 位置和时间哪个更重要?

6. 总结

7. 代码实现

8. 往期精选


1. 市场估值与预期收益之间的关系

        为了更直观的体现出估值与未来预期收益之间的关系,笔者通过散点图的方式进行刻画。如图一所示,横轴是指数的PE TTM倍数,纵轴是估值对应未来1年的预期收益。从散点分布来看,收益率大致呈现左高右低的趋势,但图中很多散点分布并不具备统一的规律,例如在20到20倍PE估值之间,既有高达近250%的未来预期收益,也有很多点分布在负收益区域。因此统计上很难直接说明未来预期收益与估值之间存在显著相关关系。

图一:上证指数PE TTM估值对应未来1年收益率(数据来源:Wind,笔者整理)

2. 市场的估值中枢变迁

        笔者对上证指数估值时间序列数据进行分析,希望找出散点分布不规则背后的原因。上证指数近30年几经沉浮,其PE指标的中枢也随着成分股的变化不断改变。从估值的角度看,目前PE倍数仅为13倍不到,处于历史较低水平。如今的指数较2005年7月19日的低点已经上涨了200.36%,但相应的估值水平却并没有任何提升,反而从16倍PE下滑至今的13倍PE。因此,直接将PE指标与过去历史中枢相比较得出的分位数很难直接说明当前估值水平高低,并对未来趋势做出前瞻性的判断。

图二:上证指数点位及PE TTM(数据来源:Wind,笔者整理)

        笔者认为估值中枢出现巨大历史性差异的主要原因是指数成分的变化。上证指数共经历过三次较大的行业结构变迁,第一次是从1995年到2005年,上证指数成分占比较大的行业主要集中在工业和能源行业;第二次是从2006年到2019年,金融行业异军突起,在这13年中每年都以40%左右的权重成为占比最高的行业。而金融行业中大部分公司估值常年处于低估值状态,这很大程度上直接拉低了指数的估值中枢;第三次是从2020年至今,金融行业的占比不断下降,而TMT、能源和材料的占比则不断上升。若考虑全A指数,万得全A中,金融板块占比仅12%,而在2015年,这一数字占比则达到24%。随着注册制的不断深化,更多科技企业IPO,笔者预计未来金融行业的权重占比还会进一步下降。

图三:万得全A 2024年3月行业权重占比(数据来源:Wind,笔者整理)

        但笔者认为这并不会直接引发上证指数估值中枢非常显著的提升。尽管注册制“万股齐发”的大背景下,未来会有更多科技类的高估值公司上市,但对于市场估值中枢的影响有限。一是虽然我国IPO发行目前存在较高溢价现象,上市后的均值回归将指数拉低;其次,尽管上市公司数量井喷,但退市公司数量依然非常低,市场将常年处于僧多粥少的局面。因此,值得资金持有的优质好公司依旧是稀缺资产,除了这少部分优质公司,更多的股票将面临长期无人问津的低估值状态,这很大程度上限制了指数整体的估值水平主动上升;最后,虽然金融行业权重占比不断下降,但金融板块巨大的体量依然是指数的压舱石,类似05年金融板块在上证指数中仅占比10%的场面在未来几年的角度上是很难再看到的。总的来看,笔者认为未来,至少近几年上证指数很难出现上世纪那样30-40倍PE左右的估值中枢。

图四:上证指数金融行业权重占比变化(数据来源:Wind,笔者整理)

3. 估值高低对未来预期收益的影响

        在认识到市场估值中枢的变迁后再看估值-收益分布图的散点就变得清晰一些,将几轮牛熊的顶底用不同颜色的散点标注后可得图五。从整个散点的分布来看,估值顶未来一年均处于负收益的亏损状态,例如08年和15年;而估值底的未来一年收益都显著大于0,例如05年、08年和14年。一个明显的例外是15年的估值顶和05年的估值底都位于20倍PE左右,这是由于05年的估值中枢已经显著高于15年导致的。尽管估值中枢不断发生偏离,但从图中的散点分布来看不难发现一个重要结论:在一轮牛熊中(估值中枢一致),估值水平与未来收益呈现明显的反比关系,估值越高,未来预期收益越低,估值越低,未来预期收益越高。因此,估值高低是决定未来预期收益的一个重要因素。市场高位时期买入的投资者往往都会遭受较大损失。

图五:上证指数PE TTM估值对应未来1年收益率(数据来源:Wind,笔者整理)

        除了标注出市场顶底的点,图中还有不少离群值。例如在25-30倍PE中,有一部分散点依旧代表了200%左右的预期收益;而在40-60倍PE的区间内,还有不少点并不属于2007年10月的估值顶,但它们同样代表了很高的亏损。因此,还需要从其它角度对离群值进行研究。

4. 持有时间对未来预期收益的影响

        导致这些离群值最主要的原因是未来持有时间的长短,由于各个估值散点的时间分布不同,部分散点对应未来的最高收益率可能并非是出现在一年的维度。如果改变未来收益统计的时间长短,则该散点图又会产生新的变化。如下图展示了估值对应未来一个月的收益率分布情况,当看未来一个月时,估值对应的收益率分布十分模糊,同样的估值水平对应的未来预期收益有正有负,统计学上也难以找到相关关系描述。

 图六:上证指数PE TTM估值对应未来1个月收益率(数据来源:Wind,笔者整理)

        当时间维度拉长到两年时间,之前难以解释的离群值已经消失,估值水平与未来收益呈现的反比关系更加明显。如图八所示:

 图七:上证指数PE TTM估值对应未来2年收益率(数据来源:Wind,笔者整理)

        当时间继续拉长到三年和四年的维度,离群值再一次出现。并且,随着时间的继续拉长,离群的散点逐渐变多且开始朝着高PE倍数方向分散开,但此时估值水平与未来收益呈现的关系开始变得模糊。例如在四年的维度上,30-50倍高估区间不仅分布着亏损超过50%的点,也还有未来收益率高达300%的点。这种现象不难解释:当时间拉得过长,区间内可能包含了不止一轮牛熊,因此预期收益既可以很高,也可以大幅亏损,此时再将估值的高低作为参考意义就不大了。总的来看可以得出结论,当时间维度过长时,估值对未来预期收益的指导意义就会变得很弱。从历史经验来看,短期10个月以内的估值-收益的关系都较为模糊;长期超过33个月,估值-收益的关系也会较为模糊;而10个月-33个月是估值-收益关系较为明朗的区间,此时将估值水平作为交易的参考指标有较强的指导意义。

图八:上证指数PE TTM估值对应未来4年收益率(数据来源:Wind,笔者整理)

5. 位置和时间哪个更重要?

        从上文已经知道了,位置够低再配合合理的持有时间可以创造想当可观的收益,可是10-33个月的范围想当大。而通过估值-收益的散点变化图可以看出,持有两年时间的未来预期收益是最大的,那么是不是说可以将两年时间作为一个精确的时间标尺作为参考?

        牛市方面,通过统计上上证指数月线级别5次较为重要的牛市阶段,笔者发现上证指数牛市平均持续时间为30个月,牛市期间平均涨幅为192.67%。但历史上出现的几次牛市数据均呈现较大离散度,例如持续时间最长的是1996年至2001年,长达65个月;而持续最短的是2014年至2015年,仅13个月。涨幅方面,最大达到461.29%,而最低仅29.5%。可以说,上证指数的牛市持续时间及最大涨幅并没有什么特定的规律,以固定的时间作为参考意义不大。

表一:牛市涨幅及其时间跨度统计表(数据来源:Wind,笔者整理)

牛市区间

持续时间/月

最大涨幅

1996.02-2001.06

65

312.62%

2005.06-2007.10

29

461.29%

2014.05-2015.05

13

127.58%

2016.03-2018.01

23

29.50%

2020.04-2021.12

21

32.35%

平均值

30.2

192.67%

        熊市方面,通过统计上上证指数月线级别6次较为重要的熊市,笔者发现上证指数熊市平均持续时间为24.5个月,短于牛市的30个月。熊市期间平均跌幅为41.21%,从几轮熊市特点来看,指数几轮熊市具有时间短,且往往跌幅较大的特征。与牛市类似,历史上出现的几次熊市数据均呈现较大离散度。持续时间最长的是2001年至2005年,长达47个月;而持续最短的是2015年至2016年,仅9个月。跌幅方面,最大达到70.97%,而最低仅23.4%。可以说,上证指数的熊市持续时间及最大涨幅也并没有什么特定的规律,以固定的时间作为参考意义不大。

表二:熊市跌幅及其时间跨度统计表(数据来源:Wind,笔者整理)

熊市区间

持续月数

最大跌幅

2001.07-2005.06

47

44.77%

2007.11-2008.10

12

70.97%

2009.12-2013.06

43

38.05%

2015.06-2016.02

9

41.71%

2018.02-2018.12

11

28.35%

2022.01-2024.01

25

23.40%

平均值

24.5

41.21%

6. 总结

        总的来看,A股牛熊周期并没有什么特定的规律,以固定的时间投资作为参考意义不大。这就导致了一个违反大多数投资者直觉的结果:“熊市已经进行了很长时间”跟“熊市是否快要结束了”的关系很弱。结合之前估值高低的结论,笔者认为:估值的高低比时间的长短更重要,与其猜测牛市什么时候会来,熊市什么时候结束,最后陷入自我否定,不如把握住估值的高低位置。

        自上证指数2021年12月14日见顶以来,指数以及调整2年有余。这期间叠加新冠封控、地区冲突和经济下行影响,指数经历了较大的波动。截至2024年2月5日,上证指数下跌26.59%,PE11.68倍,位于历史近十年分位数10.85%。从历史来看,该位置是较低且性价比较高的。但自2月5日指数见底以来,上证指数已最高反弹了13.55%,PE为12.98倍,估值分位目前位于34.28%。从绝对位置来看即使是经过一轮反弹,指数的估值也并不算高。但相对于2月5日的低点来说性价比会稍低一些,后续如果指数层面再出现低位将是不错的入场时机。不过也需要注意的是,通过估值-收益分布图来看市场位置具有历史不代表未来问题,是不是要赚这种历史重现,均值复归的钱是需要投资者进一步考虑的。

7. 代码实现

        本期的技术内容比较简单,就是一个简单的可视化,但由于涉及到散点图从1个月到4年时间逐月的变化,如果简单通过Matplotlib进行逐张散点图的生成,就会有48张图片,想当繁琐。笔者选择ipywidgets模块的交互式控件,通过一个滑块即可控制散点图进行动态展示。

        本文的数据已经从Wind上下载好,笔者使用的是周频数据。首先导入需要的模块和数据,简单对数据进行处理后得到1171 条数据,包含上证指数PE TTM和收盘价等数据:

import pandas as pd
import matplotlib.pyplot as plt
import ipywidgets as widgets
from ipywidgets import interact

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示matplotlib的中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

## 读取数据
data = pd.read_excel("000001.SH.xlsx")
data["交易日期"] = data["交易日期"].apply(lambda x: x[:4]+x[5:7]+x[8:10])
data["交易日期"] = data["交易日期"].astype("int")

print(data)
交易日期	市盈率TTM	指数点位	分位点	危险值	中位数	机会值	标准差(+1)	平均值	标准差(-1)	z 分数
0	20010209	61.49	1956.97	98.80	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
1	20010216	60.35	1941.96	98.21	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
2	20010223	60.06	1936.35	98.12	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
3	20010302	60.61	1985.11	98.46	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
4	20010309	60.70	2011.66	98.55	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
...	...	...	...	...	...	...	...	...	...	...	...
1166	20240202	11.75	2730.15	12.65	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
1167	20240208	12.26	2865.90	17.35	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
1168	20240223	12.86	3004.88	24.27	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
1169	20240301	12.94	3027.02	25.64	31.5	15.87	12.53	34.6	21.61	8.63	-0.66
1170	20240305	13.01	3047.79	26.24	31.5	15.87	12.53	34.6	21.61	8.63	-0.66

1171 rows × 11 columns

        接下来通过ipywidgets的装饰器interact()进行动态可视化,但需要加一个@ 符号用于定义装饰器,该装饰器必须搭配函数体使用,函数体相当于希望实现的效果,例如生成一个散点图,而装饰器interact()可以将值不断的放进函数变量中进行显示。装饰器的功能十分强大,不仅仅可以展示图片,文字也可。

        下面举一个非常简单的例子,通过手动移动滑块就可以动态将0到100的数字打印出来。

@interact(x=(0, 100))  
def my_function(x):  
    print(x)

        下面通过date_select参数动态的计算出指数在未来某个区间的涨跌幅,然后生成图片。

@interact(date_select = widgets.IntSlider(min=4, max=200, step=4,value = 0, layout={"width":"80%"}))
def update_plt(date_select):
    plt.figure(figsize=(10,6))
    df = data.copy()
    df["year_pct"] = (df["指数点位"].shift(-date_select) - df["指数点位"])/df["指数点位"]
    df.dropna(inplace=True)
    plt.scatter(df["市盈率TTM"].values, df["year_pct"].values, s=20, label="估值-收益分布")
    plt.vlines(ymin=min(df["year_pct"]), ymax=max(df["year_pct"]), x = data["市盈率TTM"].values[-1], color="r", linestyle="--", label="当前估值水平")
    plt.hlines(xmin=min(data["市盈率TTM"]), xmax=max(data["市盈率TTM"]), y = 0, color="black", linestyle="--", label="0收益率", alpha = 0.4)
    plt.xticks(size=15)
    plt.yticks(size=15)
    plt.xlabel("PE TTM", fontsize = 15)
    plt.ylabel("收益率(%)", fontsize = 15)
    plt.legend(fontsize = 15)
    plt.title(f"上证指数PE TTM估值对应未来{int(date_select/4)}月收益率", fontsize=20)# 周度数据,所以除以4将单位改成月频
    plt.show()

        当然,通过将牛市熊市的散点用不同颜色标注出来,即可得到笔者文中的图片,例如:

    plt.scatter(df["市盈率TTM"][(df["交易日期"]>20050301) & (df["交易日期"]<20060603)].values, 
                df["year_pct"][(df["交易日期"]>20050301) & (df["交易日期"]<20060603)].values, 
                color = "red",s=20, label="2005年7月估值底")
    plt.scatter(df["市盈率TTM"][(df["交易日期"]>20071001) & (df["交易日期"]<20080201)].values, 
                df["year_pct"][(df["交易日期"]>20071001) & (df["交易日期"]<20080201)].values, 
                color = "orange",s=20, label="2007年10月估值顶")
    
    plt.scatter(df["市盈率TTM"][(df["交易日期"]>20080901) & (df["交易日期"]<20090101)].values, 
                df["year_pct"][(df["交易日期"]>20080901) & (df["交易日期"]<20090101)].values, 
                color = "darkgreen",s=20, label="2008年9月估值底")
    plt.scatter(df["市盈率TTM"][(df["交易日期"]>20090701) & (df["交易日期"]<20100401)].values, 
                df["year_pct"][(df["交易日期"]>20090701) & (df["交易日期"]<20100401)].values, 
                color = "lawngreen",s=20, label="2009年7月估值顶")
    
    plt.scatter(df["市盈率TTM"][(df["交易日期"]>20121101) & (df["交易日期"]<20140801)].values, 
                df["year_pct"][(df["交易日期"]>20121101) & (df["交易日期"]<20140801)].values, 
                color = "darkblue",s=20, label="2012-2014年估值底")
    plt.scatter(df["市盈率TTM"][(df["交易日期"]>20150401) & (df["交易日期"]<20150601)].values, 
                df["year_pct"][(df["交易日期"]>20150401) & (df["交易日期"]<20150601)].values, 
                color = "blue",s=20, label="2015年6月估值顶", alpha=0.5)
    
    plt.scatter(df["市盈率TTM"][(df["交易日期"]>20181001) & (df["交易日期"]<20190201)].values, 
                df["year_pct"][(df["交易日期"]>20181001) & (df["交易日期"]<20190201)].values, 
                color = "purple",s=20, label="2018年12月估值底")

        最后展示一下滑动起来的效果:

 图九:估值-收益分布的动态可视化展示

8. 往期精选

 往期精选
系列文章传送门实现方式
权益投资估值幻觉Python
PB指标与剩余收益估值Python
Fama-French及PSMPython
GK模型看投资的本质Python
增速g的测算Python
PE指标平滑Python
PE BandPython
分类树算法R
蒙特卡洛模拟Python
全连接神经网络模型Python
  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Simon Cao

创作不易,您的鼓励将是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值