失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 量化回测框架Backtrader【3】-核心概念Lines

量化回测框架Backtrader【3】-核心概念Lines

时间:2024-01-04 23:44:05

相关推荐

量化回测框架Backtrader【3】-核心概念Lines

目录

一,什么是Lines

二,Lines的声明

三,Lines的访问

四,Lines的长度

五,Lines和参数的继承机制

题外话

这讲介绍BackTrader的一个贯穿框架始终的核心概念-lines。

一,什么是Lines

我们知道股票,期货这些金融概念都跟一个属性息息相关,那就是时间。而BackTrader的基本功能回测其实说白了就是将一个算法(策略)运行在一个一时间为基本维度的数据(股票,期货等)上进行迭代。时间这种顺序性极强的概念跟现实世界中的直线的概念是很相似的,于是BackTrader将框架中绝大部分的与时间概念相关的类中都包含了lines这个成员。

上节讲的数据导入中,导入的每一个数据类别都会生成一条lines,包括时间数据,还是拿之前的代码作为示例:

from datetime import datetimeimport backtrader as btimport backtrader.feeds as btfeedsclass SmaCross(bt.SignalStrategy):def __init__(self):sma1, sma2 = bt.ind.SMA(period=10), bt.ind.SMA(period=30)crossover = bt.ind.CrossOver(sma1, sma2)self.signal_add(bt.SIGNAL_LONG, crossover)cerebro = bt.Cerebro()cerebro.addstrategy(SmaCross)data = btfeeds.GenericCSVData(dataname='stock_data.csv',fromdate=datetime(, 1, 1),todate=datetime(, 12, 31),nullvalue=0.0,dtformat=('%Y%m%d'),datetime=1,open=2,high=3,low=4, close=5,volume=9,openinterest=-1)cerebro.adddata(data)cerebro.run()cerebro.plot(iplot=False)

在数据导入(代码中的GenericCSVData)时,生成了‘datetime’,‘open’,‘high’,‘low’,‘close’,‘volume’,这六条lines,另外在类SmaCross里面也生成了‘sma1’,‘sma2’以及‘crossover’三条lines。‘sma1’,‘sma2’,‘crossover’是策略用的指标,指标会在之后的章节详细讲解,这边只要了解就好。

二,Lines的声明

当你需要开发一个新的指标或者导入新的数据类别(上一讲最后部分)的时候,则必须在该指标中声明一条line来保存产生的指标值或数据类别。

就像添加参数一样,以元组作为类属性进行声明。 不支持字典,因为它们不按照插入顺序存储内容。摘抄上讲最后的例子说明:

class GenericCSV_extend(GenericCSVData):# 添加change数据lines = ('change',)

注意:如果您将一个字符串当成元组的一个成员,则在字符串后面需要添加逗号,否则,字符串中的每个字母都将被解释为元组的成员。这是Python本身的语法特性

三,Lines的访问

在策略或指标模块中需要访问lines来获取需要处理的数据,尽管两者的目的有些不同,策略是使用数据来做出开仓,平仓等操作决策,而指标则是使用数据来生成新的数据供策略使用。

对lines的访问有几种形式:

数组下标的方式lines[X]line后面跟X,比如lineXline跟X中间用一个下划线连接,比如line_X

X代表一个数字,以上三种方式都能访问到同一条line,另外还有一种line后面什么都没有就相当于line0

示例如下:

class TestStrategy(bt.SignalStrategy):def __init__(self):print(self.data.line)print(self.data.line0)print(self.data.line_0)print(self.data.lines[0])def next(self):passcerebro = bt.Cerebro()cerebro.addstrategy(TestStrategy)data = btfeeds.GenericCSVData(dataname='stock_data.csv',fromdate=datetime(, 1, 1),todate=datetime(, 12, 31),nullvalue=0.0,dtformat=('%Y%m%d'),datetime=1,open=2,high=3,low=4, close=5,volume=9,openinterest=-1)cerebro.adddata(data)cerebro.run()

运行之后的输出如下:

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC57FAC8>

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC57FAC8>

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC57FAC8>

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC57FAC8>

可见四种访问方式都指向同一个LineBuffer对象

还有一种简便的省略可以line访问方式,

self.dataX_Y

相当于

self.datas[X].lines[Y]

在上面的例子中,我们都是用数字序号的方式来访问对应的line,这有一个弊端就是不好阅读,data在导入的时候都是带名字,‘close’,‘open’等,那么我们也可以用名字的方式来访问,这样代码阅读起来更好懂一点

class TestStrategy(bt.SignalStrategy):def __init__(self):print(self.data.line)print(self.data.close)print(self.data.lines.close)print(self.data.line1)print(self.data.low)print(self.data.lines.low)def next(self):pass

将上例中的class TestStrategy(bt.SignalStrategy)替换为这个,运行的输出是这样

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC6502B0>

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC6502B0>

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC6502B0>

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC650320>

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC650320>

<backtrader.linebuffer.LineBuffer object at 0x000002A5EC650320>

前面三个和后面三个分别指向同一个对象,可见line0就是close,而line1是low。虽然他们的作用一样,一般情况下能用名字尽量用名字,可读性的重要性不言而喻。但有一个例外就是在指标函数的内部,由于指标函数会生成新的line输出,为了避免可能跟新的line同名,可以使用数字索引。

四,Lines的长度

就像我开头说的,运行回测相对于将策略从头到尾遍历一遍数据。当回测正在运行时,会有两个长度的概念,一个是过去数据的长度(已经运行过的数据),另一个是总的数据长度。做个比喻就像是爬台阶,每一个台阶就是一个数据,过去的数据就是已经爬完的台阶,脚下的台阶就是当前正在处理的数据,数据总长度就是全部的台阶数。已处理数据长度(爬过的台阶)是动态增长的,而数据总长(总台阶数)是固定的。

这两种长度在运行的过程中都可以获取,

python的len方法可以获取运行时的已处理数据长度buflen可以获取数据总长度

示例如下:

class TestStrategy(bt.SignalStrategy):def __init__(self):passdef next(self):print("已经处理了%d个数据, 总共有%d个数据" % (len(self),self.data.buflen()))cerebro = bt.Cerebro()cerebro.addstrategy(TestStrategy)data = btfeeds.GenericCSVData(dataname='stock_data.csv',fromdate=datetime(, 1, 1),todate=datetime(, 12, 31),nullvalue=0.0,dtformat=('%Y%m%d'),datetime=1,open=2,high=3,low=4, close=5,volume=9,openinterest=-1)cerebro.adddata(data)cerebro.run()

只复制了前7行输出:

已经处理了1个数据, 总共有468个数据

已经处理了2个数据, 总共有468个数据

已经处理了3个数据, 总共有468个数据

已经处理了4个数据, 总共有468个数据

已经处理了5个数据, 总共有468个数据

已经处理了6个数据, 总共有468个数据

已经处理了7个数据, 总共有468个数据

五,Lines和参数的继承机制

BackTrader提供了一种元语言来支持参数和lines的声明并且已努力使其与标准Python继承规则兼容。

参数的继承

支持多重继承基类的参数被继承如果多个基类定义相同的参数,则使用继承列表中最后一个类的默认值如果在子类中重新定义了相同的参数,则新的默认值将取代基类的默认值

lines的继承

支持多重继承所有基类的lines均被继承。 line被命名的情况下,如果在基类中被多次命名同一名称,则该line只会有一个版本

题外话

看过官方文档的小伙伴应该发现了,我的文章有的跟官方文档一致,有的又不一样。这算是我自己的风格吧,这个系列文章我是一边学习一边写的,是我学习的记录,所以我会把自己当时的理解写出来,包括例子也可能会用自己做的例子。官方文档里比较直白,好理解的,我可能会直接翻译引用一下。顺序和内容覆盖范围跟官方文档也不会一样,毕竟不是原文翻译,这样的方式可以比较自由轻松一点,但缺点就是可能错误也会多一些,所以欢迎大家就没看明白或有错误的地方跟我探讨!

如果觉得《量化回测框架Backtrader【3】-核心概念Lines》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。