简书不维护了,欢迎关注我的知乎:波罗学的个人主页
自2018年2月份开始,由于一些可知的原因,我们的大A股已经持续了一年的下跌,近3600点跌到了最低的2500左右。大家都在说现在的市场估值已经相当低了。那今天我们就用数据说话,来具体看看当前的市场估值情况。
注:因为选取数据有部分缺失,故数据选取时间统一为2012年1月1日~2019年2月14日(单身狗在情人节规划这篇文章,怎苦字了得!)。非金融出生,加入部分自己的思考,有错请指正。
什么是估值
谈到股市,最常听到的就是诸如 "股市已经到达3000点了"、"股市刚刚突破了4000点"之类的话。此处的多少点上某种意义上是代表了人们愿意为股市付出的价。
那么估值呢?简言之就是估计的某件东西的价值,该数字通常是无法准确得知的。一家公司的估值,主要是由该公司当前情况和未来的发展潜力等多方面因素决定。市场的估值则是可以由这些大大小的公司来决定,可由其成分股通过某些算法计算而得,可参见指数估值计算方法。
那么如何来评价市场估值的高低呢?我们知道,准确的估值是无法得知的,大家常会用PE和PB来衡量估值高低,PE为市盈率,即市值/盈利,PB是市净率,即市值/净资产。当这些指标高于某个值时,我们就会认为当前的估值处于高位,反之则处于低位。我们可以此来简化估值的评估逻辑。
那么,下面将通过数据来分析这些指标,来评价下当前股市。
分析工具为Python,下面首先导入一些必要的python包,数据来自于TusharePro:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
import tushare as ts
api = ts.pro_api(token='认证token')
以啥分析市场
市场,通常可以用某些指数来代表。指数有很多,通常都是基于不同标准编制的。
我们这里将选取同花顺股票软件上的六大指数来进行分析,即上证综指(000001.SH)、深圳成指(399001.SZ)、创业板指(399006.SZ)、沪深300(000300.SH)、上证50(000016.SH)和中证500(000905.SH)。
关于各个指数的样本和一些选取的准则,可以自行网上搜索。
获取目标数据
接下来我们开始获取数据,并做一些相应的处理,如对数据按交易时间升序排列以便于后续的分析。
因为需要获取六大指数的数据,首先定义一些辅助变量:
index_codes = [
'000001.SH',
'399001.SZ',
'399006.SZ',
'000300.SH',
'000016.SH',
'000905.SH'
]
start_date = '20120101' # 开始日期
end_date = '20190214' # 结束日期
index_dict = {} # 存储指标数据
为了批量获取数据,定义一个单指数数据获取与处理的函数,返回值为pd.DataFrame类型,pandas为我们提供了很多常用的数据分析方法。
def get_index_valudation_indicator_df(code, start_date, end_date):
fields = 'trade_date,pe,pb'
data = api.index_dailybasic(
ts_code=code,
start_date=start_date,
end_date=end_date,
fields=fields
)
data = data.drop_duplicates()
data.index = pd.to_datetime(data['trade_date'])
data = data.sort_index()
return data
到这里,我们开始正式下载数据。一个for循环获取就把所有数据加载到index_dict中,多么简洁快速。代码如下:
for index_code in index_codes:
index_dict[index_code] = get_index_valudation_indicator_df(index_code, start_date, end_date)
先检查下各指数数据行数,确保成功获取数据:
for k, index_df in index_dict.items():
print(k, len(index_df))
输出如下:
000001.SH 1728
399001.SZ 1728
399006.SZ 1728
000300.SH 1728
000016.SH 1728
000905.SH 1728
都为1728行,一切正常!来简单观察一下数据,以上证综指为例打印出结尾5行数据:
index_dict['000001.SH'].tail(5)
输出如下:
trade_date pe pb
trade_date
2019-02-01 20190201 12.61 1.30
2019-02-11 20190211 12.77 1.32
2019-02-12 20190212 12.83 1.32
2019-02-13 20190213 13.08 1.35
2019-02-14 20190214 13.07 1.34
pandas为我们提供了一个info
方法可以查看数据的整体情况。如下:
index_dict['000001.SH'].info()
输出如下:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 1728 entries, 2012-01-04 to 2019-02-14
Data columns (total 3 columns):
trade_date 1728 non-null object
pe 1728 non-null float64
pb 1728 non-null float64
dtypes: float64(2), object(1)
memory usage: 54.0+ KB
可以看出我们的数据时间跨度为2012年01月04日至2019年02月14日,共1728条记录。不是从2012年1月1日开始当然是因为1月1日至1月3日是元旦假期。
注:最初数据获取时,选择了2010年1月1日~2019年2月14日,但数据检查时发现部分指数缺失,故退而求其次缩短了时间段。
开始正式分析
进入正题,我们该如何进行分析?先来谈一些个人对估值的看法。
我们经常会听到这样的说法,美股PE和PB为某个范围内时则表明当前的估值是合理的,若高于或低于该值就被认为是贵或便宜了。中国也是如此,不过A股通常会比美股的合理值高出一不少,这是否表明泡沫的存在?个人看法,中国经济长期处在高速发展阶段,所存环境不同与美股截然不同,对未来的预期高一点自然是可以理解的。简单的思考后,我总结出了如下几个观点:
- 合理的估值会随着大环境变化而变化;
- 合理的估值随着不同市场行业的预期不同而不同;
- 合理的估值在一定时间周期与大环境下是相对确定的;
基于以上这些看法,下面开始具体的分析。
A股当前的合理估值是多少
虽然从长期来看,合理的估值在不断变化,但短期内是有一个值可供我们参考的。那么我们如何获取到这个值呢?能想到的办法就是从历史数据中分析而来,可计算的几个指标有:平均数、频率分布最高区间(类似众数)、中位数。这里选取的是7年的数据,当然没有什么理由,在保证数据准确完备的前提下选的时间线尽量长点。下面开始来计算这些指标。
先定义一个函数计算历史估值的分布情况并返回最高分布区间,并计算出这个区间的平均数。
def get_interval_freq_max(data):
c = pd.cut(data, bins=10).value_counts() # 计算各区间分布数量,假设为10
p = c / c.sum() # 计算各区间分布频率
m = p.max() # 计算最大值
for k, v in p.items(): # 找出最大值的key,即所在区间
if m == v:
max_interval = k
return data[(data >= max_interval.left) & (data <= max_interval.right)].mean()
计算指标并输出,最后转化为pd.DataFrame类型以便于作图,下面为代码实现:
index_current_indicator_dict = {}
for k, index in index_dict.items():
index_current_indicator_dict[k] = {
'pb': index['pb'][-1],
'pb_mean': index['pb'].mean(),
'pb_high_feq': get_interval_freq_max(index['pb']),
'pb_median': index['pb'].median(),
'pe': index['pe'][-1],
'pe_mean': index['pe'].mean(),
'pe_high_feq': get_interval_freq_max(index['pe']),
'pe_median': index['pe'].median()
}
indicator_df = pd.DataFrame(index_current_indicator_dict)
print(indicator_df)
000001.SH 399001.SZ 399006.SZ 000300.SH 000016.SH 000905.SH
pb 1.340000 2.250000 3.840000 1.380000 1.170000 1.680000
pb_high_feq 1.601113 3.118865 5.752357 1.494908 1.205844 2.235625
pb_mean 1.605637 2.710625 5.342436 1.552286 1.343003 2.629954
pb_median 1.590000 2.640000 4.990000 1.500000 1.270000 2.480000
pe 13.070000 20.130000 34.870000 12.300000 10.230000 19.360000
pe_high_feq 11.022995 15.323544 35.389715 11.991063 10.478383 27.131933
pe_mean 13.755469 24.546250 55.418264 12.171325 10.062737 36.700735
pe_median 13.550000 21.475000 54.820000 11.970000 9.970000 34.140000
从输出的数据分析不够直观,下面我们来尝试通过图形方式展现,先来看PB:
columns = ['pb', 'pb_mean', 'pb_high_feq', 'pb_median']
indicator_df.loc[columns].T.plot(kind='bar', figsize=(15, 5))
输出如下:
读图可以得到如下的结论:
- 无论使用哪个指标作为合理估值,选取的6大指数PB(即蓝色柱)当前都处在低位;
- 创业板的PB最高,这或许与大家对创业板公司的成长有较高预期有关;
- 无论使用哪个指标作为合理估值,中证500当前PB与它们都有较大的差距;
再来看PE:
columns = ['pe', 'pe_mean', 'pe_high_feq', 'pe_median']
indicator_df.loc[columns].T.plot(kind='bar', figsize=(15, 5))
读上图会发现一些与PB指标相同的结论,比如创业板估值依然是最高,中证500当前被严重低估等。那么有哪些不同呢?一个最易得出的结论就是多数指数当前的市盈率在其他各指标中并非最低。不知道这是不是近期企业盈利下降有关。
总体而言,如果钟爱于指数投资、坚信价值回归,当前或许较适合增加中证500配置比重。
当前估值处在历史什么位置
以上的分析主要从总体角度观察。下面我们将从时间序列上观察估值的历史变化,而最好的方式从分位的角度来看问题。
我们先来计算各指数的估值处在近7年的什么分位,计算代码如下:
index_current_quantile_dict = {}
for k, index in index_dict.items():
index_current_quantile_dict[k] = {
'pb_percentile': stats.percentileofscore(index['pb'], index['pb'][-1]),
'pe_percentile':stats.percentileofscore(index['pe'], index['pe'][-1])
}
pd.DataFrame(index_current_quantile_dict)
输出如下:
000001.SH 399001.SZ 399006.SZ 000300.SH 000016.SH 000905.SH
pb_percentile 13.425926 33.767361 25.954861 23.350694 19.994213 4.687500
pe_percentile 45.949074 47.366898 10.358796 55.555556 57.725694 3.009259
从这张表可以得出哪些结论?所有指数的市净率都处在1/2分位以下,而对于市盈率,除了中证500和创业板指,都位于1/2分位附近。创业板指和中证500低估较为明显,特别是中证500,无论从净资产还是盈利角度来看都有很大的投资价值。
上面是计算最近的情况,我们也可以通过绘制历史的变化图,来看看不同时期估值指标的表现情况。同时可以添加相应的分位辅助线(包括1/4、1/2、3/4分位)来更好的对比。
以上证指数为例:
data_pe = index_dict['000001.SH'][['pe']].copy()
data_pb = index_dict['000001.SH'][['pb']].copy()
data_pe['pe_Q1'] = data_pe['pe'].quantile(0.25)
data_pe['pe_Q2'] = data_pe['pe'].quantile(0.5)
data_pe['pe_Q3'] = data_pe['pe'].quantile(0.75)
data_pb['pb_Q1'] = data_pb['pb'].quantile(0.25)
data_pb['pb_Q2'] = data_pb['pb'].quantile(0.5)
data_pb['pb_Q3'] = data_pb['pb'].quantile(0.75)
准备好数据,直接作出两指标的时间序列变化图:
fig = plt.figure()
ax_pe, ax_pb = fig.subplots(2, 1)
ax_pe.set_title('pe timeseries')
ax_pb.set_title('pb timeseries')
data_pe[['pe_Q1', 'pe_Q2', 'pe_Q3', 'pe']].plot(figsize=(12, 15), ax=ax_pe)
data_pb[['pb_Q1', 'pb_Q2', 'pb_Q3', 'pb']].plot(figsize=(12, 15), ax=ax_pb)
plt.show()
绘图结果:
可以看出,在2012年至2014年期间估值长期处于低位,足足有两年之久。所以说,虽然当前资产较为便宜,但并不代表就来一定会上涨。不过或许是蓄力越久,爆发就有力。众所周知,2014年下半年至2015年上半年的那一波疯牛市。