用python实现全功能动态交互式K线图
手把手用python
一步步实现动态交互式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',