python行业中性_用Python分析指数: 11月16日热门指数Z值表
衡量市场,指数高低是一个难题!
价值投资者很难知道,现在是高估,还是低估? 买的是便宜还是,贵了? 应该现在买/卖,还是再等等?
针对这个问题,我在网上看到了一些量化的处理方法。例如:平均数法,中位数法,比例法等等。这种方法往往过于简单,只能衡量集中度。不能衡量离散度和概率。
也许统计方法中的标准差Z值法更加适合。既可以衡量某个指数的指标的集中度,还可以衡量离散度,和风险情况。尽管指数的数据也不是完美的正态分布,但Z值法依然存在较大参考意义。
我的观点
- Z值越大,越高估。因为大数定理认为:Z>1, Z>2,意味着继续变大的可能性小于16%, 5%。
- Z值越小,越低估。因为大数定理认为:Z<-1, Z<-2,意味着继续变小的可能性小于16%, 5%
- 综观550多只指数的历史数据。绝大部分指数的Z值都在-2,3之间。
- 注:少数的能源,金属类指数曾经出现过短暂疯狂的。Z值法就不太适用
我使用Python的Pandas 和 Matplotlib 等工具,加上一些渠道获得的指数数据(尤其是市盈率),做了这个工具。主要目的是:
- 方便自己定投使用。知道何时开始定投,何时停止定投,何时止盈。 (目前还没有止盈过)
- 结合统计学,熟悉Python的基本数据分析方法。
- 网上分享给愿意参考的人,交流和学习
分享是对自己最好的投资!
欢迎指正。
上个月,文章发表后在雪球和知乎上得到的不少朋友的赞和关注。非常感谢支持。 这个月指数上上下下,貌似熊市来了,又貌似牛市来了。 不同的时间跨度和评估角度看,就有不同的结果。
本月增加了热门指数的Z值高低表,并附上了该指数数据年份跨度的时间
首先,献上截至2017年11月16日的热门指数Z指高低表。
1 Python 基础模块初始化
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
!free -h
# 以下代码是为了显示正文正常
import matplotlib as mpl
import matplotlib.font_manager as font_manager
path_eng = '/usr/share/fonts/chinese/REFSAN.TTF'
path_CHN = "/usr/share/fonts/chinese/simhei.ttf"
prop = font_manager.FontProperties(fname=path_CHN) #Set the microsoft sans serief as default font family. if show chinese test, set path_CHN instead.
#prop.set_weight = 'light'
mpl.rcParams['font.family'] = prop.get_name()
Today = "2017年11月17日"
2 数据库导入
#import data
#数据源:UQer.
#数据采集是另外一套程序,由于UQer中文支持,图形支持不太好,下载数据到本地来进一步处理。
#8月份开始,UQ停止了数据下载功能。现在恢复了,不知道能用多长时间。先用着再说。
sec_map = pd.read_hdf("uqer/sec_map.h5","map") # sec_map 包含了约2800多个指数,实际指数约550只
#sec_map = sec_map.set_index("ticker")
history = pd.read_hdf("uqer/uq_history.h5","history") #hisotry 包含了从2004年到2017年11月16日的指数数据( 大约86万条数据).
print(sec_map.columns)
print(history.columns)
history.info()
3 定义指标-画图函数根据大数定律,在正态分布情况下,Z值=0,左右概率是50% Z值在(-1,+1)左右的概率合计是68%, Z值在(-2,+2)左右区间的概率合计是95%。例例:Z值=1, 其他数据大于1的概率<=84%,小于1的概率>=16%。 可近似认为:目前已经高估了
例:Z值=-1, 其他数据小于-1的概率<=84%,大于-1的概率<=16% 可近似认为:目前已经低估了
def show_KPI(history,ticker ="000001",source="uq",KPI="Close"):
#get the ticker shortName for easy understanding
shortname =sec_map.loc[ticker]["secShortName"] #get the security index chinese name
#check if the request KPI is compatible with database
if source =="uq":
if KPI not in ["Close","TurnoverValue","PE1","PE2"]:
return
elif source =="csi":
if KPI not in ["Close","Turnover","P/E1","D/P1"]:
return
# setup two time span, short for this year(2017),long for full history
timespan={7:"2017",
6:"2016",
5:"2015",
4:"2014",
3:"2013",
2:"2012",
1:"2011",
0:"2007:2016"} #define the time span for each year. 0 is for full range(2007~2017)
history= history.loc[ticker][KPI] #show the selected year KPI performances
history_thisyear =history.loc[timespan[7]]
history_compare =[history,history_thisyear]
#his=history.swaplevel().sort_index().loc["2014"]
#initialize the matplot, chinese font and size and qty of subplots
fig, axes = plt.subplots(1,2, figsize=(10,5),sharey=True)
for i,v_history in enumerate(history_compare): #0 for full histry, 1 for 2017
mean=v_history.mean()
std =v_history.std()
last=v_history.tail(1)
#print(last.index.strftime('%Y-%m-%d'),last.values[0])
fig.axes[i].plot(v_history,"b")
#xticks = v_history.date_range(start=dStart, end=dEnd, freq='W-Tue')
#fig.axes[i].set_xticklabels(rotation=30)
fig.axes[i].legend(loc='best')
if np.isnan(mean) or np.isnan(std):
pass
else:
fig.axes[i].axhline(mean,color="y",label ="mean")
fig.axes[i].axhline(mean + std,color="r",label ="+Z1")
fig.axes[i].axhline(mean - std,color="g",label ="-Z1")
#fig.axes[i].set_title(today)
fig.axes[i].axhline(round(mean +2*std,2), linestyle ="dashed",c="k",label="+Z2",lw=0.5)
fig.axes[i].axhline(round(mean -2*std,2), linestyle ="dashed",c="k",label="-Z2",lw=0.5)
today="{2} {0}:{1:,.2f}".format(KPI,last.values[0],last.index.strftime('%Y-%m-%d'))
fig.suptitle("({0} _{1}) KPI performance \n {2} ".format(shortname, ticker,today))
fig.autofmt_xdate(rotation=45, ha='right')
plt.subplots_adjust(wspace=0, hspace=0.5)
plt.show()
4 历史数据分组正态化处理 -获得Z值
infolist = ["Close","PE1","PE2","PB1","PB2","TurnoverValue","TurnoverVol"]
UQ_Stat=pd.DataFrame()
def COEV(x):
return( x.std()/x.mean())
def init(data_grp,column):
#print(df.head(2))
#df_grp =df.groupby(level="ticker")
df_Stat = data_grp[column].agg(['count',np.mean, np.std,COEV,'last'])
df_Stat["Zscore"]=(df_Stat["last"]-df_Stat["mean"])/df_Stat["std"]
df_Stat["Group_Type"]=column
#print(df_Stat.columns)
return df_Stat.sort_values(by="Zscore")
History_grp=history.groupby(level="ticker")
for info in infolist:
UQ_Stat=UQ_Stat.append(init(History_grp,info))
print("{0} 只指数将被分析".format(UQ_Stat.groupby(level=0).count().shape[0]))
5 热门指数当日和历史图
如该名单有侵权,我受到通知后,将立刻删除。热门指数主要参考ETF拯救世界和银行螺丝钉两位雪球大V的讨论文章
BigVlist=["000015","000922","CSPSADRP","500SNLV","000170","399975","000905","399989","000827","399006","000991","000300"]
mylist=["399417","399971","399814","000852"]
skiplist = ["CSPSADRP","500SNLV","399975","000170"]
hotlist=(BigVlist + mylist)
for i in skiplist: hotlist.remove(i)
"""
Hotlist 中是E大和钉大关注的指数,里面有些是重合的。
个人感觉E大比较注重资产配置,投资节奏;钉大比较注重现金流,即每个投资品种的股息率,回报率。
上证红利指数(000015)、
中证红利指数(000922)、
标普A股红利机会指数(CSPSADRP)、#缺数据,略
中证500行业中性低波指数(500SNLV),#缺数据,略
50AH优选 (000170) #缺数据,略
证券指数(399975) #缺数据,略
中证500(000905)
中证医疗(399989)
中证环保(000827)
创业板(399006)
全指医药(000991)
"""
mask1 = UQ_Stat.index.isin(hotlist)
mask2 = UQ_Stat["Group_Type"]=="Close"
mask = mask1 & mask2
tmp_count=UQ_Stat.loc[mask,"count"]/250
tmp =UQ_Stat.pivot(columns="Group_Type",values="Zscore").loc[hotlist]
tmp=pd.concat([tmp,tmp_count],axis=1)
tmp=tmp.join(sec_map[["secShortName"]])
columns_E2Cmap = {"secShortName":"名称",
"Close": "收盘价_Z值",
"PB1": "市净率_Z值",
"PE1": "市盈率Z指",
"TurnoverValue":"成交额_Z值",
"TurnoverVol":"成交量_Z指",
"count":"交易记录(年)",
#"Group_Type":"指标类型",
}
tmp=tmp[["secShortName","PE1","Close","PB1","TurnoverValue","TurnoverVol","count"]].rename(columns=columns_E2Cmap
)
tmp.index.name="代码"
print("2017年11月16日热门指数估值高低表")
print("交易记录年数约长越可靠")
tmp.sort_values(by=["交易记录(年)","市盈率Z指","收盘价_Z值"],ascending=False).round(2).style.bar(align="zero", color=[ '#5fba7d','#d65f5f',],width=100/2)
6 全市场概览 - (价格,市盈率,市净率)查看和比较目前所有指数的Z值平均数[-0.5,0.5] 常态
小于-0.5,市场低估
大于0.5, 市场高估活跃
#print(UQ_Stat.shape)
#shortname =sec_map.loc[ticker]["secShortName"]
columns =["PE1","TurnoverValue","Close",]
fig,axes=plt.subplots(1,3,figsize=(15,5), sharex=True, sharey=True)
fig.suptitle("{0}指数Z值频数图(2007-2017) ".format(Today))
for i in range(len(columns)):
mask=UQ_Stat["Group_Type"]==columns[i]
UQ_Result=UQ_Stat.loc[mask]
#print(UQ_Result.shape,fig.axes[i])
mean = UQ_Result["Zscore"].mean()
skew = UQ_Result["Zscore"].skew()
mean_limit=0.5
#print(columns[i],mean,skew)
if (mean>=(0-mean_limit) )and (mean<=mean_limit):
color_mean = "blue"
elif mean>mean_limit:
color_mean ="Y"
elif mean<(0-mean_limit):
color_mean ="g"
fig.axes[i].hist(UQ_Result["Zscore"],bins=50,color=color_mean)
fig.axes[i].set_title("{0}".format(columns[i]))
fig.axes[i].axvline(mean,color="k",linestyle='--')
fig.axes[i].set_xlabel("{0}".format(columns[i]))
#fig.axes[i].set_ylabel("指数频数")
#UQ_Result["Zscore"].hist(bins=50)
#fig.axes[i].legend()
#mask2=UQ_Result["Zscore"]<=0
print("综合指数之-{0}\t\t Z值{1:.3f}".format( columns[i],mean))
space =0.2
plt.subplots_adjust(wspace=0, hspace=space)
Z值频数图说明:横轴是从-3到+3的Z值范围,Y轴是指数的数量。 Z值=0时平均情况,不高不低。
PE1图(指数18日市盈率Z值):参考度:####当日约550只指数市盈率的Z值频数图。
正常波动:绝大多数指数市盈率在[-1,1]之间。
高估: 右侧,其中约有50只指数市盈率Z值>1,存在较大风险。约10只指数市盈率>2,异常高估(风险非常大)
低估: 左侧,其中约有10只指数市盈率Z值<-1。 长期定投买入,是不错的产品。
TurnoverValue(指数的18日市盈率成交量Z值): 参考度:###当日约550只指数市盈率的Z值频数图。 正常,高估,低估分析方法参考市盈率图。
Close图(指数的18日收盘价Z值图) 参考度:##当日约550只指数市盈率的Z值频数图。 正常,高估,低估分析方法参考市盈率图。
收盘价是绝对值,不像市盈率是相对值。因此中间线不是常规的0,而是大约1.3左右。
考虑到过去10年GDP每年增长8%,10年后经济增量,企业业绩增长,指数的绝对值也在增长。
7 3年时间以上的指数Z值
7.1 市盈率Z值最高5个指数。 某指数与自己过去历史的市盈率相比,现在所处的位置。
最低5个指数。 某指数与自己过去历史的市盈率相比,现在所处的位置。 我的观点Z值越大,越高估。因为大数定理认为:Z>1, Z>2,意味着继续变大的可能性小于16%, 5%。我的观点Z值越小,越低估。因为大数定理认为:Z<-1, Z<-2,意味着继续变小的可能性小于16%, 5% 综观550多只指数的历史数据。绝大部分指数的Z值都在-2,3之间。 注:少数的能源,金属类指数曾经出现过短暂疯狂的。Z值法就不太适用
7 3年时间以上的指数Z值
7.1 市盈率Z值最高5个指数。 某指数与自己过去历史的市盈率相比,现在所处的位置。
最低10个指数。 某指数与自己过去历史的市盈率相比,现在所处的位置。 我的观点Z值越大,越高估。因为大数定理认为:Z>1, Z>2,意味着继续变大的可能性小于16%, 5%。我的观点Z值越小,越低估。因为大数定理认为:Z<-1, Z<-2,意味着继续变小的可能性小于16%, 5% 综观550多只指数的历史数据。
注:在过去的1个月,10月到11月,许多指数出现了大幅下跌。太多的指数可以选,故本月发布最低的10个指数Z值
ndays = 750
Zscorelimit = -0.1
Type ="PE1"
mask1 = (UQ_Stat["Zscore"]
mask3 = (UQ_Stat["count"] >=ndays)
mask2 = UQ_Stat["Group_Type"] == Type
mask = mask3 & mask2
UQ_Z1M =UQ_Stat.loc[mask,["Zscore","last","Group_Type"]].drop_duplicates()
UQ_Z1M=UQ_Z1M.join(sec_map[["secShortName"]]).sort_values(by="Zscore")
UQ_Z1M=UQ_Z1M.rename(columns={"Zscore": "Z值",
"last": "最新数据",
"Group_Type":"指标类型",
"secShortName":"名称"
}
)
UQ_Z1M.index.name="代码"
#idx = pd.IndexSlice
#UQ_Z1M=UQ_Z1M.set_index("指标类型",append=True)
#mask = UQ_Z1M["名称"].str.contains("餐")
#UQ_Z1M[mask]
print(" 550指数市盈率Z值 最高5个和最低10个指数")
UQ_Z1M.iloc[np.arange(-5,10)].round(2).sort_values(by="Z值").style.bar(subset=["Z值"],align="zero", color=[ '#5fba7d','#d65f5f',],width=100/2)
7.2 指数市盈率Z值和指数收盘价Z值加权表
mask1 =(UQ_Stat["count"]>750)
#mask2 =(UQ_Stat["Zscore"]<0.5)
mask = mask1
Weight_Close = 0.2
Weight_PE1 = 0.8
tmp =UQ_Stat[mask].pivot(columns="Group_Type",values="Zscore")
tmp["OverallScore"] =(tmp["Close"]* Weight_Close +tmp["PE1"]* Weight_PE1)
tmp = tmp[~tmp.OverallScore.isnull()].sort_values(by="OverallScore",ascending=True)
tmp =tmp.join(sec_map.secShortName)
tmp = tmp[[u'secShortName',u'OverallScore',u'Close', u'PE1',u'TurnoverValue' ]]
tmp.iloc[np.arange(-5,10)].sort_values(by="OverallScore").style.bar(subset=["OverallScore"],align="zero", color=[ '#5fba7d','#d65f5f',],width=100/2)
7.3 最高和最低的指数市盈率,和收盘价例子指数: 中证1000,新能源车,中证传媒,和 食品饮料。 排除中证电信,和某些没有对应基金产品的指数。
红色的线表示,Z值=1
绿色的线表示,Z值=-1
注:左图:(以过去10年所有数据为基础计算Z值),最后一个点2017年11月16日
右图:(以2017年的所有数据为基础计算Z值),最后一个点2017年11月16日
KPIs=["PE1","Close"]
secCodes =["000852","399417" ,"399971","000807"]
for secCode in secCodes:
for KPI in KPIs:
show_KPI(history,secCode,KPI=KPI)
本文同时在雪球(仅结果)和知乎(包含程序和结果)发表,并发布到Python中文社区。
分享是对自己最好的投资!
祝大家周末快乐
2017年11月17日下午