Matplotlib 蜡烛图教程


(astroman) #1

本文原载于我的博客。本文欢迎转载,但请保留本段文字,尊重作者的权益。谢谢。
致敬Matplotlib 教程

Matplotlib 蜡烛图教程

关于使用 Matplotlib 画蜡烛图,任何一个搜索引擎都可以搜到非常多的内容,但通常都不足以为学习者提供清晰的思路和简便的实现步骤,本文力求在这两方面为学习者提供帮助。
尝试过画蜡烛图的同学,应该听说过 matplotlib.finance,而且大部分同学对这个库大概率是不抱有好感的(因为其中的雅虎接口很坑爹),但这个库确实是画蜡烛图的利器。
这个库中,共有四个画蜡烛图的函数:

matplotlib.finance.candlestick2_ochl(ax, opens, closes, highs, lows, width=4, colorup='k', colordown='r', alpha=0.75)  
matplotlib.finance.candlestick2_ohlc(ax, opens, highs, lows, closes, width=4, colorup='k', colordown='r', alpha=0.75)  +
matplotlib.finance.candlestick_ochl(ax, quotes, width=0.2, colorup='k', colordown='r', alpha=1.0)  
matplotlib.finance.candlestick_ohlc(ax, quotes, width=0.2, colorup='k', colordown='r', alpha=1.0)¶

我们选取 mpf.candlestick2_ochl 作为例子,数据用 tushare,计算均线用 talib,动手实践。

初级绘制

秉承着画出来就好的想法,按照文档的说明,先搞一套试试。这里面最大的坑是什么呢?
是日线图的时间间隔问题(非交易日无法跳过,导致图像断裂),这个问题在《Python 金融大数据分析》中也没有刻意去解决(可能是作者认为这点无关紧要),但仍有不少同学希望能够画出更好看的图。无论是官方文档还是实际操作,一般的方法都是建议重新调整横坐标,被动地过滤掉非交易时间段,例如我设置成每 10 个交易日标记一次:

ax.set_xticks(range(0, len(data['date']), 10))
ax.set_xticklabels(data['date'][::10])

再加上 10 日和 30 日均线,一起画出来:

import talib
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
import matplotlib.finance as mpf

data = ts.get_k_data('399300', index=True, start='2017-01-01', end='2017-06-31')
sma_10 = talib.SMA(np.array(data['close']), 10)
sma_30 = talib.SMA(np.array(data['close']), 30)

fig = plt.figure(figsize=(24, 8))
ax = fig.add_subplot(1, 1, 1)
ax.set_xticks(range(0, len(data['date']), 10))
ax.set_xticklabels(data['date'][::10])
ax.plot(sma_10, label='10 日均线')
ax.plot(sma_30, label='30 日均线')
ax.legend(loc='upper left')

mpf.candlestick2_ochl(ax, data['open'], data['close'], data['high'], data['low'],
                     width=0.5, colorup='r', colordown='green',
                     alpha=0.6)
plt.grid()

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘1923’%20height='652’>)

加上成交量

有了上面的 k 线图之后,希望继续加上一个成交量的图。加上成交量后,又要注意什么呢?

首先是坐标的对齐,k 线和成交量不能错开,时间得对得上。这不是什么困难的事情,只要在创建子图时,加上 sharex=True,或者暴力一点,也可以把两个子图的横轴设置成相同的。其次是两个子图的对齐方式,我希望是“两张图并成一张图”,及两张子图之间没有间隙。这一点也不困难,只需要写上 plt.subplots_adjust(hspace=0) 就行了。

成交量的图有几种画法,这里介绍两种不同的思路。

柱状图

pandas 对画图的支持是非常好的,对于从 tushare 里拿出来的 DataFrame,可以拿出成交量一列,直接画出柱状图:

data['volume'].plot(kind='bar', color='k', alpha=0.3)

从而整个的代码块变成:

import talib
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
import matplotlib.finance as mpf

data = ts.get_k_data('399300', index=True, start='2017-01-01', end='2017-06-31')
sma_10 = talib.SMA(np.array(data['close']), 10)
sma_30 = talib.SMA(np.array(daa['close']), 30)

fig, (ax, ax2) = plt.subplots(2, 1, sharex=True, figsize=(17, 10))

mpf.candlestick2_ochl(ax, data['open'], data['close'], data['high'], data['low'], 
                     width=0.5, colorup='r', colordown='green', alpha=0.6)

ax.set_xticklabels(data['date'][::10])
ax.plot(sma_10, label='10 日均线')
ax.plot(sma_30, label='30 日均线')
ax.legend(loc='upper left')
ax.grid(True)

data['volume'].plot(kind='bar', color='k', alpha=0.3)
ax2.set_xticks(range(0, len(data['date']), 10))
ax2.set_xticklabels(data['date'][::10], rotation=30)
ax2.grid(True)

plt.subplots_adjust(hspace=0)

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘1381’%20height='836’>)

会发现这里的成交量是单色的,而如果想让成交量不是单色的,会比较麻烦,于是引申出另一种方法。

mpf.volume_overlay

matplotlib.finance 中有直接用于画成交量的函数:

matplotlib.finance.volume_overlay(ax, opens, closes, volumes, colorup='k', colordown='r', width=4, alpha=1.0)

那就直接用起来:

import talib
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
import matplotlib.finance as mpf

data = ts.get_k_data('399300', index=True, start='2017-01-01', end='2017-06-31')
sma_10 = talib.SMA(np.array(data['close']), 10)
sma_30 = talib.SMA(np.array(data['close']), 30)

fig, (ax, ax2) = plt.subplots(2, 1, sharex=True, figsize=(17, 10))

mpf.candlestick2_ochl(ax, data['open'], data['close'], data['high'], data['low'], 
                     width=0.5, colorup='r', colordown='g', alpha=0.6)

ax.set_xticklabels(data['date'][::10])
ax.plot(sma_10, label='10 日均线')
ax.plot(sma_30, label='30 日均线')
ax.legend(loc='upper left')
ax.grid(True)

mpf.volume_overlay(ax2, data['open'], data['close'], data['volume'], colorup='r', colordown='g', width=0.5, alpha=0.8)
ax2.set_xticks(range(0, len(data['date']), 10))
ax2.set_xticklabels(data['date'][::10], rotation=30)
ax2.grid(True)

plt.subplots_adjust(hspace=0)

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘1381’%20height='836’>)

进阶

讲道理,到上一部分,画出来的图就已经像回事了,不过还是有一点别扭:k 线和成交量两个子图的大小是一样的。忍不了啊,继续折腾吧。
折腾了好一会,发现一个比较简便的方法是调整坐标轴:

ax = fig.add_axes([0,0.2,1,0.5])
ax2 = fig.add_axes([0,0,1,0.2])

注意这时候就不用再设置两张子图的间距了。好了,最终版本出来了:

import talib
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
import matplotlib.finance as mpf

data = ts.get_k_data('399300', index=True, start='2017-01-01', end='2017-06-31')
sma_10 = talib.SMA(np.array(data['close']), 10)
sma_30 = talib.SMA(np.array(data['close']), 30)

fig = plt.figure(figsize=(17, 10))
ax = fig.add_axes([0,0.2,1,0.5])
ax2 = fig.add_axes([0,0,1,0.2])

mpf.candlestick2_ochl(ax, data['open'], data['close'], data['high'], data['low'], 
                     width=0.5, colorup='r', colordown='g', alpha=0.6)
ax.set_xticks(range(0, len(data['date']), 10))
ax.plot(sma_10, label='10 日均线')
ax.plot(sma_30, label='30 日均线')
ax.legend(loc='upper left')
ax.grid(True)

mpf.volume_overlay(ax2, data['open'], data['close'], data['volume'], colorup='r', colordown='g', width=0.5, alpha=0.8)
ax2.set_xticks(range(0, len(data['date']), 10))
ax2.set_xticklabels(data['date'][::10], rotation=30)
ax2.grid(True)

plt.show()

![](data:image/svg+xml;utf8,<svg%20xmlns=‘http://www.w3.org/2000/svg’%20width=‘1763’%20height='781’>)

最后

细心的同学会发现上面代码块中,所有的子图叫做 ax 和 ax2,这是因为如果把第一个子图的名称换成别的,蜡烛图就画不出来了,这个我也不明白是咋回事,还希望知道的同学指点下。

另,附上子库的官方文档:matplotlib.finance