backtrader 自定义分析指标:LegDown急跌指数

backtrader 自定义分析指标:LegDown急跌指数

Real World Usage — backtrader documentation

https://www.backtrader.com/blog/posts/2015-08-27-real-world-usage/real-world-usage.html

这是源自backtrader官方blog:2015-08-17的真实案例。

当时全球性大股灾,有金融届的朋友,请backtrader团队协助:

“过去几周欧洲市场看起来像是世界末日,一位朋友问我们:是否可以制作一些专业图表,看看相关市场的下降幅度等数据”。

于是,backtrader创建了一个名为:LegDown 急跌指数的快速下降指标,来反映市场的下降范围,该指标也可以称为: HighLowRange 或者 HiLoRange.

有关LegDown 指标,参见官方blog,我们将保存为文件:anz_legdown.py,源码参见本文最后的附录:

LegDown 指标定义文件:anz_legdown.py,所有源码全部来自以上blog,我们没有做任何修改,只是用anz_legdown.py保存下来,以备调用。

以下是调用的LegDown 指标分析数据的主程序核心代码:

#
import backtrader as bt
import topquant2019 as tq
import topq_edu2019 as tqedu
#
import topq_zw2019 as tqzw
#----------------------
from anz_legdown import LegDownUpAnalyzer

#----------------------

print('\n#1,数值数据')
rs0='data/' #
rs0='/TQDat/'
rs0='/TQDat/day/'
rdat=rs0+'stk/'
tim0str,tim9str='2018-01-01','2018-12-31'
#tim0str,tim9str='',''
#
syb='002046'
#
#
syblst=[syb]
qx=tq.tq_init('tq01')
#
tq.pools_get4flst(qx,rdat,syblst,tim0str,tim9str,fgInx=False)
#tq.pools_get4flst(qx,rbas0,baslst,tim0str,tim9str,fgInx=True)
tq.bt_set(qx,anzMod=1)
#
print('\n#2-1,添加自定义分析指标:LegDownUpAnalyzer')
qx.cb.addanalyzer(LegDownUpAnalyzer, _name='legdown')
#
print('\n#2-2,添加回测策略:ma_cross')
qx.cb.addstrategy(tqedu.ma_cross,nfast=10,nslow=30)

#
print('\n#3,调用回溯测试主程序:run')
qx.bt_results= qx.cb.run()
#
#
print('\n#8,工具回测数据,绘制分析图表')
qx.cb.plot()

#
print('\n#9,分析自定义分析指标:legdown')
anzs=qx.bt_results[0].analyzers
anz_leg=anzs.legdown.get_analysis()
#tq.prDic(anz_leg,'anz_lg')


以上案例程序,相对比较简单,与自定义指标相关的主要代码有:

#1,从相关指标定义源码文件,导入LegDownAnalyzer指标

from anz_legdown import LegDownUpAnalyzer

#2,主流程部分,增加自定义指标

print('\n#2-1,添加自定义分析指标:LegDownUpAnalyzer')
qx.cb.addanalyzer(LegDownUpAnalyzer, _name='legdown')

#3,回测完成后,根据回测数据,绘制相关图形。

print('\n#8,工具回测数据,绘制分析图表')
qx.cb.plot()

需要说明的是,backtrader量化软件,有关指标绘制的语句,全部集中在Indicator指标类定义当中。
用户只要在自定义指标代码编程时,设置号plotinfo有关参数即可,非常简单。
在调用指标时,更是无需任何编码,plot绘图语句,get_analysis分析语句,会自动绘制图表,调用相关方分析模块。
这也说明了backtrader量化软件,设计的灵活性和专业性,扩展非常方便。

#4,相关分析数据

print('\n#9,分析自定义分析指标:legdown')
anzs=qx.bt_results[0].analyzers
anz_leg=anzs.legdown.get_analysis()
#tq.prDic(anz_leg,'anz_lg')

因为输出信息是逐日数据,很长,所以默认是屏蔽数据输出。

 

【附录】anz_legdown.py自定义指标源码


# -*- coding: utf-8 -*-
"""
Created on Mon Jan 14 13:13:37 2019

Real World Usage — backtrader documentation
https://www.backtrader.com/blog/posts/2015-08-27-real-world-usage/real-world-usage.html

"""

from __future__ import (absolute_import, division, print_function,
unicode_literals)

import itertools
import operator

import six
from six.moves import map, xrange, zip

import backtrader as bt
import backtrader.indicators as btind
from backtrader.utils import OrderedDict

class LegDown(bt.Indicator):
'''
Calculates what the current legdown has been using:
- Current low
- High from ``period`` bars ago
'''
lines = ('legdown',)
params = (('period', 10),)

def __init__(self):
self.lines.legdown = self.data.high(-self.p.period) - self.data.low

class LegUp(bt.Indicator):
'''
Calculates what the current legup has been using:
- Current high
- Low from ``period`` bars ago

If param ``writeback`` is True the value will be written
backwards ``period`` bars ago
'''
lines = ('legup',)
params = (('period', 10), ('writeback', True),)

def __init__(self):
self.lu = self.data.high - self.data.low(-self.p.period)
self.lines.legup = self.lu(self.p.period * self.p.writeback)

class LegDownUpAnalyzer(bt.Analyzer):
params = (
# If created indicators have to be plotteda along the data
('plotind', True),
# period to consider for a legdown
('ldown', 10),
# periods for the following legups after a legdown
('lups', [5, 10, 15, 20]),
# How to sort: date-asc, date-desc, legdown-asc, legdown-desc
('sort', 'legdown-desc'),
)

sort_options = ['date-asc', 'date-des', 'legdown-desc', 'legdown-asc']

def __init__(self):
# Create the legdown indicator
self.ldown = LegDown(self.data, period=self.p.ldown)
self.ldown.plotinfo.plot = self.p.plotind

# Create the legup indicators indicator - writeback is not touched
# so the values will be written back the selected period and therefore
# be aligned with the end of the legdown
self.lups = list()
for lup in self.p.lups:
legup = LegUp(self.data, period=lup)
legup.plotinfo.plot = self.p.plotind
self.lups.append(legup)

def nextstart(self):
self.start = len(self.data) - 1

def stop(self):
# Calculate start and ending points with values
start = self.start
end = len(self.data)
size = end - start

# Prepare dates (key in the returned dictionary)
dtnumslice = self.strategy.data.datetime.getzero(start, size)
dtslice = map(lambda x: bt.num2date(x).date(), dtnumslice)
keys = dtslice

# Prepare the values, a list for each key item
# leg down
ldown = self.ldown.legdown.getzero(start, size)
# as many legs up as requested
lups = [up.legup.getzero(start, size) for up in self.lups]

# put legs down/up together and interleave (zip)
vals = [ldown] + lups
zvals = zip(*vals)

# Prepare sorting options
if self.p.sort == 'date-asc':
reverse, item = False, 0
elif self.p.sort == 'date-desc':
reverse, item = True, 0
elif self.p.sort == 'legdown-asc':
reverse, item = False, 1
elif self.p.sort == 'legdown-desc':
reverse, item = True, 1
else:
# Default ordering - date-asc
reverse, item = False, 0

# Prepare a sorted array of 2-tuples
keyvals_sorted = sorted(zip(keys, zvals),
reverse=reverse,
key=operator.itemgetter(item))

# Use it to build an ordereddict
self.ret = OrderedDict(keyvals_sorted)

def get_analysis(self):
return self.ret

def print(self, *args, **kwargs):
# Overriden to change default behavior (call pprint)
# provides a CSV printout of the legs down/up
header_items = ['Date', 'LegDown']
header_items.extend(['LegUp_%d' % x for x in self.p.lups])
header_txt = ','.join(header_items)
print(header_txt)

for key, vals in six.iteritems(self.ret):
keytxt = key.strftime('%Y-%m-%d')
txt = ','.join(itertools.chain([keytxt], map(str, vals)))
print(txt)

发表评论

电子邮件地址不会被公开。 必填项已用*标注