用python的matplotlib和numpy库绘制股票K线均线和成交量

系统 430 0

     在用python的matplotlib和numpy库绘制股票K线均线的整合效果(含从网络接口爬取数据和验证交易策略代码)一文里,我讲述了通过爬虫接口得到股票数据并绘制出K线均线图形的方式,在本文里,将在此基础上再引入成交量效果图,并结合量价理论,给出并验证一些交易策略。

1 成交量对量化分析的意义

    美国的股市分析家葛兰碧(Joe Granville)在他所著的《股票市场指标》一书里提出著名的“量价理论”。“量价理论”的核心思想是,任何对股价的分析,如果离开了对成交量的分析,都将是无本之木,无水之源,因为成交量的增加或萎缩都表现出一定的股价趋势。

    成交量是指时间单位内已经成交的股数或总手数。成交量能反应出股市交易中的供求关系,其中道理是比较浅显易懂的,当股票供不应求时,大家争相购买,成交量就很大了,反之当供过于求时,则说明市场交易冷淡,成交量必然萎缩。

    广义的成交量包括成交股数(Volumn或Vol)、成交金额(AMOUNT,时间单位内已经成交的总金额数)和换手率(TUN,股票每天成交量除以股票的流通总股本所得的比率),而狭义则是指成交股数。我们用yahoo接口得到的数据里,有表示成交股数的Volumn列,其中的单位是“手”,一手为100股,在本部分里,我们是通过Volumn列数据绘制股票的成交量信息。

2 引入成交量

    在K线和均线整合成交量的效果图里,出于美观的考虑,我们对整合的效果提出了如下三点要求。

    第一,绘制上下两个子图,上图放K线和均线,下图放成交量效果。

    第二,上下两个子图共享x轴,也就是说,两者x轴的刻度标签和间隔应该是一样的。

    第三,通过柱状图来绘制成交量图,如果当天股票上涨,成交量图是红色,下跌则是绿色。   

    在如下的drawKMAAndVol.py案例,我们将实现增加成交量图的效果。

            1    
            
              #
            
            
              !/usr/bin/env python
            
            
2    
            
              #
            
            
              coding=utf-8
            
            
3    
            
              import
            
            
               pandas as pd

            
            4    
            
              import
            
            
               matplotlib.pyplot as plt 

            
            5    
            
              from
            
             mpl 
            
              import
            
            
               candlestick_ochl

            
            6    
            
              from
            
             matplotlib 
            
              import
            
            
               MultipleLocator

            
            7    
            
              #
            
            
              根据指定代码和时间范围,获取股票数据
            
            
8    df = pd.read_csv(
            
              '
            
            
              D:/stockData/ch7/600895.csv
            
            
              '
            
            ,encoding=
            
              '
            
            
              gbk
            
            
              '
            
            
              )

            
            9    
            
              #
            
            
              设置大小,共享x坐标轴
            
            
10    figure,(axPrice, axVol) = plt.subplots(2, sharex=True, figsize=(15,8
            
              ))

            
            11    
            
              #
            
            
              调用方法,绘制K线图 
            
            
12    candlestick_ochl(ax = axPrice,          opens=df[
            
              "
            
            
              Open
            
            
              "
            
            ].values, closes=df[
            
              "
            
            
              Close
            
            
              "
            
            ].values,                 highs=df[
            
              "
            
            
              High
            
            
              "
            
            ].values, lows=df[
            
              "
            
            
              Low
            
            
              "
            
            
              ].values,

            
            13                      width=0.75, colorup=
            
              '
            
            
              red
            
            
              '
            
            , colordown=
            
              '
            
            
              green
            
            
              '
            
            
              )

            
            14    axPrice.set_title(
            
              "
            
            
              600895张江高科K线图和均线图
            
            
              "
            
            )
            
              #
            
            
              设置子图标题
            
            
15    df[
            
              '
            
            
              Close
            
            
              '
            
            ].rolling(window=3).plot(ax=axPrice,color=
            
              "
            
            
              red
            
            
              "
            
            ,label=
            
              '
            
            
              3天均线
            
            
              '
            
            
              )

            
            16    df[
            
              '
            
            
              Close
            
            
              '
            
            ].rolling(window=5).plot(ax=axPrice,color=
            
              "
            
            
              blue
            
            
              "
            
            ,label=
            
              '
            
            
              5天均线
            
            
              '
            
            
              )

            
            17    df[
            
              '
            
            
              Close
            
            
              '
            
            ].rolling(window=10).plot(ax=axPrice,color=
            
              "
            
            
              green
            
            
              "
            
            ,label=
            
              '
            
            
              10天均线
            
            
              '
            
            
              )

            
            18    axPrice.legend(loc=
            
              '
            
            
              best
            
            
              '
            
            ) 
            
              #
            
            
              绘制图例
            
            
19    axPrice.set_ylabel(
            
              "
            
            
              价格(单位:元)
            
            
              "
            
            
              )

            
            20    axPrice.grid(True) 
            
              #
            
            
              带网格线
            
            
21    
            
              #
            
            
              如下绘制成交量子图
            
            
22    
            
              #
            
            
              直方图表示成交量,用for循环处理不同的颜色
            
            
23    
            
              for
            
             index, row 
            
              in
            
            
               df.iterrows():

            
            24        
            
              if
            
            (row[
            
              '
            
            
              Close
            
            
              '
            
            ] >= row[
            
              '
            
            
              Open
            
            
              '
            
            
              ]):

            
            25            axVol.bar(row[
            
              '
            
            
              Date
            
            
              '
            
            ],row[
            
              '
            
            
              Volume
            
            
              '
            
            ]/1000000,width = 0.5,color=
            
              '
            
            
              red
            
            
              '
            
            
              )

            
            26        
            
              else
            
            
              :    

            
            27            axVol.bar(row[
            
              '
            
            
              Date
            
            
              '
            
            ],row[
            
              '
            
            
              Volume
            
            
              '
            
            ]/1000000,width = 0.5,color=
            
              '
            
            
              green
            
            
              '
            
            
              )

            
            28    axVol.set_ylabel(
            
              "
            
            
              成交量(单位:亿手)
            
            
              "
            
            )
            
              #
            
            
              设置y轴标题
            
            
29    axVol.set_title(
            
              "
            
            
              600895张江高科成交量
            
            
              "
            
            )
            
              #
            
            
              设置子图的标题
            
            
30    axVol.set_ylim(0,df[
            
              '
            
            
              Volume
            
            
              '
            
            ].max()/100000000*1.2)
            
              #
            
            
              设置y轴范围
            
            
31    xmajorLocator = MultipleLocator(5) 
            
              #
            
            
              将x轴主刻度设置为5的倍数
            
            
32
            
                  axVol.xaxis.set_major_locator(xmajorLocator)

            
            33    axVol.grid(True) 
            
              #
            
            
              带网格线
            
            
34    
            
              #
            
            
              旋转x轴的展示文字角度
            
            
35    
            
              for
            
             xtick 
            
              in
            
            
               axVol.get_xticklabels():

            
            36        xtick.set_rotation(15
            
              )

            
            37    plt.rcParams[
            
              '
            
            
              font.sans-serif
            
            
              '
            
            ]=[
            
              '
            
            
              SimHei
            
            
              '
            
            
              ]

            
            38    plt.show()
          

    从第8行到第20行,我们一方面是从csv文件里读取数据,另一方面在第一个子图里绘制了K线和均线图。这部分的代码和之前很相似,不过请大家注意两个点。

    第一,在第10行里,不仅设置了绘图区域的大小,更通过sharex=True语句,设置了axPrice和axVol这两个子图共享x轴。

    第二,第二,在第14、18、19和第20行,由于是在K线图和均线图的axPrice子图里操作,所以若干方法的调用主体是axPrice对象,而不是之前的pyplot.plt对象。

    从第23行到第36行里,我们在axVol子图里绘制了成交量图的效果。请大家注意第23行到第27行的for循环,在其中,我们通过第24行的if语句,比较收盘价和开盘价,以判断当天股票是涨是跌,在此基础上,通过第25行或第27行的bar方法,设置当日成交量图的填充颜色。从上述代码能看出,成交量是在自于csv文件里的Volume列。

    在绘制成交量图的时候有两个细节请大家注意一下。

    第一,在第25行、第27行和第30行里,当我们设置y轴的刻度值和范围时,我们除以了一个相同的数,这是因为在第28行我们设置y轴文字时,指定了y轴成交量的单位是“亿手“。

    第二, 本次是通过第35行和第36行的for循环,设置了“x轴文字旋转”的效果,从代码里我们能看到,本案例中的旋转角度是15度。

    上述代码的运行效果如下图所示,从中大家能看两个x轴刻度一致的子图,且在成交量子图里,上涨日和下跌日的成交量填充色分别是红色和绿色。

用python的matplotlib和numpy库绘制股票K线均线和成交量的整合效果(含量化验证交易策略代码)_第1张图片

3 成交量与股价的关系

    成交量和股价间也存在着八大规律,通过下图,我们能感受到这些规律,其中纵坐标表示价(即股价),横坐标表示量(即成交量)。

用python的matplotlib和numpy库绘制股票K线均线和成交量的整合效果(含量化验证交易策略代码)_第2张图片

    我们能看出量价之间的八种关系,即量增价平、量增价升、量平价升、量缩价升、量减价平、量缩价跌、量平价跌、量跌价升,随着上述周期过程,股价也完成了一个从涨到跌的完整循环,下面我们来具体解释一下。

    1.量增价平:股价经过持续下跌进入到低位状态,出现了成交量增加但股价平稳的现象,此时不同天的成交量高度落差可能比较明显,这说明该股在底部积聚上涨动力。

    2.量增价升:成交量在低价位区持续上升,同时伴随着股价上涨趋势,这说明股价上升得到了成交量的支撑,后市将继续看好,这是中短线的买入信号。

    3.量平价升:在股价持续上涨的过程中,如果多日的成交量保持等量水平,建议在这一阶段中可以适当增加仓位。

    4.量缩价升:成交量开始减少,但股价依然在上升,此时应该视情况继续持股。但如果还没有买入的投资者就不宜再重仓介入,因为股价已经有了一定的涨幅,价位开始接近上限。

    5.量减价平:股价经长期大幅度上涨后,成交量显著减少,股价也开始横向调整不再上升,这是高位预警的信号。这个阶段里一旦有风吹草动,比如突然拉出大阳线和大阴线,建议应出货离场,做到落袋为安。

   6.量缩价跌:成交量在高位继续减少,股价也开始进入下降通道,这是明确的卖出信号。如果还出现缩量阴跌,这说明股价底部尚远,不会轻易止跌。

    7.量平价跌:成交量停止减少,但股价却出现急速下滑现象,这说明市场并没有形成一致看空的共识。股谚有“多头不死,跌势不止“的说法,出现“量平价跌”的情况,说明主力开始逐渐退出市场,这个阶段里,应继续观望或者出货,别轻易去买入以所谓的“抢反弹”。

    8. 量增价跌:股价经长期大幅下跌之后,有可能出现成交量增加的情况,此时的操作原则是建议卖出,或者空仓观望。如果低价区成交量有增加,则说明有资金在此价位区间接盘,预示后期有望形成底部并出现反弹。但如果出现量增价跌,则建议应清仓出局。

    在下文里,我们将通过Python语言验证量价理论中的两个规则。

4 验证“量增价平“的买点

    在如下的calBuyPointByVol.py案例中,我们将验证“量增价平“的买点。在这段代码里我们做了三件事,第一是通过yahoo接口得到了指定股票指定范围内的交易数据,第二通过pandas接口保存得到的数据,以便日后验证,第三通过遍历dataframe对象,计算量和价的关系,从而获得买点日期。    

            1    
            
              #
            
            
              !/usr/bin/env python
            
            
2    
            
              #
            
            
              coding=utf-8
            
            
3    
            
              import
            
            
               pandas_datareader

            
            4    
            
              import
            
            
               pandas as pd

            
            5    
            
              import
            
            
               numpy as np

            
            6    
            
              #
            
            
              涨幅是否大于指定比率
            
            
7    
            
              def
            
            
               isMoreThanPer(lessVal,highVal,per):

            
            8        
            
              if
            
             np.abs(highVal-lessVal)/lessVal>per/100
            
              :

            
            9            
            
              return
            
            
               True

            
            10        
            
              else
            
            
              :

            
            11            
            
              return
            
            
               False        

            
            12    
            
              #
            
            
              涨幅是否小于指定比率
            
            
13    
            
              def
            
            
               isLessThanPer(lessVal,highVal,per):

            
            14        
            
              if
            
             np.abs(highVal-lessVal)/lessVal
            
              :
15            
              
                return
              
              
                 True

              
              16        
              
                else
              
              
                :

              
              17            
              
                return
              
              
                 False

              
              18    code=
              
                '
              
              
                600895.ss
              
              
                '
              
              
19    stock = pandas_datareader.get_data_yahoo(code,
              
                '
              
              
                2018-09-01
              
              
                '
              
              ,
              
                '
              
              
                2018-12-31
              
              
                '
              
              
                )

              
              20    
              
                #
              
              
                删除最后一行,因为get_data_yahoo会多取一天数据
              
              
21    stock.drop(stock.index[len(stock)-1],inplace=
              
                True)

              
              22    
              
                #
              
              
                保存在本地
              
              
23    stock.to_csv(
              
                '
              
              
                D:\\stockData\ch7\\60089520181231.csv
              
              
                '
              
              
                )

              
              24    
              
                #
              
              
                从文件里得到数据
              
              
25    df = pd.read_csv(
              
                '
              
              
                D:/stockData/ch7/60089520181231.csv
              
              
                '
              
              ,encoding=
              
                '
              
              
                gbk
              
              
                '
              
              
                )

              
              26    cnt=
              
                0    

              
              27    
              
                while
              
               cnt<=len(df)-1
              
                :

              
              28        
              
                try
              
              
                :

              
              29            
              
                #
              
              
                规则1,连续三天收盘价变动不超过3%
              
              
30            
              
                if
              
               isLessThanPer(df.iloc[cnt][
              
                '
              
              
                Close
              
              
                '
              
              ],df.iloc[cnt+1][
              
                '
              
              
                Close
              
              
                '
              
              ],3) 
              
                and
              
               isLessThanPer(df.iloc[cnt][
              
                '
              
              
                close
              
              
                '
              
              ],df.iloc[cnt+2][
              
                '
              
              
                Close
              
              
                '
              
              ],3
              
                ) :

              
              31                
              
                #
              
              
                规则2,连续三天成交量涨幅超过75%
              
              
32                
              
                if
              
               isMoreThanPer(df.iloc[cnt][
              
                '
              
              
                Volume
              
              
                '
              
              ],df.iloc[cnt+1][
              
                '
              
              
                volume
              
              
                '
              
              ],75) 
              
                and
              
               isMoreThanPer(df.iloc[cnt][
              
                '
              
              
                Volume
              
              
                '
              
              ],df.iloc[cnt+2][
              
                '
              
              
                Volume
              
              
                '
              
              ],75
              
                ) :

              
              33                    
              
                print
              
              (
              
                "
              
              
                Buy Point on:
              
              
                "
              
               + df.iloc[cnt][
              
                '
              
              
                Date
              
              
                '
              
              
                ])

              
              34        
              
                except
              
              
                : 

              
              35            
              
                pass
              
                              
36        cnt=cnt+1
            
          

    在第7行定义的isMoreThanPer方法里,我们比较了高价和低价,以判断是否超过由参数per指定的涨幅。在第13行的isLessThanPer方法里,我们判断了跌幅是否超过per指定的范围。由于这两个功能经常会用到,所以我们把它们封装成函数。

    从第18行到第25行,我们完成了获取并保存数据的动作,并用df对象保存了待遍历的股票数据(即张江高科2018-09-01到2018-12-31的数据)。

    在第27行到第36行按日期遍历股票数据时,我们制定了如下规则,连续三天股票的收盘价变动范围不超过5%(即价平)且3天成交量的涨幅过75%(即量增),把满足条件的日期打印出来。 运行后,我们能看到11月2日这个买点。

    在之前代码基础上改写下,把时间范围改成2018-09-01到2018-12-31,再运行下,能看到如下图所示的效果。     用python的matplotlib和numpy库绘制股票K线均线和成交量的整合效果(含量化验证交易策略代码)_第3张图片

    从中我们能看到验证后的结果:在11月2日之后,股票的涨幅比较明显,确实是个合适的买点,从中我们能看出 “量增价平”的指导意义。

5 验证“量减价平“的卖点

    在如下calSellPointByVol.py案例中,我们同样是分析张江高科2018-09-01到2018-12-31的交易数据,本次我们制定的策略是,第一,还是连续三天股票的收盘价变动范围不超过5%(即价平),第二,较第一日相比,第二日和第三日的成交量下降幅度超过75%(即量减)。    

            1    
            
              #
            
            
              !/usr/bin/env python
            
            
2    
            
              #
            
            
              coding=utf-8
            
            
3    
            
              import
            
            
               pandas_datareader

            
            4    
            
              import
            
            
               pandas as pd

            
            5    
            
              import
            
            
               numpy as np

            
            6    
            
              #
            
            
              涨幅是否大于指定比率
            
            
7    
            
              def
            
            
               isMoreThanPer(lessVal,highVal,per):

            
            8        
            
              if
            
             np.abs(highVal-lessVal)/lessVal>per/100
            
              :

            
            9            
            
              return
            
            
               True

            
            10        
            
              else
            
            
              :

            
            11            
            
              return
            
            
               False        

            
            12    
            
              #
            
            
              涨幅是否小于指定比率
            
            
13    
            
              def
            
            
               isLessThanPer(lessVal,highVal,per):

            
            14        
            
              if
            
             np.abs(highVal-lessVal)/lessVal
            
              :
15            
              
                return
              
              
                 True

              
              16        
              
                else
              
              
                :

              
              17            
              
                return
              
              
                 False

              
              18    
              
                #
              
              
                本次直接从文件里得到数据
              
              
19    df = pd.read_csv(
              
                '
              
              
                D:/stockData/ch7/60089520181231.csv
              
              
                '
              
              ,encoding=
              
                '
              
              
                gbk
              
              
                '
              
              
                )

              
              20    cnt=
              
                0    

              
              21    
              
                while
              
               cnt<=len(df)-1
              
                :

              
              22        
              
                try
              
              
                :

              
              23            
              
                #
              
              
                规则1,连续三天收盘价变动不超过3%
              
              
24            
              
                if
              
               isLessThanPer(df.iloc[cnt][
              
                '
              
              
                Close
              
              
                '
              
              ],df.iloc[cnt+1][
              
                '
              
              
                Close
              
              
                '
              
              ],3) 
              
                and
              
               isLessThanPer(df.iloc[cnt][
              
                '
              
              
                Close
              
              
                '
              
              ],df.iloc[cnt+2][
              
                '
              
              
                close
              
              
                '
              
              ],3
              
                ) :

              
              25                
              
                #
              
              
                规则2,连续三天成交量跌幅超过75%
              
              
26                
              
                if
              
               isMoreThanPer(df.iloc[cnt+1][
              
                '
              
              
                Volume
              
              
                '
              
              ],df.iloc[cnt][
              
                '
              
              
                Volume
              
              
                '
              
              ],75) 
              
                and
              
               isMoreThanPer(df.iloc[cnt+2][
              
                '
              
              
                Volume
              
              
                '
              
              ],df.loc[cnt][
              
                '
              
              
                Volume
              
              
                '
              
              ],75
              
                ) :

              
              27                    
              
                print
              
              (
              
                "
              
              
                Sell Point on:
              
              
                "
              
               + df.iloc[cnt][
              
                '
              
              
                Date
              
              
                '
              
              
                ])

              
              28        
              
                except
              
              
                : 

              
              29            
              
                pass
              
                              
30        cnt=cnt+1
            
          

    上述代码和之前calBuyPointByVol.py案例很相似,只不过我们适当变更了第26行判断“成交量”的if条件。上述代码运行后,我们能得到的卖点是2018-12-05,从上图里我们能看出,在这段时间之后的若干交易日里,张江高科的股价确实有下跌现象。

6 求推荐,后文预告与版权说明

    在本系列的后面文章中,将陆续通过python绘制成交量、KDJ、MACD、RSI,BIAS和OBV等指标,而且还会用Python编写针对这些指标的交易策略,敬请关注。

    本文用了我将近2个小时,如果大家感觉好,请帮忙推荐下。

    关于转载有如下的说明。

    1 本文文字和代码均属原创,可转载,但谢绝用于商业用户。

    2 转载时请用 链接 的方式,给出原文出处,同时写明原作者是hsm_computer。

    3 在转载时,请原文转载 ,如要 在转载修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请请扫描上面二维码支持博主1元、2元、5元等您想捐的金额吧,狠狠点击下面给点支持吧

发表我的评论
最新评论 总共0条评论