BackTrader 中文文档(三)(2)

简介: BackTrader 中文文档(三)

BackTrader 中文文档(三)(1)https://developer.aliyun.com/article/1489225

样本二进制数据源

backtrader已经为VisualChart的导出定义了一个 CSV 数据源(VChartCSVData),但也可以直接读取二进制数据文件。

让我们做吧(完整的数据源代码可以在底部找到)

初始化

二进制的 VisualChart 数据文件可以包含每日数据(.fd 扩展名)或分钟数据(.min 扩展名)。在这里,参数timeframe将用于区分正在读取的文件类型。

__init__中,为每种类型设置不同的常量。

def __init__(self):
        super(VChartData, self).__init__()
        # Use the informative "timeframe" parameter to understand if the
        # code passed as "dataname" refers to an intraday or daily feed
        if self.p.timeframe >= TimeFrame.Days:
            self.barsize = 28
            self.dtsize = 1
            self.barfmt = 'IffffII'
        else:
            self.dtsize = 2
            self.barsize = 32
            self.barfmt = 'IIffffII'

开始

当回测开始时(在优化过程中实际上可以多次启动),数据源将会启动

start方法中,打开二进制文件,除非已传递了类似文件的对象。

def start(self):
        # the feed must start ... get the file open (or see if it was open)
        self.f = None
        if hasattr(self.p.dataname, 'read'):
            # A file has been passed in (ex: from a GUI)
            self.f = self.p.dataname
        else:
            # Let an exception propagate
            self.f = open(self.p.dataname, 'rb')

停止

在回测完成时调用。

如果文件已打开,则将其关闭

def stop(self):
        # Close the file if any
        if self.f is not None:
            self.f.close()
            self.f = None

实际加载

实际工作是在_load中完成的。调用以加载下一组数据,这种情况下的下一个数据是:datetime、open、high、low、close、volume、openinterest。在backtrader中,“实际”时刻对应于索引 0。

从打开的文件中读取一定数量的字节(由__init__期间设置的常量确定),使用struct模块解析,如果需要进一步处理(例如使用 divmod 操作处理日期和时间),然后存储在数据源的lines中:datetime、open、high、low、close、volume、openinterest。

如果无法从文件中读取数据,则假定已到达文件结束(EOF)。

  • 返回False表示没有更多数据可用

或者如果数据已加载并解析:

  • 返回True表示数据集加载成功
def _load(self):
        if self.f is None:
            # if no file ... no parsing
            return False
        # Read the needed amount of binary data
        bardata = self.f.read(self.barsize)
        if not bardata:
            # if no data was read ... game over say "False"
            return False
        # use struct to unpack the data
        bdata = struct.unpack(self.barfmt, bardata)
        # Years are stored as if they had 500 days
        y, md = divmod(bdata[0], 500)
        # Months are stored as if they had 32 days
        m, d = divmod(md, 32)
        # put y, m, d in a datetime
        dt = datetime.datetime(y, m, d)
        if self.dtsize > 1:  # Minute Bars
            # Daily Time is stored in seconds
            hhmm, ss = divmod(bdata[1], 60)
            hh, mm = divmod(hhmm, 60)
            # add the time to the existing atetime
            dt = dt.replace(hour=hh, minute=mm, second=ss)
        self.lines.datetime[0] = date2num(dt)
        # Get the rest of the unpacked data
        o, h, l, c, v, oi = bdata[self.dtsize:]
        self.lines.open[0] = o
        self.lines.high[0] = h
        self.lines.low[0] = l
        self.lines.close[0] = c
        self.lines.volume[0] = v
        self.lines.openinterest[0] = oi
        # Say success
        return True

其他二进制格式

可以将相同的模型应用于任何其他二进制源:

  • 数据库
  • 分层数据存储
  • 在线来源

再次执行以下步骤:

  • __init__ -> 实例的任何初始化代码,仅一次
  • start -> 开始回测(如果将进行优化,则一次或多次)
    例如,这将打开到数据库的连接或到在线服务的套接字
  • stop -> 清理工作,如关闭数据库连接或打开的套接字
  • _load -> 查询数据库或在线源以获取下一组数据,并将其加载到对象的lines中。标准字段包括:datetime、open、high、low、close、volume、openinterest

VChartData 测试

VCharData 从本地“.fd”文件加载谷歌 2006 年的数据。

这只涉及加载数据,因此甚至不需要Strategy的子类。

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import datetime
import backtrader as bt
from vchart import VChartData
if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False)
    # Add a strategy
    cerebro.addstrategy(bt.Strategy)
    ###########################################################################
    # Note:
    # The goog.fd file belongs to VisualChart and cannot be distributed with
    # backtrader
    #
    # VisualChart can be downloaded from www.visualchart.com
    ###########################################################################
    # Create a Data Feed
    datapath = '../../datas/goog.fd'
    data = VChartData(
        dataname=datapath,
        fromdate=datetime.datetime(2006, 1, 1),
        todate=datetime.datetime(2006, 12, 31),
        timeframe=bt.TimeFrame.Days
    )
    # Add the Data Feed to Cerebro
    cerebro.adddata(data)
    # Run over everything
    cerebro.run()
    # Plot the result
    cerebro.plot(style='bar')

VChartData 完整代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import datetime
import struct
from backtrader.feed import DataBase
from backtrader import date2num
from backtrader import TimeFrame
class VChartData(DataBase):
    def __init__(self):
        super(VChartData, self).__init__()
        # Use the informative "timeframe" parameter to understand if the
        # code passed as "dataname" refers to an intraday or daily feed
        if self.p.timeframe >= TimeFrame.Days:
            self.barsize = 28
            self.dtsize = 1
            self.barfmt = 'IffffII'
        else:
            self.dtsize = 2
            self.barsize = 32
            self.barfmt = 'IIffffII'
    def start(self):
        # the feed must start ... get the file open (or see if it was open)
        self.f = None
        if hasattr(self.p.dataname, 'read'):
            # A file has been passed in (ex: from a GUI)
            self.f = self.p.dataname
        else:
            # Let an exception propagate
            self.f = open(self.p.dataname, 'rb')
    def stop(self):
        # Close the file if any
        if self.f is not None:
            self.f.close()
            self.f = None
    def _load(self):
        if self.f is None:
            # if no file ... no parsing
            return False
        # Read the needed amount of binary data
        bardata = self.f.read(self.barsize)
        if not bardata:
            # if no data was read ... game over say "False"
            return False
        # use struct to unpack the data
        bdata = struct.unpack(self.barfmt, bardata)
        # Years are stored as if they had 500 days
        y, md = divmod(bdata[0], 500)
        # Months are stored as if they had 32 days
        m, d = divmod(md, 32)
        # put y, m, d in a datetime
        dt = datetime.datetime(y, m, d)
        if self.dtsize > 1:  # Minute Bars
            # Daily Time is stored in seconds
            hhmm, ss = divmod(bdata[1], 60)
            hh, mm = divmod(hhmm, 60)
            # add the time to the existing atetime
            dt = dt.replace(hour=hh, minute=mm, second=ss)
        self.lines.datetime[0] = date2num(dt)
        # Get the rest of the unpacked data
        o, h, l, c, v, oi = bdata[self.dtsize:]
        self.lines.open[0] = o
        self.lines.high[0] = h
        self.lines.low[0] = l
        self.lines.close[0] = c
        self.lines.volume[0] = v
        self.lines.openinterest[0] = oi
        # Say success
        return True

数据 - 多时间框架

原文:www.backtrader.com/docu/data-multitimeframe/data-multitimeframe/

有时,投资决策是根据不同的时间框架进行的:

  • 每周评估趋势
  • 每日执行入场

或者 5 分钟对比 60 分钟。

这意味着需要在backtrader中组合多个时间框架的数据以支持这种组合。

平台已经内置了对此的本地支持。最终用户只需遵循这些规则:

  • 具有最小时间框架(因此具有更多柱状图)的数据必须是添加到 Cerebro 实例的第一个数据
  • 数据必须正确地对齐日期时间,以便平台能够理解它们的含义

此外,最终用户可以自由地在较短/较大的时间框架上应用指标。当然:

  • 应用于较大时间框架的指标将产生较少的柱状图

平台还将考虑以下内容

  • 较大时间框架的最小周期

可能会有最小周期的副作用,这可能导致在策略添加到 Cerebro 后需要消耗几个数量级的较小时间框架柱状图才能开始执行。

内置的cerebro.resample将用于创建较大的时间框架。

以下是一些示例,但首先是测试脚本的来源。

# Load the Data
    datapath = args.dataname or '../../datas/2006-day-001.txt'
    data = btfeeds.BacktraderCSVData(dataname=datapath)
    cerebro.adddata(data)  # First add the original data - smaller timeframe
    tframes = dict(daily=bt.TimeFrame.Days, weekly=bt.TimeFrame.Weeks,
                   monthly=bt.TimeFrame.Months)
    # Handy dictionary for the argument timeframe conversion
    # Resample the data
    if args.noresample:
        datapath = args.dataname2 or '../../datas/2006-week-001.txt'
        data2 = btfeeds.BacktraderCSVData(dataname=datapath)
        # And then the large timeframe
        cerebro.adddata(data2)
    else:
        cerebro.resampledata(data, timeframe=tframes[args.timeframe],
                             compression=args.compression)
    # Run over everything
    cerebro.run()

步骤:

  • 加载数据
  • 根据用户指定的参数重新采样
    该脚本还允许加载第二个数据
  • 将数据添加到 cerebro
  • 将重新采样的数据(更大的时间框架)添加到 cerebro
  • 运行

示例 1 - 每日和每周

脚本的调用:

$ ./multitimeframe-example.py --timeframe weekly --compression 1

输出图表:

示例 2 - 每日和每日压缩(2 根柱状图合并为 1 根)

脚本的调用:

$ ./multitimeframe-example.py --timeframe daily --compression 2

输出图表:

示例 3 - 带有 SMA 的策略

尽管绘图很好,但这里的关键问题是展示较大的时间框架如何影响系统,特别是当涉及到起始点时

该脚本可以使用--indicators来添加一个策略,该策略在较小和较大时间框架数据上创建周期为 10的简单移动平均线。

如果只考虑较小的时间框架:

  • next将在第 10 根柱状图之后首先被调用,这是简单移动平均线需要产生数值的时间
    注意:请记住,策略监视创建的指标,并且只有当所有指标都产生数值时才调用next。其理念是,最终用户已经添加了指标以在逻辑中使用它们,因此如果指标没有产生数值,则不应进行任何逻辑操作。

但在这种情况下,较大的时间框架(每周)会延迟调用next,直到每周数据上的简单移动平均线产生数值,这需要… 10 周。

该脚本覆盖了nextstart,它只被调用一次,默认调用next以显示第一次调用的时间。

调用 1:

只有较小的时间框架,每日,获得一个简单移动平均线

命令行和输出

$ ./multitimeframe-example.py --timeframe weekly --compression 1 --indicators --onlydaily
--------------------------------------------------
nextstart called with len 10
--------------------------------------------------

以及图表。

调用 2:

两个时间框架都有一个简单移动平均线

命令行:

$ ./multitimeframe-example.py --timeframe weekly --compression 1 --indicators
--------------------------------------------------
nextstart called with len 50
--------------------------------------------------
--------------------------------------------------
nextstart called with len 51
--------------------------------------------------
--------------------------------------------------
nextstart called with len 52
--------------------------------------------------
--------------------------------------------------
nextstart called with len 53
--------------------------------------------------
--------------------------------------------------
nextstart called with len 54
--------------------------------------------------

这里有两件事需要注意:

  • 策略在 50 个周期后而不是 10 个周期后首次调用。
    这是因为应用于较大(每周)时间框架的简单移动平均线在 10 周后产生一个值……那就是 10 周 * 5 天 / 周 …… 50 天
  • nextstart 被调用了 5 次,而不是只有 1 次。
    这是混合时间框架并且(在这种情况下仅有一个)指标应用于较大时间框架的自然副作用。
    较大时间框架的简单移动平均产生了 5 次相同的值,而同时消耗了 5 个每日的条形图。
    并且因为周期的开始由较大的时间框架控制,nextstart 被调用了 5 次。

以及图表。

结论

backtrader 中,可以使用多个时间框架的数据,无需特殊对象或调整:只需先添加较小的时间框架。

测试脚本。

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import argparse
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind
class SMAStrategy(bt.Strategy):
    params = (
        ('period', 10),
        ('onlydaily', False),
    )
    def __init__(self):
        self.sma_small_tf = btind.SMA(self.data, period=self.p.period)
        if not self.p.onlydaily:
            self.sma_large_tf = btind.SMA(self.data1, period=self.p.period)
    def nextstart(self):
        print('--------------------------------------------------')
        print('nextstart called with len', len(self))
        print('--------------------------------------------------')
        super(SMAStrategy, self).nextstart()
def runstrat():
    args = parse_args()
    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False)
    # Add a strategy
    if not args.indicators:
        cerebro.addstrategy(bt.Strategy)
    else:
        cerebro.addstrategy(
            SMAStrategy,
            # args for the strategy
            period=args.period,
            onlydaily=args.onlydaily,
        )
    # Load the Data
    datapath = args.dataname or '../../datas/2006-day-001.txt'
    data = btfeeds.BacktraderCSVData(dataname=datapath)
    cerebro.adddata(data)  # First add the original data - smaller timeframe
    tframes = dict(daily=bt.TimeFrame.Days, weekly=bt.TimeFrame.Weeks,
                   monthly=bt.TimeFrame.Months)
    # Handy dictionary for the argument timeframe conversion
    # Resample the data
    if args.noresample:
        datapath = args.dataname2 or '../../datas/2006-week-001.txt'
        data2 = btfeeds.BacktraderCSVData(dataname=datapath)
        # And then the large timeframe
        cerebro.adddata(data2)
    else:
        cerebro.resampledata(data, timeframe=tframes[args.timeframe],
                             compression=args.compression)
    # Run over everything
    cerebro.run()
    # Plot the result
    cerebro.plot(style='bar')
def parse_args():
    parser = argparse.ArgumentParser(
        description='Multitimeframe test')
    parser.add_argument('--dataname', default='', required=False,
                        help='File Data to Load')
    parser.add_argument('--dataname2', default='', required=False,
                        help='Larger timeframe file to load')
    parser.add_argument('--noresample', action='store_true',
                        help='Do not resample, rather load larger timeframe')
    parser.add_argument('--timeframe', default='weekly', required=False,
                        choices=['daily', 'weekly', 'monhtly'],
                        help='Timeframe to resample to')
    parser.add_argument('--compression', default=1, required=False, type=int,
                        help='Compress n bars into 1')
    parser.add_argument('--indicators', action='store_true',
                        help='Wether to apply Strategy with indicators')
    parser.add_argument('--onlydaily', action='store_true',
                        help='Indicator only to be applied to daily timeframe')
    parser.add_argument('--period', default=10, required=False, type=int,
                        help='Period to apply to indicator')
    return parser.parse_args()
if __name__ == '__main__':
    runstrat()

BackTrader 中文文档(三)(3)https://developer.aliyun.com/article/1489229

相关文章
|
6月前
|
存储 编解码 API
BackTrader 中文文档(四)(1)
BackTrader 中文文档(四)
70 1
|
6月前
|
Python
BackTrader 中文文档(六)(4)
BackTrader 中文文档(六)
83 0
|
6月前
|
存储
BackTrader 中文文档(四)(4)
BackTrader 中文文档(四)
44 0
|
6月前
|
Python
BackTrader 中文文档(一)(3)
BackTrader 中文文档(一)
71 0
|
6月前
BackTrader 中文文档(五)(2)
BackTrader 中文文档(五)
81 0
|
6月前
|
存储 算法 索引
BackTrader 中文文档(三)(1)
BackTrader 中文文档(三)
123 0
|
6月前
|
存储 数据可视化 机器人
BackTrader 中文文档(六)(1)
BackTrader 中文文档(六)
99 0
|
6月前
|
测试技术 索引 Python
BackTrader 中文文档(二)(2)
BackTrader 中文文档(二)
70 0
|
6月前
|
数据可视化
BackTrader 中文文档(三)(3)
BackTrader 中文文档(三)
54 0
|
6月前
|
存储 缓存 Shell
BackTrader 中文文档(二)(3)
BackTrader 中文文档(二)
131 0