python量化分析之---创新高个股值不值得追

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

 

 

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

 

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

 

先上数据分析图

图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股最近一年以内创新高后的涨跌情况,从分析来看,追涨盈利是大概率事件,但是并不代表按照这个策略执行的每一个人都会赚钱,道理很简单,四个字,机会成本。什么意思留给读者自己思考。如何解决这个问题,期待后面的文章解答。

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)

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值