Python量化投资

您所在的位置:网站首页 蜡烛图k线图上下影线 Python量化投资

Python量化投资

2023-09-25 07:43| 来源: 网络整理| 查看: 265

用mplfinance实现全功能动态交互式K线图 手把手用`python`+mplfinance实现高级K线图` mplfinance`的基本K线图目标实现自定义风格和颜色图表尺寸调整、相关信息的显示添加完整移动平均线添加指标MACD实现鼠标拖动平移交互功能实现鼠标滚轮缩放实现双击切换指标使用键盘方向键平移缩放K线图及切换指标这一段代码感谢 @szFoxtech 的贡献! 完整代码

手把手用python+mplfinance实现高级K线图

特别鸣谢:@szFoxtech 同学热心提供了通过键盘操作平移和缩放K线图的代码!!

mplfinance的基本K线图

在我的另一篇文章《七行代码利用tushare和mplfinance生成K线图》中,我介绍了两个非常好用的python库,结合起来可以非常方便容易地生成股票的K线图,对于使用python进行量化投资研究的朋友可谓是必备良方。如下图所示,仅仅几行代码,就能生成下面这张K线图:

在这里插入图片描述

不过,仅仅使用七行代码生成的K线图,虽然看上去也像那么回事了,但是对于真正需要使用K线图作为研究工具或者可视化工具的朋友们来说,是远远不够的。在我看来,至少上面的K线图有以下这些问题:

颜色风格不符合习惯:众所周知中国股市K线图的颜色代码跟世界惯例是恰好相反的,其他国家都是红跌绿涨而我国是红涨绿跌,上面这幅图中的颜色信息并不符合中国股市的惯例,让人看着别扭。信息显示不够完整:一般的K线图都会显示出图上最后一个交易日的OHLC也就是开/高/低/收价格、使用红色和绿色表示本交易日相对过去的涨跌情况,同时还能显示其他的相关数据如涨幅、交易额、交易量等等信息,另外,股票代码、名称等信息也应该显示出来。均线系统不够完善:首先是均线不完整,最初的多个交易日内没有均线,这是因为仅仅使用图表内的数据计算均线导致产生了空缺值,其次,除了均线之外,没有其他的相关价格指标,如布林带线等与价格一同显示,也没有区间内最高价、最低价的指示。无动态交互功能:无法拖动平移K线以显示更早或更晚的交易日K线数据,无法通过鼠标滚轮增大或缩小显示的K线的范围,也无法通过点选某一根K线以显示当天交易日的详细信息。

以上这些功能都只能说是绝大部分股票软件所提供的最基本的交互式K线图交互功能,然而前面实现的K线图仅仅是个静态图像,因此离哪怕是最基本的实用性也还差得很远。好在mplfinance是基于matplotlib开发的,因此前面所描述的基本交互K线图功能,都可以实现。

在这里先把最终效果贴出来,本文最后有全部源代码 在这里插入图片描述

下面,我们就一步步地实现并扩充基本的K线图,使它成为一个具备基本实用性的股票价格可视化工具,成为一个真正的动态可交互K线蜡烛图。

目标

在开始实际工作之前,需要确定我们需要达到的目标,以便一步步实现:

符合中国习惯的配色风格——红涨绿跌自然是必须实现的第一步 图表上要能显示股票代码和股票名称、以及价格信息 图表上要显示完整的移动平均线 在交易量的下方显示第三张图表,同步显示相关指标如MACD等 在图表上用鼠标单击拖动,可以平移K线图以显示更早或更晚的K线 在图表上是用鼠标滚轮缩放,可以实现放大或缩小所显示的K线的范围 在图表上双击,可以循环切换移动平均线和布林带线 在指标图上双击,可以循环切换不同的指标类型如MACD/DEMA/RSI等等

下面我们就来一一实现上面的功能,随着本文的逐步完善,已经实现的功能会打上勾,如果朋友们有新的想法,也可以给我留言,我会把有价值的点子添加进来,逐步尝试实现。

为了简单起见,我们使用提前准备好的K线数据,包含OHLC、交易量、移动平均价格以及MACD指标数据,需要的朋友可以在这里下载

实现自定义风格和颜色

这肯定是上面的所有功能中最容易实现的一个。mplfinance提供了两个相关的函数:make_mpf_style 以及make_marketcolors。示例如下:

import mplfinance as mpf # 设置mplfinance的蜡烛颜色,up为阳线颜色,down为阴线颜色 my_color = mpf.make_marketcolors(up='r', down='g', edge='inherit', wick='inherit', volume='inherit') # 设置图表的背景色 my_style = mpf.make_mpf_style(marketcolors=my_color, figcolor='(0.82, 0.83, 0.85)', gridcolor='(0.82, 0.83, 0.85)')

在make_marketcolors函数中,几个不同的参数主要用于设置K线的颜色,up 和down都很明显,用于分别指定上涨K线和下跌K线的颜色。因此根据国内习惯自然应该设置up=‘r'也就是red红色,down自然就是’g'也就是‘green’绿色。不过需要注意的是这里仅仅设置K线的柱子的内部填充色,如果不指定边框、上下影线的颜色,他们都会是黑色,显示的效果就是黑色的边框、黑色的上下影线,挺难看的,因此还需要设置边框"edge"的颜色。此处设置为“in”或“inherit”代表“使用主配色“。也就是说,阳线(上涨)的柱子外框线跟阳线的内部填充色一致,那么如果阳线的颜色为红色,边框的颜色也是红色,如果阳线是绿色,则边框也是绿色。阴线也一样。 wick设置的就是上下影线的颜色,这里为了显眼,同样设置为”in“。 类似的,volume设置的是交易量柱子的颜色,也设置为”in“就可以了。

有朋友可能想问,如果我不喜欢标准的红绿色配色,觉得太鲜艳了,想改成自定义的RGB配色可不可以,当然可以,不过需要注意的是,在标准的matplotlib中,可以传入一个元组表示RGB配色,例如(0.5, 0.8, 0.6)然而mpf不能直接传递元组作为颜色代码,但可以接受一个表示元组的字符串,如上面代码中的figcolor='(0.82, 0.83, 0.85)'。

make_mpf_style()函数接受上面的参数,将所有的配置都存储在一个字典中,然后使用mpf的基本绘图方法,就可以生成一张符合中国股市习惯的K线图了:

import pandas as pd # 读取测试数据 data = pd.read_csv('test_data.csv', index_col=0) # 读取的测试数据索引为字符串类型,需要转化为时间日期类型 data.index = pd.to_datetime(data.index) mpf.plot(data.iloc[100:200], style=my_style, type='candle', volume=True)

在这里插入图片描述

上面的代码从本地读取测试数据后,将其中一部分显示在K线图中,如上图所示,红涨绿跌。这就是符合国内习惯的K线图了。

图表尺寸调整、相关信息的显示

有了符合中国股市习惯配色风格的K线图,接下来需要调整图表尺寸,同时显示价格信息。 在mplfinance的默认设置下,K线图会显示两张图表,K线图在上,交易量柱状图在下。实际上在大多数情况下,还需要第三张图表以显示一些相关的指标如KDJ,MACD等等,另外,图表的顶部应该预留出一些区域用于显示价格。

因此我们必须对图表的尺寸和位置进行精确控制,然而mplfinance的基础用法是不允许我们控制每一个图表的位置的,因此就必须使用mplfinance提供的另一种用法“External Axes Mode“,在这种模式下,我们可以像使用matplotlib一样直接控制画布上的每一个图标元素和文字元素,获得更大的操作自由。

为了实现自由控制,需要获取图表的figure对象,然后手动在figure上放置图表Axes和文字Text,文字和图表的位置、大小、格式完全自定义,下面是代码(测试数据可以在这里下载):

# data是测试数据,可以直接下载后读取,在下例中只显示其中100个交易日的数据 plot_data = data.iloc[100: 200] # 读取显示区间最后一个交易日的数据 last_data = plot_data.iloc[-1] # 使用mpf.figure()函数可以返回一个figure对象,从而进入External Axes Mode,从而实现对Axes对象和figure对象的自由控制 fig = mpf.figure(style=my_style, figsize=(12, 8), facecolor=(0.82, 0.83, 0.85)) # 添加三个图表,四个数字分别代表图表左下角在figure中的坐标,以及图表的宽(0.88)、高(0.60) ax1 = fig.add_axes([0.06, 0.25, 0.88, 0.60]) # 添加第二、三张图表时,使用sharex关键字指明与ax1在x轴上对齐,且共用x轴 ax2 = fig.add_axes([0.06, 0.15, 0.88, 0.10], sharex=ax1) ax3 = fig.add_axes([0.06, 0.05, 0.88, 0.10], sharex=ax1) # 设置三张图表的Y轴标签 ax1.set_ylabel('price') ax2.set_ylabel('volume') ax3.set_ylabel('macd') # 在figure对象上添加文本对象,用于显示各种价格和标题 fig.text(0.50, 0.94, '513100.SH - 纳指ETF:') fig.text(0.12, 0.90, '开/收: ') fig.text(0.14, 0.89, f'{np.round(last_data["open"], 3)} / {np.round(last_data["close"], 3)}') fig.text(0.14, 0.86, f'{last_data["change"]}') fig.text(0.22, 0.86, f'[{np.round(last_data["pct_change"], 2)}%]') fig.text(0.12, 0.86, f'{last_data.name.date()}') fig.text(0.40, 0.90, '高: ') fig.text(0.40, 0.90, f'{last_data["high"]}') fig.text(0.40, 0.86, '低: ') fig.text(0.40, 0.86, f'{last_data["low"]}') fig.text(0.55, 0.90, '量(万手): ') fig.text(0.55, 0.90, f'{np.round(last_data["volume"] / 10000, 3)}') fig.text(0.55, 0.86, '额(亿元): ') fig.text(0.55, 0.86, f'{last_data["value"]}') fig.text(0.70, 0.90, '涨停: ') fig.text(0.70, 0.90, f'{last_data["upper_lim"]}') fig.text(0.70, 0.86, '跌停: ') fig.text(0.70, 0.86, f'{last_data["lower_lim"]}') fig.text(0.85, 0.90, '均价: ') fig.text(0.85, 0.90, f'{np.round(last_data["average"], 3)}') fig.text(0.85, 0.86, '昨收: ') fig.text(0.85, 0.86, f'{last_data["last_close"]}') # 调用mpf.plot()函数,注意调用的方式跟上一节不同,这里需要指定ax=ax1,volume=ax2,将K线图显示在ax1中,交易量显示在ax2中 mpf.plot(plot_data, ax=ax1, volume=ax2, type='candle', style=my_style) fig.show()

在External Axes Mode模式下,由于我们手动创建了几个Axes对象(这也就是External Axes Mode的由来),调用mpf.plot()函数,注意调用的方式跟上一节不同,这里需要指定ax=ax1, volume=ax2,将K线图显示在ax1中,交易量显示在ax2中。 通过运行上面的代码得到下面的图表: 在这里插入图片描述 可以看到,图表的格式和数量都正确了,三个Axes分别用于显示K线图、交易量以及指标(暂时还未显示),最后一个交易日的价格显示在顶部区域,但是有两个问题:

数字的格式和颜色不对,应该用红绿色区分不同的价格,字体大小也需要设置正确的格式中文显示为乱码,需要设法使mplfinance支持utf-8编码格式的字符串

为了解决第一个问题,我们可以预设几种不同的格式备用,而第二个问题的原因在于使用的字体不支持中文,只要使用支持中文的字体就可以了。因此,可以分别定义下面几种字体,分别用于标题(黑色大字),开盘价/收盘价(大字体数字)和普通字体,每种字体都有红色和绿色两个版本:

# 标题格式,字体为中文字体,颜色为黑色,粗体,水平中心对齐 title_font = {'fontname': 'pingfang HK', 'size': '16', 'color': 'black', 'weight': 'bold', 'va': 'bottom', 'ha': 'center'} # 红色数字格式(显示开盘收盘价)粗体红色24号字 large_red_font = {'fontname': 'Arial', 'size': '24', 'color': 'red', 'weight': 'bold', 'va': 'bottom'} # 绿色数字格式(显示开盘收盘价)粗体绿色24号字 large_green_font = {'fontname': 'Arial', 'size': '24', 'color': 'green', 'weight': 'bold', 'va': 'bottom'} # 小数字格式(显示其他价格信息)粗体红色12号字 small_red_font = {'fontname': 'Arial', 'size': '12', 'color': 'red', 'weight': 'bold', 'va': 'bottom'} # 小数字格式(显示其他价格信息)粗体绿色12号字 small_green_font = {'fontname': 'Arial', 'size': '12', 'color': 'green', 'weight': 'bold', 'va': 'bottom'} # 标签格式,可以显示中文,普通黑色12号字 normal_label_font = {'fontname': 'pingfang HK', 'size': '12', 'color': 'black', 'va': 'bottom', 'ha': 'right'} # 普通文本格式,普通黑色12号字 normal_font = {'fontname': 'Arial', 'size': '12', 'color': 'black', 'va': 'bottom', 'ha': 'left'}

然后修改一下前面的代码,将格式分别应用到各个文本中去:

# 这里的代码与上一段完全相同 fig = mpf.figure(style=my_style, figsize=(12, 8), facecolor=(0.82, 0.83, 0.85)) ax1 = fig.add_axes([0.06, 0.25, 0.88, 0.60]) ax2 = fig.add_axes([0.06, 0.15, 0.88, 0.10], sharex=ax1) ax3 = fig.add_axes([0.06, 0.05, 0.88, 0.10], sharex=ax1) ax1.set_ylabel('price') ax2.set_ylabel('volume') ax3.set_ylabel('macd') # 设置显示文本的时候,返回文本对象 # 对不同的文本采用不同的格式 t1 = fig.text(0.50, 0.94, '513100.SH - 纳指ETF:', **title_font) t2 = fig.text(0.12, 0.90, '开/收: ', **normal_label_font) t3 = fig.text(0.14, 0.89, f'{np.round(last_data["open"], 3)} / {np.round(last_data["close"], 3)}', **large_red_font) t4 = fig.text(0.14, 0.86, f'{last_data["change"]}', **small_red_font) t5 = fig.text(0.22, 0.86, f'[{np.round(last_data["pct_change"], 2)}%]', **small_red_font) t6 = fig.text(0.12, 0.86, f'{last_data.name.date()}', **normal_label_font) t7 = fig.text(0.40, 0.90, '高: ', **normal_label_font) t8 = fig.text(0.40, 0.90, f'{last_data["high"]}', **small_red_font) t9 = fig.text(0.40, 0.86, '低: ', **normal_label_font) t10 = fig.text(0.40, 0.86, f'{last_data["low"]}', **small_green_font) t11 = fig.text(0.55, 0.90, '量(万手): ', **normal_label_font) t12 = fig.text(0.55, 0.90, f'{np.round(last_data["volume"] / 10000, 3)}', **normal_font) t13 = fig.text(0.55, 0.86, '额(亿元): ', **normal_label_font) t14 = fig.text(0.55, 0.86, f'{last_data["value"]}', **normal_font) t15 = fig.text(0.70, 0.90, '涨停: ', **normal_label_font) t16 = fig.text(0.70, 0.90, f'{last_data["upper_lim"]}', **small_red_font) t17 = fig.text(0.70, 0.86, '跌停: ', **normal_label_font) t18 = fig.text(0.70, 0.86, f'{last_data["lower_lim"]}', **small_green_font) t19 = fig.text(0.85, 0.90, '均价: ', **normal_label_font) t20 = fig.text(0.85, 0.90, f'{np.round(last_data["average"], 3)}', **normal_font) t21 = fig.text(0.85, 0.86, '昨收: ', **normal_label_font) t22 = fig.text(0.85, 0.86, f'{last_data["last_close"]}', **normal_font) mpf.plot(plot_data, ax=ax1, volume=ax2, type='candle', style=my_style) fig.show()

可以看到,这次得到正确的结果了: 在这里插入图片描述 有的朋友在运行上述代码时,可能会遇到错误说使用的中文字体不存在,因而还是显示乱码,这里给出一个解决方案供大家参考:

为了显示系统中有哪些中文字体,可以先导入matplotlib的FontManager类,调用这个类的ttflist属性,就可以看到系统中已经存在的所有可以被matplotlib使用的字体了,选择其中的中文字体即可(中文字体名称中一般都带有拼音,或者含有TC、SC之类的关键字:

>>> from matplotlib.font_manager import FontManager >>> fm = FontManager() >>> fm.ttflist Out: [, , ... , ... , ]

清单中的字体可能会比较多,也有多种中文字体,比如上面例子中的PingFang HK就是中文字体,将字体名称PingFang HK用于font就可以了:font_name='PingFang HK'

添加完整移动平均线

在mplfinance的标准plot()方法中,有一个mav参数,接受一个整数元组或列表,代表不同的移动平均线的天数,如[5, 20, 60]代表绘制三条均线,分别为5日、20日和60日均线。

然而,直接传入mav参数后绘制的移动平均线是不完整的,在图表的最初一段时间内没有均线,因为mplfinance没有足够的数据计算完整均线。如果要显示完整的均线,就必须提前计算好均线数据,然后再添加到K线图中。

在mplfinance中,添加更多的数据和均线到K线图中,不管是移动平均线,指标、买卖点、还是布林带线等等信息,都需要用到addplot。

在我们准备好的test_data中,已经计算好了四条均线的值,通过data[['MA5', 'MA10', 'MA20', 'MA60']]就可以访问了,我们现在通过make_addplot()方法把这几条均线添加到ax1中:

# 通过ax=ax1参数指定把新的线条添加到ax1中,与K线图重叠 ap = mpf.make_addplot(plot_data[['MA5', 'MA10', 'MA20', 'MA60']], ax=ax1)

接下来,还是调用mplfinance.plot()方法,同时指定addplot=ap即可。为节省篇幅,下面的代码省略了Axes对象和text对象的创建部分,这部分代码与前面相同:

# 调用plot()方法,注意传递addplot=ap参数,以添加均线 mpf.plot(plot_data, ax=ax1, volume=ax2, addplot=ap, type='candle', style=my_style) fig.show()

显示效果如下,现在所有的均线都已经显示出来了,而且,这里的均线都是完整的: 在这里插入图片描述

添加指标MACD

至此,一个静态的实用K线图已经初具雏形了,不过,ax3还是空的,这里本来应该显示MACD指标的,那么如何实现呢?自然还是需要addplot()! 不过,在开始之前,我们要知道,MACD指标包含两条线,还有一组柱状图,而且柱状图还分红绿两色,不是一个简单的图形,需要分别绘制。在mplfinance中,addplot可以是一个字典,也可以是一个列表,列表中包含多组不同的addplot,这样在调用mpf.plot()的时候,可以传入任意多个addplot,从而实现复杂的图表形式。

在本文的测试数据中,已经计算好了用于MACD的数据,分别存放在data[['macd-m', 'macd-h', 'macd-s']]中,addplot应该按照以下方法设置:

# 生成一个空列表用于存储多个addplot ap = [] # 在ax3图表中绘制 MACD指标中的快线和慢线 ap.append(mpf.make_addplot(plot_data[['macd-m', 'macd-s']], ax=ax3)) # 使用柱状图绘制快线和慢线的差值,根据差值的数值大小,分别用红色和绿色填充 # 红色和绿色部分需要分别填充,因此先生成两组数据,分别包含大于零和小于等于零的数据 bar_r = np.where(plot_data['macd-h'] > 0, plot_data['macd-h'], 0) bar_g = np.where(plot_data['macd-h'] 0, plot_data['macd-h'], 0) bar_g = np.where(plot_data['macd-h']


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3