PE即市盈率相信大家都不陌生,很多机构在预测牛市熊市普遍用到了低市盈率买入,高市盈率卖出。
因为1年为252个交易日(美),A股为240个交易日,聚宽样本数量最大为3000,该策略研究我选取近似10年的交易日数据为2500个,也符合A股5年一波牛市,即约2个牛市的或者说1个朱古拉周期的样本数。
策略中PE建模为每月更新一次,由于处理后的历史PE值(取以10为底的对数后,[np.log10])属于非正态分布,呈现右偏态,所以选取中位数效果好于平均数。
原来想对中位数±0.25倍标准差设立【安全交易带】,但是考虑到A股市场股民价值投资较少,涨到一定程度,还会继续追高,而市场价值低估他们仍然不会买入,因此相比其他市场存在一定超卖,所以下轨道设置为0.5倍std可以获得更好的择时效果。
交易买入和卖出选择MA5上穿或下穿MA10(策略中采用EMA效果因人而异),5日和10日均线在短线交易上效果较好,但是用在大盘择时上显然不可能,加入了【PE安全交易带】之后,在安全带之上卖出,安全带之下买入,而当前PE处在【PE安全交易带】之内时,不进行择时。这样就达到了很不错的效果,100%(当然有点标题党了,嘿嘿)。
本研究只选取了HS300指数进行交易(资金麻烦选择1000万以上,否则资金不够无法买入),仅展示择时效果,其实可以略微调整安全带的下轨0.5倍值,原因其实在低市盈率市场下,进行ROE、CR、PEG等因子买入超低股并持有,往往会获得比HS300指数更好的alpha,这也是为何熊市持有低贝塔的原因。
另外,各位可以通过调节MA的值比如MA1周期更小,MA2周期更大,来获得不一样的效果,具体择时效果可以参考论坛其他帖子,当然了使用EXPMA效果会更好些,这些都可以自己调整测试。
对于安全带的大小设定,也可以通过调节std前的倍数来调节宽还是窄,建议不要进行过大或者过小的调整,同时也要考虑到MA的择时效应,比如宽安全带,灵敏MA或者窄安全带,钝化MA。
def MA(self):
date = self.date
security = self.security #p = get_price(security = security, count = 21, end_date = date, fields=['close']) #MA1 = p.close[-5:].mean() #MA2 = p.close[0:10].mean() MA1 = EXPMA(security,date,timeperiod = 5)[security]
MA2 = EXPMA(security,date,timeperiod = 25)[security] return MA1/MA2
if g.NOWPE < g.stdL and g.MA > 1:
#调节MA选择值,比如1改为1.01来提高买入择时效果
下图附上同期RSRS择时进行比较。在没有调优的情况下,本策略持股时间更长,而且回测效果明显好于RSRS择时,熊市低市盈率持股配合PEG选股或者戴维斯双击,可以获得更好的alpha。
from jqdata import financefrom sklearn import linear_modelfrom jqdata import *import scipy.stats as statsimport numpy as npimport pandas as pdimport mathclass PEQ:def __init__(self,date,security):self.date = date self.security = securitydef Q(self,count): #查询PE等数据修正后归为Vq=query(finance.STK_EXCHANGE_TRADE_INFO).filter(finance.STK_EXCHANGE_TRADE_INFO.date <= self.date,finance.STK_EXCHANGE_TRADE_INFO.exchange_code == '322002',).order_by(finance.STK_EXCHANGE_TRADE_INFO.date.desc()).limit(count)df=finance.run_query(q)DF = df[['date','pe_*erage','turnover_ratio']]DF = DF.copy()#DF['V'] = DF.apply(lambda x: np.sqrt(turnover_ratio) * np.log(x.pe_*erage), axis=1) #算上换手率考虑牛市波动DF['V'] = DF.apply(lambda x: np.log10(x.pe_*erage), axis=1) #纯PE择时 取以10为底的lgPE值。return DFdef STD(self): #获得中位数±1倍标准差section = self.Q(2500).Vmean = section.mean()median = section.median()std = section.std()return median+0.25*std,median-0.5*stddef NOW(self): #获得现在的数据值return self.Q(1).V[0]def MA(self):date = self.date security = self.security#p = get_price(security = security, count = 21, end_date = date, fields=['close'])#MA1 = p.close[-5:].mean()#MA2 = p.close[0:10].mean()MA1 = EXPMA(security,date,timeperiod = 5)[security]MA2 = EXPMA(security,date,timeperiod = 25)[security]return MA1/MA2
date = datetime.datetime.now()-datetime.timedelta(days=1)peq = PEQ(date,'000300.XSHG')#print peq.MA()H,L = peq.STD() #PE安全带上下轨print peq.NOW(),H,L #打印PE安全带上下轨和现值print (H-peq.NOW())/peq.NOW(),(peq.NOW()-L)/peq.NOW()print 10**((H-peq.NOW())/peq.NOW()),10**((peq.NOW()-L)/peq.NOW())print peq.Q(2500)[:1]print peq.Q(2500).describe()
1.2140752085 1.22304978447 1.13390046121
0.00739210874305 0.0660377106233
1.0171666413 1.1642271171
date pe_*erage turnover_ratio V
0 2019-04-17 16.371 1.0626 1.214075
pe_*erage turnover_ratio V
count 2500.000000 2500.000000 2500.000000
mean 16.435674 0.843043 1.199169
std 4.693169 0.653243 0.118866
min 9.578000 0.222500 0.981275
25% 12.784500 0.443375 1.106684
50% 15.607500 0.606950 1.193333
75% 18.613250 0.915075 1.269822
max 29.920000 4.290600 1.475962
DF = peq.Q(2500)x = DF.date.tolist()y = DF.V.tolist() plt.subplots(figsize=(16,9))plt.plot(x,y,label='曲线')plt.legend() # 让图例生效plt.show()
list = DF.Vplt.hist(list, bins=50, rwidth=0.5, normed=True)print ('mean=%s'%list.mean(),'median=%s'%list.median(),'std=%s'%list.std(),'min=%s'%list.min(),'max=%s'%list.max())print ('kurt=%s'%pd.Series(list).kurt(),'skew=%s'%pd.Series(list).skew())
('mean=1.19916917838', 'median=1.19333334338', 'std=0.118865764343', 'min=0.981274832707', 'max=1.47596158919')
('kurt=-0.628105314427', 'skew=0.288813212346')