统计分析告诉你,创新高个股值不值得追

发布于: 雪球转发:1回复:0喜欢:1

@今日话题 $上证指数(SH000001)$ $创业板指(SZ399006)$ $深证成指(SZ399001)$ 

一直在做算法和数据分析相关的工作,对于创新高就买入持有这种简单的策略,反而忽略了,抽时间分析了一下,竟然收获了意外的惊喜。

最近在量化分析微信交流群中,一位朋友说他的策略是从创一年新高的个股中买入满足某些条件的个股,但是不会写代码分析,我就说帮他写代码作可行性分析。所谓可行性分析,就是用数据说话,用什么数据说话?用统计概率!

本文使用的数据就是上一周分享的数据,数据分享,我给的版本写比较基础,感兴趣的朋友可以到文末下载源码调试,按照文末的提示修改跑一跑。后面再写这方面的文章。

先上数据分析图

图1 创新高后持仓5个交易日风险收益分析图

每一张图的基础数据都相同,变量只有一个,即创新高后持仓的交易日数量。每一张图分为上下两个子图,后面以上图和下图代替上子图和下子图。上图横坐标为交易序号,没有实际意义,仅起标签的作用,纵坐标表示创新高后,买入持有五个交易日的收益率百分数,按照收益率从小到大排序后画出的曲线。

我们来分析一下这条曲线蕴含的信息。

一共近五千次统计中,创一年新高后持有5个交易日的最低收益率为亏损50%,最高收益率为盈利60%。如果只看这两个数据,没有多大价值,还有一个关键点是曲线与0轴线的交点位置。此位置在横轴1300左右,该点左侧的样本代表亏损,右侧的样本代表盈利,也就是说,在近5000个样本中,有74%左右的样本盈利,只有26%左右的样本亏损。那是不是意味着这个简单策略的风险只有26%呢?当然不是,请看下图(柱状图)。

创新高后持仓5个交易日风险收益分析图

柱状图横轴表示收益率,纵轴表示收益率对应的频率。即将5000个样本的收益率分组,再统计每一组的样本个数。根据大数定律,当统计样本足够大时,频率分布可以看作概率分布。从图中可以看出,该概率分布类似正态分布,但是并不对称,右边的分布所在比例明显大于左边。即盈利的概率大于亏损的概率。到底大多少,怎样在实际操作中最大化这个概率,怎样使期望收益最大?这个问题留作后面的文章解答。

下面看其余持仓时间的收益风险分布图

图2 创新高后持仓10个交易日风险收益分析图

图3 创新高后持仓15个交易日风险收益分析图

图4 创新高后持仓20个交易日风险收益分析图

图5 创新高后持仓30个交易日风险收益分析图

图6 创新高后持仓50个交易日风险收益分析图

图7 创新高后持仓60个交易日风险收益分析图

控制变量持仓天数是为了分析买入后持有多久利益能最大化。这个问题比较复杂。如果从上面几张图面看,持有30-60天是比较合理的。但是事实并非如此。原因主要有以下几点:

1:盈利和亏损都是概率事件,不是必然事件。任意一幅图都可以看出,盈利越高,概率越小。

2:时间成本和机会成本。如果你买入一只创新高个股,持有60个交易日,最终获得10%的利润。如果你不是持有这只股票60个交易日,而在持有20个交易日,盈利5%时卖出,同时买入另一只创新高个股,而这只可能在余下的40个交易日给你带来超过5%的收益

3:没有后悔药,时间不会倒流。如果你信奉持有60个交易日获得更多利润的可能性要大一些,但是市场是波动的,有可能这只股票创新高后前20个交易日一直走高,但是剩下的40个交易日节节败退

怎样解答这篇文章中提出的种种疑问,留作后面的文章探讨。

此次分析使用的数据为所有上市a股从2017-01-01到最近交易日的数据,剔除停牌的交易日后所做的分析。公众号【数据之佳】提供超过3500只a股从上市之日起到最近交易日的所有历史数据,感兴趣的朋友可以到公众号回复【新高】两个字获取分析源代码。

当然,这个分析比较粗糙,只是对这个策略有没有价值做出了肯定的答复,如何将这个策略的收益最大化,同时将风险控制在最低,还涉及到建模过程,这个过程公众号后面的文章或课程中解答。

下面给朋友们提出几个优化这个分析结果的方向

1:本次分析使用所有a股的历史数据的进行分析,可以对某一类股票单独做类似分析,后面再写文章举例

2:通过基本面过滤一些股票

3:可以按照牛熊市分别展开分析

4:本例其实有一个比较大的隐患,不知道读者有没有发现。此次分析统计所有a股最近一年以内创新高后的涨跌情况,从分析来看,追涨盈利是大概率事件,但是并不代表按照这个策略执行的每一个人都会赚钱,道理很简单,四个字,机会成本。什么意思留给读者自己思考。如何解决这个问题,期待后面的文章解答。

如果对这个问题感兴趣,可以关注公众号【数据之佳】,在后台留言就可以进量化分析微信群和众多网友一起探讨量化

公众号其他文章:

最近一周数据分享

所有A股自上市之日起历史数据分享(包含15字段)

动动手指,轻松获取沪股通及重要指数所有历史数据

5行代码实现1秒以内获取一次所有股票的实时粉笔数据

python多线程和多进程获取股票实时分笔数据

tushare获取股票实时数据,最小延时是多大

源代码:

import pandas as pd import time import numpy class Stock():#初始化时传入路径,读入股票的数据 #shift表示计算统计多长时间窗口的最高价,shift表示要计算每个窗口未来多长时间的涨跌幅 #startDate表示从哪一个日期开始计算 def __init__(self,stockPath,frame,shift,startDate): f=open(stockPath,'r',encoding="UTF-8") data=pd.read_csv(stockPath,sep=',') # A日期、B编码、C名称、D收盘价、E开盘价、F最低价、G最高价、H平均价、I涨跌额度、J涨跌幅、K换手率、L成交量、M成交金额、N总值、O流值 data.columns=["date","code","name","tClose","tOpen","tLow","tHigh","tMean","zhangE","zhangFu","changeRate","volume","money","total","liquid"] data=data[data["date"]>=startDate] rows, cols = data.shape self.frame=frame self.shift=shift self.colsDesc=data.columns self.length=rows self.data=data self.frames=rows//(frame)#计算该股票的历史数据可以得到多少个切片 self.stockReport={}#窗口的开始日期,窗口的结束日期,窗口是否创新高,窗口未来shift个交易日的涨跌幅 def getStockReport(self): for i in range(0,self.length-self.shift-self.frame):#此处可以改为多进程,先规划好切片,再计算每一个切片的结果 frame=Frame(self.data,i,i+self.frame,self.shift) frameStartDate=frame.startDate#切片的开始日期 frameEndDate=frame.endDate#切片的结束日期 frameFlag=frame.getFrameFlag()#切片的是否创新高 frameRate=frame.getFrameRate()#切片未来shif日期内的涨幅 self.stockReport[frameStartDate]={"frameStartDate":frameStartDate,"frameEndDate":frameEndDate, "frameFlag":frameFlag,"frameRate":frameRate} return self.stockReport class Frame:#窗口类, #初始化参数为,切片数据源,切片的开始索引,结束索引 def __init__(self,FrameData,startIndex,endIndex,shift): self.frameData=FrameData.iloc[startIndex:endIndex,:]#一个切片,从startIndex到endIndex-1窗口的数据 self.startDate=self.frameData["date"].values[0]#窗口的开始日期 self.endDate=self.frameData["date"].values[-1]#窗口的结束日期 self.flag=0#1表示该窗口创出新高,0表示没有创新高 self.rate=0 self.rateList=FrameData["tClose"].values[endIndex:endIndex+shift]#在这个序列中寻找最大涨幅 self.frameClose=self.frameData["tClose"].values[-1]#窗口的收盘价 def getFrameFlag(self):#用于返回该段数据的标签,标注该切片最后一个交易日有没有创新高 maxIndex=numpy.argwhere(self.frameData["tClose"]==numpy.max(self.frameData["tClose"])) # print(maxIndex[0]) # print(self.frameData["tClose"]) if maxIndex[0]==len(self.frameData["tClose"])-1: self.flag=1 else: self.flag=0 return self.flag def getFrameRate(self):#返回窗口指定交易日内的最大涨 maxpriceOfRateList=numpy.max(self.rateList) rate=((maxpriceOfRateList-self.frameClose)/self.frameClose)*100 self.rate=float("%.2f" % rate) return self.rate

import os import pandas as pd from multiprocessing import Process from analyseNewHigh.stockKClass import Stock,Frame def process(path,index): stock = Stock(path, 200, 60, '2017-01-01') report = stock.getStockReport() reportDataFrame = pd.DataFrame(report).T rows, cols = reportDataFrame.shape if rows > 0: yesDataFrame = reportDataFrame[reportDataFrame["frameFlag"] == 1] rows1, cols1 = yesDataFrame.shape if rows1 > 0: yesDataFrame.to_csv("G:\创新高分析python代码包和数据\标的结果200新高后60交易日\\"+file) print(index,file) print(yesDataFrame) for (root,dirs,files) in os.walk("H:\根据标的和日期分析结果\标的数据"): length=len(files) for i in range(length): file=files[i] path=root+"\\"+file process(path,i)