backtrader 的画图主要是通过 matplotlib 来实现的,当 cerebro 中加载的数据比较多,指标比较多的时候,用 matplotlib 画图就会显得比较臃肿,现在也有不少人对 backtrader 的画图功能进行了扩展,比如backtrader_plotting(注:在 python3.8 ubuntu20.04 中,有 bug)
关于画图,其实已经讲过一部分内容了,比如在如何创建一个新的技术指标(indicator) 讲了 plotinfo 的一些参数,在这里,进行一些补充:
plotinfo = dict(
plot = True, # 是否显示这个指标值,True 的时候显示,False 的时候不显示
subplot = True,# 是否把指标显示到另一个窗口,True 是显示到另一个窗口,False 是显示在主图
plotname = "",# 显示 line 的名称,默认是 class.__name__
plotskip=False,# 旧版本的 matplotlib 上使用,已经移除
plotabove = False,# 默认情况下,指标是在主图或者主图下面,当是 True 的时候,指标值会画在主图上面的图上
plotlinelabels = False,# 默认情况下,如果计算的是指标 1 的指标 2,只显示指标 2,当是 True 的时候,两个都显示
plotlinevalues=True,# 控制是否显示指标以及 observer 的最后一个数据的值
plotvaluetags=True,# 控制是否显示最后一个值得 value tag
plotymargin=0.15,# 画图的时候距离顶部和底部的距离是 0.15
plotyticks=[1.0, -1.0],# 计算 y 的边界值,当是空列表的时候会自动计算
plothlines=[1.0, -1.0],# 计算水平的边界值,默认在 1 到-1 的范围
plotyhlines=[],# 用同一个参数,控制垂直和水平范围
plotforce=False,# 当认为一个指标应该被画出来,但是实际上并没有,就把这个设置成 True,作为最后的手段
plotmaster=None,# 是否显示指标是在哪个数据上计算的
plotylimited=True,# 计算的指标控制数据显示的范围;如果是 False,是数据控制显示范围,指标可能超出图形的边界
) 在计算技术指标的时候,可以自定义显示的名称以及参数等数据,举例说明:
sma = bt.indicators.SimpleMovingAverage(self.data, period=15, plotname='mysma')
# 或者使用下面的代码
sma = bt.indicators.SimpleMovingAverage(self.data, period=15)
sma.plotinfo.plotname = 'mysma' line = dict(
_plotskip = True,# 如果设置成 True,这个 line 将会被忽略,不会画图,False 会画图
_plotvalue = True,# 控制是否画出来这个 line 的最后一个值
_plotvaluetag = True,# 控制是否把最后一个值显示在右边的坐标轴上
_name = "xxx",# 把 line 的名称改变为 xxx
_skipnan = False,# 默认不忽略缺失值,如果设置成 True,将会忽略缺失值,在两个点之间画线
_samecolor = True,# 强制让 matplotlib 画图的颜色是一样,否则将按照 matplotlib 的机制,将循环给每个图赋予不同的颜色
_method = "",# 给这个 line 画图选定特定的方法,如果没有指定,将会使用最基本的画图方法
_fill_gt(line_name or value,color),# 两个参数,一个是 line 的名称或者数值,一个是颜色;用于填充颜色
) 举例说明一些常见的用法:
# 把 histo 这条线画成柱状图(bar)
lines = ('histo',)
plotlines = dict(histo=dict(_method='bar', alpha=0.50, width=1.0))
# 把 myline 这条线超过 other_line 的部分画成红色
plotlines = dict(
myline=dict(_fill_gt('other_line', 'red'))
)
# 把 myline 超过 50 的部分画成红色
plotlines = dict(
myline=dict(_fill_gt(50, 'red))
)
# 把 myline 这条线超过 other_line 的部分画成红色,设置 50%的透明度
plotlines = dict(
myline=dict(_fill_gt('other_line', ('red', 0.50)))
)
# 买卖点设置
plotlines = dict(
buy=dict(marker='^', markersize=8.0, color='lime', fillstyle='full'),
sell=dict(marker='v', markersize=8.0, color='red', fillstyle='full')
)
# observer 中的 trades 的 pnl 的设置
...
lines = ('pnlplus', 'pnlminus')
...
plotlines = dict(
pnlplus=dict(_name='Positive',
marker='o', color='blue',
markersize=8.0, fillstyle='full'),
pnlminus=dict(_name='Negative',
marker='o', color='red',
markersize=8.0, fillstyle='full')
)
# observer 中回撤设置
lines = ('drawdown', 'maxdrawdown',)
...
plotlines = dict(maxdrawdown=dict(_plotskip='True',))
# 技术指标中布林带的设置
plotlines = dict(
mid=dict(ls='--'),
top=dict(_samecolor=True),
bot=dict(_samecolor=True),
)
# 技术指标中 Stochastic 的设置
lines = ('percK', 'percD',)
...
plotlines = dict(percD=dict(_name='%D', ls='--'),
percK=dict(_name='%K')) 当设计指标和 observer 的时候,有两个函数可以用于控制画图
-
_plotlabel(self)
控制显示的指标名称;会在指标的名称后面增加新的参数,如:
def _plotlabel(self): plabels = [self.p.period] plabels += [self.p.movav] * self.p.notdefault('movav') return plabels
-
_plotinit(self)
将会在画图之前调用,做一些特殊的初始化操作;
# 将会额外增加两条水平的线,upperband 和 lowerband def _plotinit(self): self.plotinfo.plotyhlines = [self.p.upperband, self.p.lowerband]
cerebro.plot()有很多参数可以使用,可以使用不同的参数控制画出来的图形
-
plotter
控制画图的属性值,默认值是 None,将会使用默认的 plotter,即 PlotScheme
-
numfigs
当一个图形中有太多的 bar 的时候,将会不容易观察;这个将会把图形切割成很多的片段
-
iplot
如果在 notebook 中进行画图,将会自动使用 inline 模式
-
**kwargs
传入的参数将会改变 plotter 或者 PlotScheme 的值
# 默认画图中的一些参数,大家可以忽略,基本用不到.最好能够结合 matplotlib 来看这一部分
# self.style 这个比较有意思,可以设置线段的类型
class PlotScheme(object):
def __init__(self):
# to have a tight packing on the chart wether only the x axis or also
# the y axis have (see matplotlib)
self.ytight = False
# y-margin (top/bottom) for the subcharts. This will not overrule the
# option plotinfo.plotymargin
self.yadjust = 0.0
# Each new line is in z-order below the previous one. change it False
# to have lines paint above the previous line
self.zdown = True
# Rotation of the date labes on the x axis
self.tickrotation = 15
# How many "subparts" takes a major chart (datas) in the overall chart
# This is proportional to the total number of subcharts
self.rowsmajor = 5
# How many "subparts" takes a minor chart (indicators/observers) in the
# overall chart. This is proportional to the total number of subcharts
# Together with rowsmajor, this defines a proportion ratio betwen data
# charts and indicators/observers charts
self.rowsminor = 1
# Distance in between subcharts
self.plotdist = 0.0
# Have a grid in the background of all charts
self.grid = True
# Default plotstyle for the OHLC bars which (line -> line on close)
# Other options: 'bar' and 'candle'
self.style = 'line'
# Default color for the 'line on close' plot
self.loc = 'black'
# Default color for a bullish bar/candle (0.75 -> intensity of gray)
self.barup = '0.75'
# Default color for a bearish bar/candle
self.bardown = 'red'
# Level of transparency to apply to bars/cancles (NOT USED)
self.bartrans = 1.0
# Wether the candlesticks have to be filled or be transparent
self.barupfill = True
self.bardownfill = True
# Wether the candlesticks have to be filled or be transparent
self.fillalpha = 0.20
# Wether to plot volume or not. Note: if the data in question has no
# volume values, volume plotting will be skipped even if this is True
self.volume = True
# Wether to overlay the volume on the data or use a separate subchart
self.voloverlay = True
# Scaling of the volume to the data when plotting as overlay
self.volscaling = 0.33
# Pushing overlay volume up for better visibiliy. Experimentation
# needed if the volume and data overlap too much
self.volpushup = 0.00
# Default colour for the volume of a bullish day
self.volup = '#aaaaaa' # 0.66 of gray
# Default colour for the volume of a bearish day
self.voldown = '#cc6073' # (204, 96, 115)
# Transparency to apply to the volume when overlaying
self.voltrans = 0.50
# Transparency for text labels (NOT USED CURRENTLY)
self.subtxttrans = 0.66
# Default font text size for labels on the chart
self.subtxtsize = 9
# Transparency for the legend (NOT USED CURRENTLY)
self.legendtrans = 0.25
# Wether indicators have a leged displaey in their charts
self.legendind = True
# Location of the legend for indicators (see matplotlib)
self.legendindloc = 'upper left'
# Plot the last value of a line after the Object name
self.linevalues = True
# Plot a tag at the end of each line with the last value
self.valuetags = True
# Default color for horizontal lines (see plotinfo.plothlines)
self.hlinescolor = '0.66' # shade of gray
# Default style for horizontal lines
self.hlinesstyle = '--'
# Default width for horizontal lines
self.hlineswidth = 1.0
# Default color scheme: Tableau 10
self.lcolors = tableau10
# strftime Format string for the display of ticks on the x axis
self.fmt_x_ticks = None
# strftime Format string for the display of data points values
self.fmt_x_data = None PlotScheme 提供了一个可以被重写的方法,用于定义下个 line 的颜色:
def color(self,idx):
pass
# idx 是现在的 index 在一个单独的幅图中画图。
# macd 有三个 line,将会使用 0,1,2 三个 idx
# 在下个图或者另外的指标中,idx 将会重新从 idx 开始 backtrader 默认使用 Tableau 10 Color Palette,并且把 index 修改成如下:
tab10_index = [3, 0, 2, 1, 2, 4, 5, 6, 7, 8, 9] 通过重写 color 或者在 plot 中改变 lcolors 的变量值,整个画图的颜色是可以完全改变的。在源代码中,还有Tableau 10 Light 和 the Tableau 20 color palettes 的定义,感兴趣可以参考。
如果 cerebro.plot()包含的日期或者 bar 个数较多,可以只画出来一部分,cerebro.plot()有两个参数 start 和 end 支持截取一部分日期或者 bar,参数也支持两种,一种是 datetime 格式,一种是数字格式
# 使用 datetime 格式
cerebro.plot(start=datetime.date(2005, 7, 1), end=datetime.date(2006, 1, 31))
# 或者使用数字
cerebro.plot(start=75, end=185) 如果要加载两个以上的数据,并且希望这两个数据共享一个坐标轴,就需要对第二个以后的数据做特殊处理
import backtrader as bt
cerebro = bt.Cerebro()
data0 = bt.feeds.MyFavouriteDataFeed(dataname='futurename')
cerebro.adddata(data0)
data1 = bt.feeds.MyFavouriteDataFeed(dataname='spotname')
# 需要对 data1 额外做下面的处理
data1.compensate(data0)
data1.plotinfo.plotmaster = data0
data1.plotinfo.sameaxis = True
# 加载 data1
cerebro.adddata(data1)
...
cerebro.run() 智慧、心灵、财富,总要有一个在路上,愿我们能在人生的道路上,不断成长、不断成熟~~~
感兴趣可以关注我的专栏:
my_quant_study_note:分享一些关于量化投资、量化交易相关的思考
backtrader 量化投资回测与交易:本专栏免费,分享 backtrader 相关的内容。
[量化投资神器-backtrader 源码解析-从入门到精通:本专栏目前收费 99 元,预计更新 100 篇策略+20 篇 backtrader 讲解+80 篇源代码分析。](