本文基于 matplotlib,动态复盘了沪深 300ETF 和纳指 ETF 的近 60 天来的走势。通过对动态复盘的制作,对 matplotlib 中常用的函数进行总结和整理。事实上,动态复盘不是目的,其业务意义也不是很大。基于此的 matplotlib 的笔记整理才是本文最大的初衷。
1. 相关库导入#
# 导入所需库
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
确定 matplotlib 的版本(笔者本次使用的 3.0.2)#
print(mpl.__version__)
2. 全局参数初始化#
matplotlib 主要通过 plt.rc、plt.rcParams 来进行全局参数的设定。以下两种设定方法是等价的。
plt.rc('axes', axisbelow=True)
plt.rcParams['axes.axisbelow'] = True
除此以外,还可以通过 plt.rcParams.update () 来实现,该方法更为便捷。
# 设置全局参数
#font.sans-serif:用来正常显示中文标签
#animation.emded_limit:动态图太大了,很容易突破默认 byte,
#如果不设置,显示出来的图是不完整的,
#保险起见可以设一个比较大的数,比如 2^64。
params = {'axes.axisbelow': True,
'font.sans-serif': 'SimHei',
'figure.figsize': (16, 8),
'animation.embed_limit':2**64}
plt.rcParams.update(params)
设置显示风格#
plt.style.use('ggplot')
在使用的过程中,也可以随时调节参数,进行个性化定制。此时,只需要回滚为初始值即可。
plt.rcParams.update(mpl.rcParamsDefault)
3. 数据导入 — 基于 JoinQuant#
security_list=['510300.XSHG','513100.XSHG']
沪深 300ETF#
stock = '510300.XSHG'
纳指 ETF#
stock2 = '513100.XSHG'
df = history(count=60, unit='1d', field='close', security_list=security_list, df=True, skip_paused=False, fq='pre')
4. 静态价格图#
当第一次执行 plt.xxx () 画图代码时,系统会去判断是否已经有了 figure 对象,
如果没有,系统会自动创建一个 figure 对象,并且在这个 figure 之上,自动创建一个 axes 坐标系 (注意:默认创建一个 figure 对象,一个 axes 坐标系)。也就是说,如果我们不设置 figure 对象,那么一个 figure 对象上,只能有一个 axes 坐标系,即我们只能绘制一个图形。Figure、Axes 及 Axis 的关系可以参考图 1 所示。
图 1 Figure、Axes 及 Axis 的关系
以 plt.plot (x,y,format) 为例,plt.plot () 默认画折线图,参数 format 由 {color}{marker}{linestyle} 三方面的要素组成(该三方面的要素亦可单独指定),比如,
- ‘go-’:green colored dots with solid line
- 'r*--' : red stars with dashed lines
- 'bD-.' : blue diamonds with dash-dot line
plt.text 和 plt.annotate 可以用来追加文字及注释。
plt.plot(df.index,df[stock],'r*--')
用 Arrow Props 和 bbox 进行注释#
需要被注释的点的位置设定#
x_annotate = df[stock].idxmax(axis=1)
y_annotate = max(df[stock])
xy: 需要被注释的点的位置#
xytext:注释文本的位置#
arrowprops: xy→xytext 的箭头#
bbox:注释文本显示形状#
plt.annotate(y_annotate,
xy=(x_annotate, y_annotate), xytext=(x_annotate, y_annotate*1.02),
bbox=dict(boxstyle='square', fc='red', linewidth=0.2),
arrowprops=dict(facecolor='red', shrink=0.01, width=0.1),
fontsize=12, color='white', horizontalalignment='center')
文字标注#
文字标注位置设定#
x_text = df[stock].idxmin(axis=1)
y_text = min(df[stock])
文字标注#
plt.text (df.index [-1], df [stock][-1],s="沪深 300 ETF", color='red', size=12, ha='center', va='top')
图 2 静态价格图
一个 Figure 上可以设定布置一个 Axes,主要通过 plt.subplot 的方式实现。如果要实现更为复杂的子图布局,可以使用 plt.subplot2grid、plt.GridSpec 函数。这里就不一一展开。
# 获取每个位置的 axes 对象
subplot (x,y,n) 表示 axes 子图排列为 x 行 * y 列,n 为第 n 个子图#
axes1 = plt.subplot(211)
axes1.plot(df.index,df[stock])
axes2 = plt.subplot(212)
axes2.plot(df.index, df[stock2])
5. 动态价格复现#
(1)构造静态画图函数#
构造一个 animate (frame),其中 frame 可看成是 df 的变化的 index。不同的 frame,就会切片得到 df.iloc [,:]。
#构造一个 animate (frame),其中 frame 可看成是 df 的变化的 index。
#不同的 frame,就会切片得到 df.iloc [,:]
def animate(frame):
axes1.clear()
axes2.clear()
# 不同的 frame,就会切片得到 df.iloc [,:]
df_temp = df.iloc[0,:]
# 折线图
axes1.plot(df_temp.index,df_temp[stock],color='blue')
axes2.plot(df_temp.index,df_temp[stock2],'r*--')
#
axes1.set_xlabel (' 日期 ')
axes1.set_ylabel (' 沪深 300 ETF', color='blue')
axes1.tick_params(axis='y', rotation=0, labelcolor='blue' )
axes2.set\_ylabel("纳指ETF", color='red')
axes2.tick\_params(axis='y', labelcolor='red')
axes2.set\_title("沪深300ETF & 纳指ETF 近3个月表现", fontsize=18)
# 文字标识
axes1.text(df\_temp.index\[-1\], df\_temp\[stock\]\[-1\],s='沪深300 ETF', color='blue', size=7, ha='center', va='top')
axes2.text(df\_temp.index\[-1\], df\_temp\[stock2\]\[-1\],s='纳指ETF', color='red', size=7, ha='center', va='top')
# 注释with Arrow Props and bbox
x\_annotate\_min = df\_temp\[stock\].idxmin(axis=1)
y\_annotate\_min = min(df\_temp\[stock\])
# xy:需要注释的点的位置
# xytext:注释的位置
# arrowprops: xy→xytext的箭头
axes1.annotate(y\_annotate\_min,
xy=(x\_annotate\_min, y\_annotate\_min),
xytext=(x\_annotate\_min, y\_annotate\_min\*0.98),
bbox=dict(boxstyle='square', fc='blue', linewidth=0.1),
arrowprops=dict(facecolor='blue', shrink=0.01, width=0.1),
fontsize=12, color='white', horizontalalignment='center')
x\_annotate\_max = df\_temp\[stock\].idxmax(axis=1)
y\_annotate\_max = max(df\_temp\[stock\])
axes1.annotate(y\_annotate\_max,
xy=(x\_annotate\_max, y\_annotate\_max),
xytext=(x\_annotate\_max, y\_annotate\_max\*1.01),
bbox=dict(boxstyle='square', fc='blue', linewidth=0.1),
arrowprops=dict(facecolor='blue', shrink=0.01, width=0.1),
fontsize=12, color='white', horizontalalignment='center')
x\_annotate2\_min = df\_temp\[stock2\].idxmin(axis=1)
y\_annotate2\_min = min(df\_temp\[stock2\])
# xy:需要注释的点的位置
# xytext:注释的位置
# arrowprops: xy→xytext的箭头
axes2.annotate(y\_annotate2\_min,
xy=(x\_annotate2\_min, y\_annotate2\_min),
xytext=(x\_annotate2\_min, y\_annotate2\_min\*0.98),
bbox=dict(boxstyle='square', fc='red', linewidth=0.1),
arrowprops=dict(facecolor='red', shrink=0.01, width=0.1),
fontsize=12, color='white', horizontalalignment='center')
# Annotate with Arrow Props and bbox
x\_annotate2\_max = df\_temp\[stock2\].idxmax(axis=1)
y\_annotate2\_max = max(df\_temp\[stock2\])
# xy:需要注释的点的位置
# xytext:注释的位置
# arrowprops: xy→xytext的箭头
axes2.annotate(y\_annotate2\_max,
xy=(x\_annotate2\_max, y\_annotate2\_max),
xytext=(x\_annotate2\_max, y\_annotate2\_max\*1.01),
bbox=dict(boxstyle='square', fc='red', linewidth=0.1),
arrowprops=dict(facecolor='red', shrink=0.01, width=0.1),
fontsize=12, color='black', horizontalalignment='center')
(2)生成 figure、axes1、axes2#
和静态价格图的生成不同,这里我们显示调用 figure 函数。可以通过 axes.twinx 来实现右边的 y 轴。
fig, axes1 = plt.subplots(1, 1, dpi=80)
生成右侧的 y 轴#
axes2 = axes1.twinx()
(3)调用 FuncAnimation () 函数#
#参数设定
#fig:即 Figure 对象
#animate:之前定义的静态画图函数
#frames:动画的帧数。通过该帧数来设定 animate 的频率。
#interval:每一帧的时间间隔
ani = FuncAnimation(fig,
animate,
frames=np.arange(1,df.shape[0],1),
interval=300)
ani.save("movie.mp4")
参考文献并推荐阅读:
- 王圣元,《盘一盘 Python 系列特别篇 - Matplotlib Animation》
- MechinelearningPlus,《Matplotlib Tutorial – A Complete Guide to Python Plot w/ Examples》
- MechinelearningPlus,《Top 50 matplotlib Visualizations – The Master Plots》