BackTrader 中文文档(二)(3)https://developer.aliyun.com/article/1489218
addtz(tz)
这也可以通过参数tz
完成
为策略添加全局时区。参数tz
可以是
None
:在这种情况下,策略显示的日期时间将为 UTC,这一直是标准行为pytz
实例。将按此使用它将 UTC 时间转换为所选时区string
。将尝试实例化一个pytz
实例。integer
。对于策略,使用与self.datas
可迭代对象中相应data
相同的时区(0
将使用来自data0
的时区)
add_timer(when, offset=datetime.timedelta(0), repeat=datetime.timedelta(0), weekdays=[], weekcarry=False, monthdays=[], monthcarry=True, allow=None, tzdata=None, strats=False, cheat=False, *args, **kwargs)
安排定时器以调用notify_timer
- 参数when(-) – 可以是
datetime.time
实例(见下文tzdata
)bt.timer.SESSION_START
表示会话开始bt.timer.SESSION_END
表示会话结束- 必须是
datetime.timedelta
实例的offset
- 用于偏移值
when
。与SESSION_START
和SESSION_END
结合使用时,它具有有意义的用途,用于指示诸如在会话开始后15 分钟
调用定时器之类的事情。
- 必须是
datetime.timedelta
实例的repeat
指示如果在第 1 次调用后,后续调用将在同一会话中按计划的repeat
增量中安排
一旦定时器超过会话结束,它将被重置为when
的原始值 weekdays
:一个排序的可迭代对象,其中的整数表示可以实际调用定时器的日期(ISO 代码,星期一为 1,星期日为 7)
如果未指定,定时器将在所有日期上都处于活动状态。weekcarry
(默认值:False
)。如果为True
且周几未见(例如:交易假期),则定时器将在下一天执行(即使在新的一周中)monthdays
:一个排序的可迭代对象,其中的整数表示定时器必须执行的月份中的哪一天。例如,每个月的第15天始终执行
如果未指定,定时器将在所有日期上都处于活动状态monthcarry
(默认值:True
)。如果当天没有看到(周末、交易假期),则定时器将在下一个可用日期执行。allow
(默认值:None
)。一个回调函数,接收一个datetime.date
实例,并返回True
如果该日期允许定时器,否则返回False
tzdata
可以是None
(默认值),一个pytz
实例或一个data feed
实例。None
:when
按面值解释(即使它不是 UTC,也会处理它)pytz
实例:when
将被解释为时区实例指定的本地时间。data feed
实例:when
将被解释为数据源实例的tz
参数指定的本地时间。
注意
如果when
是SESSION_START
或SESSION_END
且tzdata
为None
,则系统中的第 1 个数据源(也称为self.data0
)将用作查找会话时间的参考。strats
(默认值:False
)还会调用策略的notify_timer
cheat
(默认为False
)如果为True
,则会在经纪人有机会评估订单之前调用计时器。例如,在交易会话开始之前,可以根据开盘价下订单。*args
: 任何额外的参数都将传递给notify_timer
。**kwargs
: 任何额外的关键字参数都将传递给notify_timer
。
返回值:
- 创建的计时器
notify_timer(timer, when, *args, **kwargs)
接收计时器通知,其中 timer
是由 add_timer
返回的计时器,when
是调用时间。args
和 kwargs
是传递给 add_timer
的任何额外参数。
实际的 when
时间可能会晚一些,但系统可能无法在之前调用计时器。该值是计时器值,而不是系统时间。
add_order_history(orders, notify=True)
将订单历史记录添加到经纪人中,以供性能评估直接执行
orders
: 是一个可迭代对象(例如列表、元组、迭代器、生成器),其中每个元素也将是一个具有以下子元素的可迭代对象(有 2 种格式)[datetime, size, price]
或[datetime, size, price, data]
注意必须按照日期时间升序排序(或生成排序的元素)。其中:
datetime
是一个 pythondate/datetime
实例,或者是一个格式为 YYYY-MM-DD[THH:MM:SS[.us]] 的字符串,方括号中的元素是可选的。size
是一个整数(买入为正,卖出为负)price
是一个浮点数/整数- 如果存在
data
,则可以采用以下任何值
- None - 将使用第 1 个数据源作为目标。
- integer - 将使用该索引对应的数据(在Cerebro中的插入顺序)。
- string - 该名称的数据,例如通过
cerebro.addata(data, name=value)
分配,将作为目标。
notify
(默认值:True)
如果设为True
,则会通知系统中插入的第 1 个策略,其会根据每个订单中的信息创建人工订单。
注意
隐含在描述中的是需要添加一个数据源,该数据源是订单的目标。例如,分析器需要追踪回报率。
节省内存
发布版本 1.3.1.92已经重新设计并完全实现了先前存在的节省内存方案,尽管没有受到太多宣传并且使用较少。
backtrader
曾经(并将继续)在内存较大的机器上开发,并且与通过绘图提供的视觉反馈是一个必需的美好事物相结合,使得设计决策变得容易:将所有内容保存在内存中。
这个决定有一些缺点:
- 用于数据存储的
array.array
在超过某些限制时必须分配和移动数据。 - RAM 较少的机器可能会受到影响。
- 连接到可以在线运行数周/数月、提供数千秒/分钟分辨率 ticks 的实时数据源
后者比第一点更加重要,因为另一个设计决定是为了 backtrader
:
- 必须是纯 Python,以便在需要时在嵌入式系统中运行。
将来的一个场景可能是backtrader
连接到第二台机器,该机器提供实时数据源,而backtrader
本身运行在类似 Raspberry Pi 或甚至更有限的设备上,如 ADSL 路由器(AVM Frit!Box 7490,带有 Freetz 映像)
因此需要 backtrader
支持动态内存方案。现在 Cerebro
可以使用以下语义进行实例化或run
:
- exactbars(默认值:False)默认值为
False
,每个存储在一行中的值都会保存在内存中。可能的值:
True
或1
:所有“lines”对象将内存使用量减少到自动计算的最小周期。如果简单移动平均值的周期为 30,底层数据将始终具有 30 个条形图的运行缓冲区,以允许计算简单移动平均值
- 此设置将停用
preload
和runonce
。 - 使用此设置还会停用plotting。
-1
:在策略级别,数据和指标/操作将保留所有数据在内存中。例如:RSI
在内部使用指标UpDay
进行计算。此子指标将不会将所有数据保存在内存中。
- 这允许保持
plotting
和preloading
处于活动状态。 runonce
将被停用。
-2
:作为策略属性保留的数据和指标将在内存中保存所有数据。例如:RSI
在内部使用指标UpDay
进行计算。此子指标将不会将所有数据保存在内存中。如果在__init__
中定义了类似a = self.data.close - self.data.high
这样的内容,则a
将不会将所有数据保存在内存中。
- 这允许保持
plotting
和preloading
处于活动状态。 runonce
将被停用。
一如既往,一个例子胜过千言万语。一个示例脚本显示了差异。它针对 1996 年至 2015 年的 Yahoo 每日数据运行,总共 4965
天。
注意
这只是一个小样本。每天交易 14 小时的 EuroStoxx50 期货,在仅 1 个月的交易中将产生大约 18000 个 1 分钟的 K 线。
执行脚本 1^(st)以查看在不请求内存节省时使用了多少内存位置:
$ ./memory-savings.py --save 0 Total memory cells used: 506430
对于 1 级(总储蓄):
$ ./memory-savings.py --save 1 Total memory cells used: 2041
天啊!!!从五十万跌至2041
。确实。系统中的每个lines对象都使用collections.deque
作为缓冲区(而不是array.array
),并且长度被限制在绝对需要的最小值以进行请求的操作。例如:
- 在数据源上使用期间为
30
的SimpleMovingAverage
策略。
在这种情况下,将进行以下调整:
- 数据源将具有
30
个位置的缓冲区,这是SimpleMovingAverage
产生下一个值所需的数量 SimpleMovingAverage
将有一个1
位置的缓冲区,因为除非其他指标需要(这些指标将依赖于移动平均线),否则不需要保留较大的缓冲区。
注意
这种模式最吸引人且可能最重要的功能是,所使用的内存量在脚本的整个生命周期内保持不变。
无论数据源的大小如何。
如果例如连接到长时间的实时数据源,则这将非常有用。
但请注意:
- 绘图不可用
- 还有其他的内存消耗来源,随着时间的推移会积累,比如策略生成的
orders
。 - 此模式只能在
cerebro
中的runonce=False
时使用。对于实时数据源,这也是强制性的,但在简单回测的情况下,这比runonce=True
慢。
肯定有一个权衡点,从而内存管理比逐步执行回测更昂贵,但这只能由平台的最终用户在每个案例中进行判断。
现在是负级别。这些级别旨在在仍然节省相当数量的内存的同时保持绘图可用。第一级别-1
:
$ ./memory-savings.py --save -1 Total memory cells used: 184623
在这种情况下,指标的第 1 级别(在策略中声明的那些)保持其完整长度的缓冲区。但是如果这些指标依赖于其他指标(这就是情况),则子对象将被长度限制。在这种情况下,我们已经从:
506430
个内存位置到->184623
超过 50%的节省。
注意
当然,array.array
对象已被collections.deque
所取代,这在内存方面更昂贵,尽管在操作方面更快。但是collections.deque
对象相当小,并且节省了大致计算的内存位置的使用。
现在是级别-2
,这也是为了节省在策略级别上声明的指标的内存,这些指标已被标记为不需要绘制:
$ ./memory-savings.py --save -2 Total memory cells used: 174695
现在没有保存太多。这是因为一个单独的指标已被标记为不需要绘制:TestInd().plotinfo.plot = False
让我们看看来自最后一个示例的绘图:
$ ./memory-savings.py --save -2 --plot Total memory cells used: 174695
对于感兴趣的读者,示例脚本可以生成对指标层次结构中遍历的每个行对象的详细分析。运行时启用绘图(保存在-1
):
$ ./memory-savings.py --save -1 --lendetails -- Evaluating Datas ---- Data 0 Total Cells 34755 - Cells per Line 4965 -- Evaluating Indicators ---- Indicator 1.0 Average Total Cells 30 - Cells per line 30 ---- SubIndicators Total Cells 1 ---- Indicator 1.1 _LineDelay Total Cells 1 - Cells per line 1 ---- SubIndicators Total Cells 1 ... ---- Indicator 0.5 TestInd Total Cells 9930 - Cells per line 4965 ---- SubIndicators Total Cells 0 -- Evaluating Observers ---- Observer 0 Total Cells 9930 - Cells per Line 4965 ---- Observer 1 Total Cells 9930 - Cells per Line 4965 ---- Observer 2 Total Cells 9930 - Cells per Line 4965 Total memory cells used: 184623
同样的,但启用了最大节省(1
):
$ ./memory-savings.py --save 1 --lendetails -- Evaluating Datas ---- Data 0 Total Cells 266 - Cells per Line 38 -- Evaluating Indicators ---- Indicator 1.0 Average Total Cells 30 - Cells per line 30 ---- SubIndicators Total Cells 1 ... ---- Indicator 0.5 TestInd Total Cells 2 - Cells per line 1 ---- SubIndicators Total Cells 0 -- Evaluating Observers ---- Observer 0 Total Cells 2 - Cells per Line 1 ---- Observer 1 Total Cells 2 - Cells per Line 1 ---- Observer 2 Total Cells 2 - Cells per Line 1
第二个输出立即显示了数据源中的行数被限制为38
个内存位置,而不是完整数据源长度4965
。
并且指标和观察者在可能的情况下被限制为1
,如输出的最后几行所示。
脚本代码和用法
在backtrader
的源代码中可作为示例使用。用法:
$ ./memory-savings.py --help usage: memory-savings.py [-h] [--data DATA] [--save SAVE] [--datalines] [--lendetails] [--plot] Check Memory Savings optional arguments: -h, --help show this help message and exit --data DATA Data to be read in (default: ../../datas/yhoo-1996-2015.txt) --save SAVE Memory saving level [1, 0, -1, -2] (default: 0) --datalines Print data lines (default: False) --lendetails Print individual items memory usage (default: False) --plot Plot the result (default: False)
代码:
from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import sys import backtrader as bt import backtrader.feeds as btfeeds import backtrader.indicators as btind import backtrader.utils.flushfile class TestInd(bt.Indicator): lines = ('a', 'b') def __init__(self): self.lines.a = b = self.data.close - self.data.high self.lines.b = btind.SMA(b, period=20) class St(bt.Strategy): params = ( ('datalines', False), ('lendetails', False), ) def __init__(self): btind.SMA() btind.Stochastic() btind.RSI() btind.MACD() btind.CCI() TestInd().plotinfo.plot = False def next(self): if self.p.datalines: txt = ','.join( ['%04d' % len(self), '%04d' % len(self.data0), self.data.datetime.date(0).isoformat()] ) print(txt) def loglendetails(self, msg): if self.p.lendetails: print(msg) def stop(self): super(St, self).stop() tlen = 0 self.loglendetails('-- Evaluating Datas') for i, data in enumerate(self.datas): tdata = 0 for line in data.lines: tdata += len(line.array) tline = len(line.array) tlen += tdata logtxt = '---- Data {} Total Cells {} - Cells per Line {}' self.loglendetails(logtxt.format(i, tdata, tline)) self.loglendetails('-- Evaluating Indicators') for i, ind in enumerate(self.getindicators()): tlen += self.rindicator(ind, i, 0) self.loglendetails('-- Evaluating Observers') for i, obs in enumerate(self.getobservers()): tobs = 0 for line in obs.lines: tobs += len(line.array) tline = len(line.array) tlen += tdata logtxt = '---- Observer {} Total Cells {} - Cells per Line {}' self.loglendetails(logtxt.format(i, tobs, tline)) print('Total memory cells used: {}'.format(tlen)) def rindicator(self, ind, i, deep): tind = 0 for line in ind.lines: tind += len(line.array) tline = len(line.array) thisind = tind tsub = 0 for j, sind in enumerate(ind.getindicators()): tsub += self.rindicator(sind, j, deep + 1) iname = ind.__class__.__name__.split('.')[-1] logtxt = '---- Indicator {}.{} {} Total Cells {} - Cells per line {}' self.loglendetails(logtxt.format(deep, i, iname, tind, tline)) logtxt = '---- SubIndicators Total Cells {}' self.loglendetails(logtxt.format(deep, i, iname, tsub)) return tind + tsub def runstrat(): args = parse_args() cerebro = bt.Cerebro() data = btfeeds.YahooFinanceCSVData(dataname=args.data) cerebro.adddata(data) cerebro.addstrategy( St, datalines=args.datalines, lendetails=args.lendetails) cerebro.run(runonce=False, exactbars=args.save) if args.plot: cerebro.plot(style='bar') def parse_args(): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description='Check Memory Savings') parser.add_argument('--data', required=False, default='../../datas/yhoo-1996-2015.txt', help='Data to be read in') parser.add_argument('--save', required=False, type=int, default=0, help=('Memory saving level [1, 0, -1, -2]')) parser.add_argument('--datalines', required=False, action='store_true', help=('Print data lines')) parser.add_argument('--lendetails', required=False, action='store_true', help=('Print individual items memory usage')) parser.add_argument('--plot', required=False, action='store_true', help=('Plot the result')) return parser.parse_args() if __name__ == '__main__': runstrat()