【笔记】backtrader速成笔记,本人自用快速恢复启动,最全backtrader笔记,什么情况都有

        这篇文章写于自己,在长时间不复习后的快速启动,目前最全的教程,如果在哪里写的不满您心意了,请您私信告诉我,不要直接开始在评论区喷,谢谢您,请您关注我,到500粉丝,或者专栏破100订阅,后续会发免费且专业的视频和文字教程

正文

self.sma = SimpleMovingAverage(.....)

七个点

数据源、指标和策略都有*线*。

一条线是一系列点的连续性,这些点连接在一起形成线条。在谈论市场时,每天的数据源通常有以下一组点:

  • 开盘价、最高价、最低价、收盘价、成交量、持仓量、日期时间

平均线策略

self.sma = SimpleMovingAverage(.....)

        移动

av = self.sma[0]

        访问

previous_value = self.sma[-1]

实例化backtrader

import backtrader as bt

if __name__ == '__main__':

    cerebro = bt.Cerebro()
    cerebro.run()

设置资金

  cerebro.broker.setcash(100000.0)

如果不设置默认是10000.0

添加数据源

        数据获取    

bt.feed 的主要功能

  1. 数据加载:从外部数据源(如文件、API、数据库等)加载数据。

  2. 数据格式化:将加载的数据转换为 backtrader 所需的内部格式。

  3. 数据提供:在回测或实时交易过程中,按需提供数据点给策略。

        bt.feeds常用参数

  • bt.feeds.YahooFinanceData:从 Yahoo Finance 加载历史数据。

  • bt.feeds.PandasData:从 Pandas DataFrame 加载数据。

  • bt.feeds.GenericCSVData:从 CSV 文件加载数据。

  • bt.feeds.IBData:从 Interactive Brokers 加载实时数据。

        数据格式化

                基准数据HS300
hs300 = ak.index_zh_a_hist(symbol=benchmark_code, period='daily', start_date=start_date,
                           end_date=end_date) if bar == "daily" else ak.index_zh_a_hist_min_em(
    symbol=benchmark_code,
    period='1',
    start_date=start_date,
    end_date=end_date)
hs300 = hs300.iloc[:, :6]
hs300.columns = ['datetime', 'open', 'close', 'high', 'low', 'volume']
hs300.index = pd.to_datetime(hs300.datetime)
hs300['openinterest'] = 0
columns_to_keep = ['datetime', 'open', 'high', 'low', 'close', 'volume', 'openinterest']  # 转换为backtrader要求的数据格式
hs300 = hs300[columns_to_keep].copy()
hs300 = hs300.dropna()
hs300 = bt.feeds.PandasData(dataname=hs300)
                股票数据
data = ak.stock_zh_a_daily(symbol=symbol, adjust="qfq", start_date=start_date,
                           end_date=end_date) if bar == "daily" else ak.stock_zh_a_minute(symbol=symbol,
                                                                                          adjust="qfq")
data = data.set_index(pd.to_datetime(data['date'])) if bar == "daily" else data.set_index(
    pd.to_datetime(data['day']))
data = data.dropna()
# data['open'] = data['open'] * 100
data['close'] = data['close'] * 10
data = bt.feeds.PandasData(dataname=data)

        添加数据

        name用于指定股票数据

cerebro.adddata(data, name=symbol)
cerebro.adddata(hs300, name='000300')

DataSeries

        DataSeries ( Data Feeds 中的底层类)对象具有用于访问众所周知的OHLC(开盘价、最高价、最低价、收盘价)每日值的别名。这应该简化我们的打印逻辑的创建。

hs300['openinterest'] = 0 
columns_to_keep = ['datetime', 'open', 'high', 'low', 'close', 'volume', 'openinterest']

 这两行代码的作用是准备数据以符合 backtrader 框架的要求。以下是对这两行代码的详细解释:

hs300['openinterest'] = 0

这行代码的作用是为数据集 hs300 添加一个名为 openinterest 的列,并将其所有值初始化为 0。

  • openinterest:在金融数据中,openinterest 表示未平仓合约的数量。对于股票数据,通常没有未平仓合约的概念,因此这个字段通常设置为 0。backtrader 框架在处理数据时需要这个字段,即使它的值为 0。

columns_to_keep = ['datetime', 'open', 'high', 'low', 'close', 'volume', 'openinterest']

这行代码定义了一个列表 columns_to_keep,其中包含了 backtrader 框架所需的数据列名称。

  • datetime:时间戳,表示每个数据点的时间。

  • open:开盘价。

  • high:最高价。

  • low:最低价。

  • close:收盘价。

  • volume:成交量。

  • openinterest:未平仓合约数量,通常设置为 0。

self.datas[0].datetime 是一个特殊的字段,它通常包含时间戳数据。backtrader 提供了多种方式来访问和处理时间戳数据。

  • self.datas[0].datetime.date(0):返回当前时间戳的日期部分(datetime.date 对象)。

  • self.datas[0].datetime.datetime(0):返回当前时间戳的完整日期和时间(datetime.datetime 对象)。

  • self.datas[0].datetime.timestamp(0):返回当前时间戳的浮点数表示(从1970年1月1日开始的秒数)。

卖出操作

这个代码实现了,连续两天降价就买入的,上涨就卖出的操作,但是 只能支持一个订单

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

import datetime  # 用于处理日期时间对象
import os.path  # 用于管理路径
import sys  # 用于找出脚本名称(在argv[0]中)

# 导入backtrader平台
import backtrader as bt


# 创建一个策略
class TestStrategy(bt.Strategy):

    def log(self, txt, dt=None):
        ''' 本策略的日志记录函数'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def __init__(self):
        # 保留对数据序列中"收盘价"行的引用
        self.dataclose = self.datas[0].close

        # 跟踪挂起的订单
        self.order = None

    def notify_order(self, order):
        # 如果订单状态是已提交或已接受,无需操作
        if order.status in [order.Submitted, order.Accepted]:
            return

        # 检查订单是否已完成
        # 注意:如果资金不足,经纪人可能会拒绝订单
        if order.status in [order.Completed]:
            # 如果是买单,记录执行价格
            if order.isbuy():
                self.log('买入执行,%.2f' % order.executed.price)
            # 如果是卖单,记录执行价格
            elif order.issell():
                self.log('卖出执行,%.2f' % order.executed.price)

            # 记录执行订单时的条形图索引
            self.bar_executed = len(self)

        # 如果订单被取消、保证金不足或被拒绝,记录下来
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('订单取消/保证金不足/被拒绝')

        # 记录没有挂起的订单
        self.order = None

    def next(self):
        # 简单记录数据序列的收盘价
        self.log('收盘价, %.2f' % self.dataclose[0])

        # 检查是否有挂起的订单,如果有,不能发第二个订单
        if self.order:
            return

        # 检查是否已经在市场中
        if not self.position:

            # 还没有...如果满足条件我们可能会买入...
            if self.dataclose[0] < self.dataclose[-1]:
                # 当前收盘价低于前一天的收盘价

                if self.dataclose[-1] < self.dataclose[-2]:
                    # 前一天的收盘价低于前前一天的收盘价

                    # 买入,买入,买入!!!(使用默认参数)
                    self.log('创建买入订单, %.2f' % self.dataclose[0])

                    # 跟踪创建的订单以避免第二个订单
                    self.order = self.buy()

        else:

            # 已经在市场中...我们可能会卖出
            if len(self) >= (self.bar_executed + 5):
                # 卖出,卖出,卖出!!!(使用所有可能的默认参数)
                self.log('创建卖出订单, %.2f' % self.dataclose[0])

                # 跟踪创建的订单以避免第二个订单
                self.order = self.sell()


if __name__ == '__main__':
    # 创建一个Cerebro实体
    cerebro = bt.Cerebro()

    # 添加策略
    cerebro.addstrategy(TestStrategy)

    # 数据在示例的子文件夹中。需要找到脚本的位置
    # 因为它可能从任何地方被调用
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    datapath = os.path.join(modpath, '../../datas/orcl-1995-2014.txt')

    # 创建一个数据源
    data = bt.feeds.YahooFinanceCSVData(
        dataname=datapath,
        # 不要传递此日期之前的值
        fromdate=datetime.datetime(2000, 1, 1),
        # 不要传递此日期之后的值
        todate=datetime.datetime(2000, 12, 31),
        # 不要传递此日期之后的值
        reverse=False)

    # 将数据源添加到Cerebro
    cerebro.adddata(data)

    # 设置我们希望的初始现金
    cerebro.broker.setcash(100000.0)

    # 打印初始条件
    print('初始投资组合价值:%.2f' % cerebro.broker.getvalue())

    # 运行所有
    cerebro.run()

    # 打印最终结果
    print('最终投资组合价值:%.2f' % cerebro.broker.getvalue())

佣金的设置、

佣金设置之后,后续可以进行记录,并扣去用户金额

cerebro.broker.setcommission(commission=0.001) # 0.1% ...除以100去掉百分号

这个代码实现了如何设置佣金并打印出来

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

import datetime  # 用于处理日期时间对象
import os.path  # 用于管理路径
import sys  # 用于找出脚本名称(在argv[0]中)

# 导入backtrader平台
import backtrader as bt


# 创建一个策略
class TestStrategy(bt.Strategy):

    def log(self, txt, dt=None):
        ''' 本策略的日志记录函数'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def __init__(self):
        # 保留对数据序列中"收盘价"行的引用
        self.dataclose = self.datas[0].close

        # 跟踪挂起的订单、买入价格和手续费
        self.order = None
        self.buyprice = None
        self.buycomm = None

    def notify_order(self, order):
        # 如果订单状态是已提交或已接受,无需操作
        if order.status in [order.Submitted, order.Accepted]:
            return

        # 检查订单是否已完成
        # 注意:如果资金不足,经纪人可能会拒绝订单
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log('买入执行,价格:%.2f,成本:%.2f,手续费 %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:  # 卖出
                self.log('卖出执行,价格:%.2f,成本:%.2f,手续费 %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

            # 记录执行订单时的条形图索引
            self.bar_executed = len(self)

        # 如果订单被取消、保证金不足或被拒绝,记录下来
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('订单取消/保证金不足/被拒绝')

        # 记录没有挂起的订单
        self.order = None

    def notify_trade(self, trade):
        # 如果交易未关闭,则返回
        if not trade.isclosed:
            return

        # 记录交易的利润,毛利润和净利润
        self.log('操作利润,毛利润 %.2f,净利润 %.2f' %
                 (trade.pnl, trade.pnlcomm))

    def next(self):
        # 简单记录数据序列的收盘价
        self.log('收盘价, %.2f' % self.dataclose[0])

        # 检查是否有挂起的订单,如果有,不能发第二个订单
        if self.order:
            return

        # 检查是否已经在市场中
        if not self.position:

            # 还没有...如果满足条件我们可能会买入...
            if self.dataclose[0] < self.dataclose[-1]:
                # 当前收盘价低于前一天的收盘价

                if self.dataclose[-1] < self.dataclose[-2]:
                    # 前一天的收盘价低于前前一天的收盘价

                    # 买入,买入,买入!!!(使用默认参数)
                    self.log('创建买入订单, %.2f' % self.dataclose[0])

                    # 跟踪创建的订单以避免第二个订单
                    self.order = self.buy()

        else:

            # 已经在市场中...我们可能会卖出
            if len(self) >= (self.bar_executed + 5):
                # 卖出,卖出,卖出!!!(使用所有可能的默认参数)
                self.log('创建卖出订单, %.2f' % self.dataclose[0])

                # 跟踪创建的订单以避免第二个订单
                self.order = self.sell()


if __name__ == '__main__':
    # 创建一个Cerebro实体
    cerebro = bt.Cerebro()

    # 添加策略
    cerebro.addstrategy(TestStrategy)

    # 数据在示例的子文件夹中。需要找到脚本的位置
    # 因为它可能从任何地方被调用
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    datapath = os.path.join(modpath, '../../datas/orcl-1995-2014.txt')

    # 创建一个数据源
    data = bt.feeds.YahooFinanceCSVData(
        dataname=datapath,
        # 不要传递此日期之前的值
        fromdate=datetime.datetime(2000, 1, 1),
        # 不要传递此日期之后的值
        todate=datetime.datetime(2000, 12, 31),
        # 不要传递此日期之后的值

自定义策略参数

设置自定义策略中,需要接收的参数,这个参数需要手动传入,和function一样

params = (
(‘myparam’, 27), (‘exitbars’, 5),

)

在向cerebro中添加分析器,策略等时需要自己携带参数

# 添加策略
cerebro.addstrategy(TestStrategy, myparam=20, exitbars=7)

如果公司使用的是较老版本可以使用addsizer

cerebro.addsizer(bt.sizers.FixedSize, stake=10)`

展示策略传参

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

import datetime  # 用于处理日期时间对象
import os.path  # 用于管理路径
import sys  # 用于找出脚本名称(在argv[0]中)

# 导入backtrader平台
import backtrader as bt


# 创建一个策略
class TestStrategy(bt.Strategy):
    params = (
        ('exitbars', 5),  # 设置退出条形图的数量
    )

    def log(self, txt, dt=None):
        ''' 本策略的日志记录函数'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def __init__(self):
        # 保留对数据序列中"收盘价"行的引用
        self.dataclose = self.datas[0].close

        # 跟踪挂起的订单、买入价格和手续费
        self.order = None
        self.buyprice = None
        self.buycomm = None

    def notify_order(self, order):
        # 如果订单状态是已提交或已接受,无需操作
        if order.status in [order.Submitted, order.Accepted]:
            return

        # 检查订单是否已完成
        # 注意:如果资金不足,经纪人可能会拒绝订单
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log('买入执行,价格:%.2f,成本:%.2f,手续费 %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:  # 卖出
                self.log('卖出执行,价格:%.2f,成本:%.2f,手续费 %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

            # 记录执行订单时的条形图索引
            self.bar_executed = len(self)

        # 如果订单被取消、保证金不足或被拒绝,记录下来
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('订单取消/保证金不足/被拒绝')

        # 记录没有挂起的订单
        self.order = None

    def notify_trade(self, trade):
        # 如果交易未关闭,则返回
        if not trade.isclosed:
            return

        # 记录交易的利润,毛利润和净利润
        self.log('操作利润,毛利润 %.2f,净利润 %.2f' %
                 (trade.pnl, trade.pnlcomm))

    def next(self):
        # 简单记录数据序列的收盘价
        self.log('收盘价, %.2f' % self.dataclose[0])

        # 检查是否有挂起的订单,如果有,不能发第二个订单
        if self.order:
            return

        # 检查是否已经在市场中
        if not self.position:

            # 还没有...如果满足条件我们可能会买入...
            if self.dataclose[0] < self.dataclose[-1]:
                # 当前收盘价低于前一天的收盘价

                if self.dataclose[-1] < self.dataclose[-2]:
                    # 前一天的收盘价低于前前一天的收盘价

                    # 买入,买入,买入!!!(使用默认参数)
                    self.log('创建买入订单, %.2f' % self.dataclose[0])

                    # 跟踪创建的订单以避免第二个订单
                    self.order = self.buy()

        else:

            # 已经在市场中...我们可能会卖出
            if len(self) >= (self.bar_executed + self.params.exitbars):
                # 卖出,卖出,卖出!!!(使用所有可能的默认参数)
                self.log('创建卖出订单, %.2f' % self.dataclose[0])

                # 跟踪创建的订单以避免第二个订单
                self.order = self.sell()

if __name__ == '__main__':
    # 创建一个Cerebro实体
    cerebro = bt.Cerebro()

    # 添加策略
    cerebro.addstrategy(TestStrategy)

    # 数据在示例的子文件夹中。需要找到脚本的位置
    # 因为它可能从任何地方被调用
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    datapath = os.path.join(modpath, '../../datas/orcl-1995-2014.txt')

    # 创建一个数据源
    data = bt.feeds.YahooFinanceCSVData(
        dataname=datapath,
        # 不要传递此日期之前的值
        fromdate=datetime.datetime(2000, 1, 1),
        # 不要传递此日期之后的值

添加指标

  • 如果收盘价大于平均值,以市价“买入”

  • 如果处于市场中,则在收盘价小于平均值时“卖出”

self.sma = bt.indicators.MovingAverageSimple(self.datas[0], period=self.params.maperiod)

自动格式化小数

backtrader 提供的Yahoo数据源在应用调整后,将值四舍五入到小数点后两位。

最终绘图返回图像

安装绘图依赖matplotlib

pip install matplotlib

 运行绘图

cerebro.plot()

 需要画那条线就使用bt.indicators进行绘画

# 用于绘图显示的指标 
bt.indicators.ExponentialMovingAverage(self.datas[0], period=25) 

bt.indicators.WeightedMovingAverage(self.datas[0], period=25).subplot = True 

bt.indicators.StochasticSlow(self.datas[0]) 

bt.indicators.MACDHisto(self.datas[0]) 

rsi = bt.indicators.RSI(self.datas[0]) 

bt.indicators.SmoothedMovingAverage(rsi, period=10) 

bt.indicators.ATR(self.datas[0]).plot = False 

策略方法

        init

                用于在自定义策略中进行初始化操作

        start

                用于执行基础逻辑

        next

                用于在每个start后执行

        stop

                用于在数据耗尽时执行

一键导入backtrader依赖

        

import backtrader as bt 
import backtrader.indicators as btind 
import backtrader.feeds as btfeeds

DataSeries快捷访问

dataseries的快捷访问可以通过self.datas 数组的项目可以使用额外的自动成员变量直接访问:


- self.data 目标是 self.datas[0]

当计算时可以直接计算

sma = btind.SimpleMovingAverage(self.data, period=self.params.period)
class MyStrategy(bt.Strategy):
    params = dict(period=20)

    def __init__(self):

        sma = btind.SimpleMovingAverage(self.data, period=self.params.period)

也可以直接省略数据源,因为我们已经进行了添加

class MyStrategy(bt.Strategy):
    params = dict(period=20)

    def __init__(self):``sma = btind.SimpleMovingAverage(period=self.params.period)

所有的操作指标都可以操作

所有的内容都是可以直接操作的

比如:

class MyStrategy(bt.Strategy):
    params = dict(period1 = 20, period2 = 25, period3 = 10, period4)

    def __init__(self):
        sm1 = btind.SimpleMovingAverage(self.data,self.p.period1)
        sm2 = btind.SimpleMovingAverage(sm1,self.p.period2)
        Num = sum2 - sum1 + self.data.close
        boolNum = sum2 >= sum1

参数

  • 带有默认值的参数被声明为类属性(元组的元组或类似字典的对象)

  • 关键字参数( `` kwargs`` )会被扫描以寻找匹配的参数,如果找到则从 `` kwargs`` 中删除,并将值赋给对应的参数

  • 最后,可以通过访问成员变量 self.params (简写: self.p )来在类的实例中使用参数

Lines指标线

params = ((‘period’, 20),)

def __init__(self):
    # 获取简单移动平均线 
    sma = btind.SimpleMovingAverage(self.data, period=self.p.period)
    self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)

def next(self):
    if self.movav.lines.sma[0] > self.data.lines.close[0]:
    print(‘简单移动平均线大于收盘价’)

两个包含 lines 属性的对象已经暴露出来:

  • self.data 它有一个 lines 属性,并包含一个 close 属性

  • self.movav 是一个 SimpleMovingAverage 指标 它有一个 lines 属性,并包含一个 sma 属性

而且, lines ,即 close 和 sma 可以从点( 索引 0 )进行查询并比较值。

还存在一种简写访问方式:

  • xxx.lines 可以简写为 xxx.l

  • xxx.lines.name 可以简写为 xxx.lines_name

  • Strategies 和 Indicators 这类复杂对象提供了快速访问数据的 lines

`- ``self.data_name``提供了直接访问``self.data.lines.name``的方式 - 同样适用于编号数据变量: ``self.data1_name -> self.data1.lines.name

此外,可以直接访问线路名称:

  • 通过 self.data.close 和 self.movav.sma

但是,与前面的表示不同,这种表示方式无法清楚地表明是否正在访问 lines 。

传输线长度

传输线在执行过程中会动态增长,因此可以随时通过调用Python的标准 len 函数来测量其长度。

这适用于例如:

  • 数据源(Data Feeds)

  • 策略(Strategies)

  • 指示器(Indicators)

在数据被 预加载 的情况下,另外一个属性适用于 数据源(Data Feeds) :

  • 方法 buflen

该方法返回 数据源(Data Feed) 可用的实际柱数量。

len 和`buflen`之间的区别 - len 报告已处理的条的数量

  • buflen 报告数据源已加载的总条数

如果两者返回相同的值,那么要么没有预加载的数据,要么条的处理已经消耗了所有的预加载条(除非系统连接了实时数据源,否则这意味着处理结束)

Lines和Params的继承

一种”元语言” 用于支持 Params 和*Lines*的声明。已经尽力与Python的标准继承规则兼容。

继承应该按预期工作:

  • 支持多重继承

  • 继承基类的Params

  • 如果多个基类定义了相同的param,则使用继承列表中最后一个类的默认值

  • 如果在子类中重新定义了相同的param,则新的默认值将覆盖基类的默认值

  • 支持多重继承

  • 继承所有基类的Lines。如果在基类中多次使用了相同的名称,则只会有一个版本的线路

索引:0 和 -1 *** ** ** ** ** ** ** ** ** Lines*如前所述是线系列,它们由一系列的点组成,当这些点在时间轴上连接在一起时形成一条线(就像将所有收盘价沿时间轴连接在一起时)

要在常规代码中访问这些点,选择使用以 0 为基础的方法来获取/设置当前的 get/set 实例。

策略仅用于 获取*值。指标还可 设置*值。

从前面简单策略示例中可以简单看到 next 方法:

```python def next(self):

if self.movav.lines.sma[0] > self.data.lines.close[0]:

print(‘Simple Moving Average is greater than the closing price’)

```

该逻辑是通过应用索引 0 来获取移动平均线和当前收盘价的当前值。

注意

实际上,对于索引 0 和应用逻辑/算术运算符进行比较时,可以直接进行比较,如下所示:

```python if self.movav.lines.sma > self.data.lines.close:

``` 请看本文档后面的操作符解释。

设置被用于开发一个`指标`,因为指标需要通过设置当前输出值来进行计算。可以按照以下方式计算当前“get/set”点的简单移动平均值:

```python def next(self):

self.line[0] = math.fsum(self.data.get(0, size=self.p.period)) / self.p.period

```

访问前一个“set”点是根据Python对数组/可迭代对象的定义进行建模:

  • 它指向数组的最后一个元素

该平台认为在当前实时的“get/set”点之前,最后一个“set”项为 -1 。

因此,将当前的“close”与 前一个 “close”进行比较是一个 0 对 -1 的事情。例如,在策略中:

```python def next(self):

if self.data.close[0] > self.data.close[-1]:

print(‘今天的收盘价较高’)

```

当然,在逻辑上, -1 之前的“set”价格将以 -2,-3,... 进行访问。

切片 *** ** **

backtrader 不支持对 lines 对象进行切片,这是一个设计决策,遵循``[0]``和``[-1]``的索引方案。对于常规的可索引Python对象,您可以执行以下操作:```python

myslice = self.my_sma[0:] # 从开头到结尾取一个切片

但请记住,选择0意味着选择当前的值,之后没有值了。另外:

myslice = self.my_sma[0:-1] # 从开头到结尾减去1取一个切片

再次说明…0表示当前的值,-1表示最新(前一个)的值。这就是为什么从0到-1的切片在 backtrader 生态系统中没有意义。

如果将来支持切片,它可能会是这样的:

myslice = self.my_sma[:0] # 从当前点向前切片到开头

或者:

myslice = self.my_sma[-1:0] # 最后一个值和当前值

或者:

myslice = self.my_sma[-3:-1] # 从最后一个值向前切片到倒数第三个值 ```获取切片 =========

仍然可以获取具有最新值的数组。语法为:

myslice = self.my_sma.get(ago=0, size=1)  # 显示默认值

这将返回一个包含 1 个值( size=1 )的数组,以当前时刻 0 作为向后查找的起点。

要从当前时刻获取10个值(即最后10个值):

myslice = self.my_sma.get(size=10)  # 默认时刻为0

当然,数组的顺序是您期望的。最左侧的值是最旧的值,最右侧的值是最新的值(它是一个普通的Python数组,而不是一个 lines 对象)

要跳过仅当前时刻并获取最后10个值:

myslice = self.my_sma.get(ago=-1, size=10)

行:延迟索引 *** ** ** ** ** ** [] 操作符的语法用于在“next”逻辑阶段提取单个值。 Lines 对象还支持通过 延迟的Lines对象 来通过 __init__ 阶段访问值的附加表示法。

假设我们对逻辑感兴趣的是将之前的 close 值与 简单移动平均线 的实际值进行比较。可以在每个“next”迭代中手动执行此操作,也可以生成一个预先制作的’lines’对象:

class MyStrategy(bt.Strategy):

params = dict(period=20)

def __init__(self):

self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period) self.cmpval = self.data.close(-1) > self.sma

def next(self):

if self.cmpval[0]:

print(‘之前的收盘价高于移动平均线’)

在这里,使用了”(delay)”符号:

  • 这会生成一个 close 价格的副本,但是延迟了“-1”。

    而比较 self.data.close(-1) > self.sma 生成另一个 lines 对象,如果条件为 True ,则返回 1 ,如果为 False ,则返回 0 。

Lines耦合 **************操作符 () 可以像上面展示的那样与 delay 值一起使用,以提供 lines 对象的延迟版本。

如果使用语法时 未提供 delay 值,则返回一个 LinesCoupler lines 对象。它旨在在具有不同时间框架的 datas 之间建立耦合。

具有不同时间框架的数据源具有不同的 长度,在其上操作的指标会复制数据的长度。例如:

  • 每年的日数据源大约有250个柱状图

  • 每年的周数据源有52个柱状图

尝试创建一个操作(例如)来比较两个在上述数据上操作的 简单移动平均线 将会失败。不清楚如何将每日时间框架的250个柱状图与每周时间框架的52个柱状图配对。

读者可以想象背后进行了一种 date 比较,以找出一天 - 周的对应关系,但是:

  • Indicators 只是数学公式,不包含 datetime 信息

    它们对环境一无所知,只知道如果数据提供了足够的值,就可以进行计算。

空调用符 () 来拯救我们:class MyStrategy(bt.Strategy):

params = dict(period=20)

def __init__(self):

# data0是每日数据 sma0 = btind.SMA(self.data0, period=15) # 15日均线 # data1是每周数据 sma1 = btind.SMA(self.data1, period=5) # 5周均线

self.buysig = sma0 > sma1()

def next(self):

if self.buysig[0]:

print(‘每日均线大于每周均线1’)

这里,较大时间框架的指标 sma1 与每日时间框架通过 sma1() 耦合。这返回一个与 sma0 的较大数量的条形兼容的对象,并复制由 sma1 产生的值,从而将52周条形有效地分散在250日条中

运算符,使用自然结构 *** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **

为了实现“易用性”的目标,该平台允许(在Python的限制下)使用运算符。为了进一步增强此目标,使用运算符分为两个阶段。

阶段1 - 运算符创建对象

一个示例已经被看到,即使并非特意如此。在初始化阶段(__init__方法)中,像指标和策略这样的对象会由运算符创建可以进行操作、赋值或保留为后续在策略逻辑的评估阶段中使用的对象。再次给出 SimpleMovingAverage 的一个潜在实现,进一步分解成步骤。

SimpleMovingAverage 指标的 __init__ 方法中的代码可能如下所示:

```python

def __init__(self):

# 求 N 期值的和 - datasum 现在是一个 Lines 对象 # 当使用运算符 [] 和索引 0 查询时,返回当前和

datasum = btind.SumN(self.data, period=self.params.period)

# datasum (虽然是 Lines 对象,但只有一行) 可以被自然地除以 int/float # 就像这个例子。它实际上也可以被另一个 Lines 对象除以。 # 运算返回一个赋值给 “av” 的对象,再次使用 [0] 查询时返回当前瞬时平均值

av = datasum / self.params.period

# av Lines 对象可以被自然地赋值给该指标提供的命名行。 # 使用该指标的其他对象将直接访问该计算

self.line.sma = av

```

在 Strategy 的初始化期间展示了更完整的用例:

```python

class MyStrategy(bt.Strategy):

```def __init__(self):

sma = btind.SimpleMovingAverage(self.data, period=20)

close_over_sma = self.data.close > sma sma_dist_to_high = self.data.high - sma

sma_dist_small = sma_dist_to_high < 3.5

# 不幸的是,“and”不能在Python中重载, # 因为它是一种语言结构而不是运算符, # 因此平台必须提供一个函数来模拟它

sell_sig = bt.And(close_over_sma, sma_dist_small)

在上述操作完成后, sell_sig 是一个 Lines 对象, 可以在策略的逻辑中使用,指示条件是否满足。

第二阶段 - 自然的运算符

首先我们要记住,策略有一个 next 方法,在系统处理每个柱时调用这个方法。在第二阶段,运算符实际处于模式2中。基于前面的例子:

class MyStrategy(bt.Strategy):def __init__(self):

  self.sma = sma = btind.SimpleMovinAverage(self.data, period=20)

  close_over_sma = self.data.close > sma
  self.sma_dist_to_high = self.data.high - sma

  sma_dist_small = sma_dist_to_high < 3.5

  # 不幸的是,在 Python 中无法重写"and",因为它是一种语言结构而不是运算符,所以平台必须提供一个函数来模拟它

  self.sell_sig = bt.And(close_over_sma, sma_dist_small)

def next(self):

# 虽然这看起来不像是一个”运算符”实际上它确实是,因为该对象正在被测试以获得一个 True/False 的响应

if self.sma > 30.0:

print(‘sma 大于 30.0’)

if self.sma > self.data.close:

print(‘sma 高于收盘价’)```python

if self.sell_sig: # 如果 sell_sig 为 True,也可以写成:if sell_sig == True:

print(‘sell sig 为 True’)

else:

print(‘sell sig 为 False’)

if self.sma_dist_to_high > 5.0:

print(‘距离 sma 到高点的距离大于 5.0’)

这不是一个非常有用的策略,只是一个例子。在阶段 2 中,操作符返回预期的值(如果测试真值则返回布尔值,如果与浮点数进行比较则返回浮点数),并且算术运算也会返回预期的结果。

一些未覆盖的操作符/函数

Python 不允许覆盖所有内容,因此提供了一些函数来处理这些情况。

操作符:逻辑控制:

  • and -> And

  • or -> Or

逻辑控制:

  • if -> If

函数:

  • any -> Any

  • all -> All

  • cmp -> Cmp

  • max -> Max

  • min -> Min

  • sum -> Sum

    Sum 实际上使用 math.fsum 作为底层操作,因为该平台处理的是浮点数,而应用常规的 sum 可能会影响精度。

  • reduce -> Reduce

这些实用运算符/函数用于操作可迭代对象。可迭代对象的元素可以是常规的 Python 数值类型(整数、浮点数,…),也可以是具有 Lines 的对象。

以下是一个生成非常简单的购买信号的示例代码:

class MyStrategy(bt.Strategy):```python

def __init__(self):

sma1 = btind.SMA(self.data.close, period=15) self.buysig = bt.And(sma1 > self.data.close, sma1 > self.data.high)

def next(self):

if self.buysig[0]:

pass # 在这里做些什么

```

显然,如果 sma1 高于最高价,则它肯定比收盘价更高。但是,重点是展示了 bt.And 的用法。

使用 bt.If :

```python class MyStrategy(bt.Strategy):

def __init__(self):

sma1 = btind.SMA(self.data.close, period=15) high_or_low = bt.If(sma1 > self.data.close, self.data.low, self.data.high) sma2 = btind.SMA(high_or_low, period=15)

```

解析:

  • 对 data.close 进行 period=15 的 SMA 计算- 然后

    • 如果 sma 的值大于 close ,则返回 low ,否则返回 high

      请记住,当调用 bt.If 时,并不会返回实际值。它返回一个类似于 SimpleMovingAverage 的 Lines 对象。

      系统运行时将会计算这些值。

    • 生成的 bt.If Lines 对象随后被输送到第二个 SMA 中,该对象有时会使用 low 价格,有时会使用 high 价格进行计算。

这些 函数 也接受数值参数。下面是一个带有修改的示例:

``` python class MyStrategy(bt.Strategy):

def __init__(self):

sma1 = btind.SMA(self.data.close, period=15) high_or_30 = bt.If(sma1 > self.data.close, 30.0, self.data.high) sma2 = btind.SMA(high_or_30, period=15)

```

现在,第二个移动平均线根据 sma 和 close 的逻辑状态,选择使用 30.0 或者 high 价格进行计算。.. 注意::

值 30 在内部被转换为一个伪可迭代对象,该对象总是返回 30

详细解读

最小周期

在最小周期中我们使用next方法进行,什么叫做最小周期,就是在回测数据中,会以行的形式回执,那么最小周期就是每一行走完的迭代过程

迭代器的方法

  • next 方法

    它将在每次迭代时调用。 行迭代器 具有的将作为逻辑/计算基础的 datas 数组已经被平台移动到了下一个索引位置(除非进行了“数据回放”)。

    当满足 行迭代器 的 最小周期 时,将调用该方法。关于这一点稍后会详细解释。但是因为它们不是常规的迭代器,所以还有两个额外的方法:

  • prenext

    在`行迭代器``的 最小周期 满足之前调用。

  • nextstart

    当`行迭代器``的 最小周期 满足时,仅调用 一次 。

    默认行为是将调用转发给 next ,但如果需要的话,当然可以覆盖它。

迭代器的快捷方法

为了加快操作速度, 指示器 支持了一个批处理操作模式,被称为 runonce 。这并不是严格需要的(只需要一个 next 方法就足够了),但它大大缩短了时间。

runonce 方法规则无效化了使用索引为0的 get/set 位置,并依赖于对保存数据的底层数组进行直接访问,并传递给每个状态的正确索引。

下面定义的方法遵循了 next 家族的命名方式: - once(self, start, end)

当满足最小周期时调用。在内部数组的从开始到结束的位置之间必须进行处理,这些位置是从内部数组的起始位置开始计算的。

  • preonce(self, start, end)

    在满足最小周期之前调用。

  • oncestart(self, start, end)

    当满足最小周期时,仅调用一次。

    默认行为是将调用转发给 once ,但如果需要,可以覆盖。

图片胜过千言万语,在这种情况下可能还有一个例子。SimpleMovingAverage能够解释它:

class SimpleMovingAverage(指标):

lines = (‘sma’,) params = dict(period=20) def __init__(self):

def prenext(self):

print(‘prenext:: 当前周期:’, len(self))

def nextstart(self):

print(‘nextstart:: 当前周期:’, len(self)) # 模拟默认操作…调用next self.next()

def next(self):

print(‘next:: 当前周期:’, len(self))

实例化可以如下所示:

sma = btind.SimpleMovingAverage(self.data, period=25)

简要解释:

  • 假设传递给移动平均值的数据是标准数据源,其默认周期为 1 ,即数据源产生一根没有初始延迟的K线。

  • 然后, “period=25” 实例化的移动平均值将按以下方式调用其方法:

    • prenext 调用 24 次

    • nextstart 调用 1 次(随后调用 next )

    • next 调用 n 次,直到 数据源 耗尽为止让我们来使用杀手指标:在另一个简单移动平均线(SimpleMovingAverage)上计算一个简单移动平均线(SimpleMovingAverage)。实例化的代码如下:

```python sma1 = btind.SimpleMovingAverage(self.data, period=25)

sma2 = btind.SimpleMovingAverage(sma1, period=20) ```

现在发生的情况是:

  • 对于 sma1 ,与上述相同

  • sma2 接收了一个 数据源 ,其 最小周期 为25,即我们的 sma1

  • 执行了以下 SMA2 方法:

    • prenext 方法首先执行了25 + 18次,总共43次 - 前25次用于产生 sma1 的第一个可靠值 - 后18次用于累计额外的 sma1 值- 以19个值的总数进行调用(在第25次调用之后还有18次)。

  • nextstart 会被调用1次(依次调用 next )

  • next 会被调用n次,直到 数据流 用尽

当系统已经处理了44个条形图时,平台会调用 next 。

最小周期 已经自动调整为传入的`数据`。

策略和指标都遵循这种行为:

  • 只有当自动计算的最小周期达到之后,才会调用 next (除了首次调用 nextstart )

对于 runonce 批处理操作模式,对于 preonce , oncestart 和 once 也适用相同的规则。

最小周期 的行为可以被修改,尽管不建议这样做。如果希望修改,可以在策略或指标中使用 setminperiod(minperiod) 方法。

开始运行

启动和运行至少需要 3 个 Lines 对象:- 一个数据源

  • 一个策略(实际上是从策略派生的类)

  • 一个Cerebro(西班牙语中的“大脑”)

策略(派生)类

在继续之前,并为了更简化的方法,请检查文档中的*信号*部分,如果不希望子类化策略。使用本平台的目标是对数据进行回测,这是在策略(派生类)中完成的。

至少需要自定义的有两个方法:

  • __init__

  • next

在初始化过程中,对数据进行指标计算和其他计算的准备,以便后续应用逻辑。

接下来的方法会被调用来对每个数据条应用逻辑。

如果传递了不同时间框架的数据源(因此有不同的数据条数),则 next 方法将被调用用于主数据源(在cerebro中传递的第一个数据源),它必须是时间框架较小的数据。

如果使用了数据回放功能, next 方法将多次被调用,用于对同一数据条进行回放。

一个基本的策略派生类:

class MyStrategy(bt.Strategy):

def __init__(self):```python

self.sma = btind.SimpleMovingAverage(self.data, period=20)

def next(self):

if self.sma > self.data.close:

self.buy()

elif self.sma < self.data.close:

self.sell()

策略有其他可重写的方法(或挂钩点):

python class MyStrategy(bt.Strategy):

def __init__(self):

self.sma = btind.SimpleMovingAverage(self.data, period=20)

def next(self):

if self.sma > self.data.close:

submitted_order = self.buy()

elif self.sma < self.data.close:

submitted_order = self.sell()

def start(self):

print(‘即将开始回测’)

def stop(self):

print(‘回测已完成’)

def notify_order(self, order):

print(‘已接收到一个新的/更改的/执行的/撤销的订单’)

“start”和“stop”方法应该是不言自明的。按照打印函数中的文本,预计“notify_order”方法会在策略需要通知时被调用。使用案例:

  • 请求买入或卖出(如下所示)

    买入/卖出将返回一个“order”,该订单将被提交给经纪人。将提交的订单保留下来由调用者决定。

    例如,可用于确保在订单未决时不提交新订单。

  • 如果订单被接受/执行/取消/更改,经纪人将通过通知方法将状态更改(例如执行数量)发送回策略

  • buy / sell / close

    使用底层的 经纪人 和*大小配置器*向经纪人发送买入/卖出订单。

    也可以手动创建一个订单并将其传递给经纪人来完成相同的操作。但是,这个平台旨在让使用者能够轻松使用。

    close 会获取当前的市场仓位并立即平仓。

  • getposition (或属性 “position”)

    返回当前的市场仓位。

  • setsizer / getsizer (或属性 “sizer”)

    这些允许设置/获取底层的仓位大小配置器。可以通过不同的仓位大小配置器来对同一情况进行不同的配置(固定大小、与资本成比例、指数增长)。

python class MyStrategy(bt.Strategy):

params = ((‘period’, 20),)

def __init__(self):

self.sma = btind.SimpleMovingAverage(self.data, period=self.params.period)

注意如何不再用固定值20实例化 SimpleMovingAverage ,而是用已为策略定义的参数”period”来实例化。

Cerebro

一旦数据源可用并且策略已定义,Cerebro实例是将所有内容汇总并执行操作的关键。实例化一个很简单:

python cerebro = bt.Cerebro() ```如果没有特殊要求,缺省值会生效。

  • 创建了一个默认的经纪人

  • 操作没有佣金

  • 数据源将被预加载

  • 默认的执行模式是batch(批量操作),速度更快

    所有指标都必须支持 runonce 模式以获得最快的速度。平台中包含的指标都支持。

    自定义指标不需要实现runonce功能。 Cerebro 将模拟它,这意味着那些不兼容runonce的指标将运行得较慢。但系统的大部分操作仍将在批处理模式下运行。

由于已经有了数据源和策略(之前创建的),将它们组合并使其运行的标准方式是:

cerebro.adddata(data) cerebro.addstrategy(MyStrategy, period=25) cerebro.run()

请注意以下事项:

  • 数据源“实例”被添加进来

  • MyStrategy “类”与参数(kwargs)一起被添加进来,这些参数将被传递给它。

    MyStrategy的实例化将由cerebro在后台完成,并且 addstrategy 中的任何kwargs都将被传递给它。用户可以根据需要添加任意多的策略和数据源。策略之间如何协调通信(如果需要的话)并没有受到平台的限制。

当然,Cerebro提供了额外的可能性:

  • 决定预加载和操作模式:

    cerebro = bt.Cerebro(runonce=True, preload=True)
    

    这里有一个限制: runonce 需要预加载(否则无法运行批量操作)。当然,预加载数据源不强制要求使用 runonce 。

  • setbroker / getbroker (和 broker 属性)

    如果需要,可以设置自定义的经纪人。也可以访问实际的经纪人实例。

  • 绘图。在普通情况下非常简单:

    cerebro.run()
    cerebro.plot()
    

    plot 函数可以接受一些参数用于自定义。- numfigs=1

如果图太密集,可以将其分解为多个图。

  • plotter=None

    可以传递一个自定义的绘图实例,cerebro 不会自动初始化一个默认的实例。

  • **kwargs - 标准关键字参数

    这些参数将传递给绘图实例。

如上所述,Cerebro 接收一个派生自 Strategy 类的类(而不是实例)以及调用 “run” 时将传递给它的关键字参数。

这样做是为了实现优化。相同的 Strategy 类将根据需要实例化多次,并使用新的参数。如果一个实例被传递给 cerebro… 这是不可能的。如下所示,要求进行优化:

        cerebro.optstrategy(MyStrategy, period=xrange(10, 20)) 

optstrategy 方法和 addstrategy 具有相同的签名,但会执行额外的工作以确保优化如预期运行。 一个策略可以期望作为策略的一个普通参数的是一个 range ,而 addstrategy 不会对传递的参数进行任何假设。

另一方面, optstrategy 会理解可迭代对象是一组值,必须按照顺序传递给每个 Strategy 类的实例化。

需要注意的是,传递的是一组值而不是单个值。在这个简单的例子中,将尝试使用这个策略的 10 个值 10 -> 19(20 是上限)。

如果开发了一个具有额外参数的更复杂的策略,可以将它们全部传递给 optstrategy 。不需要进行优化的参数可以直接传递,而无需用户创建一个只包含一个值的假可迭代对象。例如:

` cerebro.optstrategy(MyStrategy, period=xrange(10, 20), factor=3.5) `

optstrategy 方法会创建 dummy iterable 来处理 factor(这是一个必需的),dummy iterable 只有一个元素(在这个例子中为 3.5)。

交互式 Python Shell 和某些类型的冻结可执行文件在 Windows 下对 Python 的 multiprocessing 模块存在问题。

Cerebro

这个类是 backtrader 的核心,因为它充当以下功能的中央点:

  1. 收集所有输入( 数据源 )、参与者( 策略)、观察者(* 观察器 )、分析器( 分析器 )和文档编写器( 编写器*), 确保节目在任何时刻都可以继续进行。

  2. 执行回测/实时数据供给/交易

  3. 返回结果

  4. 提供绘图功能

收集输入 

  1. 首先创建一个 cerebro 实例:

    cerebro = bt.Cerebro(**kwargs)

    可以使用一些 **kwargs 来控制执行情况,详情请参见参考文档 (同样的参数也可以稍后应用于 run 方法)1. 添加 数据源

最常见的模式是 cerebro.adddata(data) , 其中 data 是一个已经实例化的 数据源 。例如:

data = bt.BacktraderCSVData(dataname='mypath.days', timeframe=bt.TimeFrame.Days)
cerebro.adddata(data)

重新取样 和 回放 数据也可以采用相同的模式:

cerebro.addstrategy(MyStrategy, myparam1=value1, myparam2=value2)
  data = bt.BacktraderCSVData(dataname='mypath.min', timeframe=bt.TimeFrame.Minutes)
  cerebro.resampledata(data, timeframe=bt.TimeFrame.Days)

或者::

  data = bt.BacktraderCSVData(dataname='mypath.min', timeframe=bt.TimeFrame.Minutes)
  cerebro.replaydata(data, timeframe=bt.TimeFrame.Days)

系统可以接受任意数量的数据源,包括混合常规数据与重新取样和/或回放的数据。当然,其中的一些组合肯定是没有意义的,为了能够组合数据,有一个约束: *时间对齐* 。参见
:doc:`data-multitimeframe/data-multitimeframe`,
:doc:`data-resampling/data-resampling` - 重新取样 和
:doc:`data-replay/data-replay` 部分。

添加 策略

 与已经是类实例的 数据源 不同, cerebro 直接接受 策略 类和要传递给它的参数。其背后的原理是: 在优化方案中,类将被实例化多次,并传递不同的参数 。即使没有运行任何*优化*,该模式仍然适用:

cerebro.addstrategy(MyStrategy, myparam1=value1, myparam2=value2)

当进行 优化*时,参数必须作为可迭代对象添加。有关详细解释,请参见 优化*部分。基本模式如下:

cerebro.optstrategy(MyStrategy, myparam1=range(10, 20))

这将运行 MyStrategy 10次, myparam1 的值从10到19(请记住Python中的范围是半开区间,不会达到 20 )

 其他元素

还可以添加一些其他元素来增强回测体验。相关方法有:

  • addwriter

  • addanalyzer

  • addobserver (或 addobservermulti )4. 更改经纪人

Cerebro会使用 backtrader 中的默认经纪人,但是可以进行覆盖:

`python broker = MyBroker() cerebro.broker = broker  # 使用getbroker/setbroker方法的属性 `

 接收通知

如果 数据源 和/或 经纪人 发送通知(或创建通知的 存储提供者 ),可以通过 Cerebro.notify_store 方法接收到它们。有三种方法可以处理这些通知:

  • 通过 addnotifycallback(callback) 调用,在 cerebro 实例中添加一个*回调*。回调函数必须支持以下签名:

    `python callback(msg, *args, **kwargs) `

    实际接收到的 msg 、 `` args`` 和 ``**kwargs`` 是实现定义的(完全取决于 数据/经纪人/存储 ),但通常可以期望它们是 可打印*的,以便接收和实验。

  • 在添加到 cerebro 实例的 Strategy 子类中重写 notify_store 方法。

    签名: notify_store(self, msg, *args, **kwargs) - 子类化 Cerebro 并覆盖 notify_store (与 Strategy 中的签名相同)

    这应该是最不推荐的方法

执行回测

有一个单独的方法可以执行,但它支持多个选项(也可以在实例化时指定)以决定如何运行:

result = cerebro.run(**kwargs)

请参考下面的参考资料以了解可用的参数。

标准观察者

cerebro (除非另有说明)自动实例化*三个*标准 观察者

  • 一个 Broker 观察者,用于跟踪 cash 和 value (投资组合)

  • 一个 Trades 观察者,应该显示每个交易的效果如何

  • 一个 Buy/Sell 观察者,应该记录操作执行的时间点

回测结果分析

我们刚才有讲过

result = cerebro.run(**kwargs)

run 返回的 result 的格式会根据是否使用了 优化*(使用 ``optstrategy`` 添加了 策略*)而有所不同:

  • 所有使用 addstrategy 添加的策略

    result 将是一个在回测期间运行的实例列表

  • 使用 optstrategy 添加了 1 个或多个策略

    result 将是一个 list 的 list 。每个内部列表将包含每次优化运行之后的策略

优化 的默认行为已更改为仅返回系统中存在的* 分析器*,以减轻计算机核心之间的消息传递负担。

如果希望返回完整的策略集合作为返回值,请将参数 optreturn 设置为 False 。提供绘图功能

数据源参数讲解

在 backtrader 中,有一组数据源解析器(截止到撰写时,全部为基于CSV的),让您可以从不同的源加载数据。

  • Yahoo(在线或已经保存到文件中)

  • VisualChart(请参阅 www.visualchart.com <http://www.visualchart.com> _)

  • Backtrader CSV(自有的用于测试的格式)

  • 通用的CSV支持

我们刚才在讲述了数据来源的别名,接下来还有许多类似的参数

  • dataname (默认值: None) 必须提供

具体含义根据数据来源类型而变化(文件位置,代码标记,…) - name (默认值:’’)

用于在绘图中作为装饰目的。如果未指定,则可以从 dataname (例如文件路径的最后一部分)派生

  • fromdate (默认值:mindate)
  • 指示应忽略任何早于此日期时间的Python日期时间对象
  • todate (默认值:maxdate)
  • 指示应忽略任何晚于此日期时间的Python日期时间对象
  • timeframe (默认值:TimeFrame.Days)
  • 可能的值: Ticks , Seconds , Minutes , Days , Weeks , Months 和 Years
  • compression (默认值:1)
  • 每根K线实际包含的条形数。信息性。仅在数据重采样/重新播放时有效。 - sessionstart (默认值:无)
  • 数据的会话开始时间的指示。可以用于诸如重新取样之类的目的。
  • sessionend (默认值:无)
  • 数据的会话结束时间的指示。可以用于诸如重新取样之类的目的

CSV 数据源常见参数

参数(额外的通用参数):

  • headers (默认值:True)

    指示传入的数据是否有一个初始的标题行

  • separator (默认值:”,”)

    要考虑的分隔符,用于将每个 CSV 行进行标记化.. _generic-csv-datafeed:

GenericCSVData

这个类提供了一个通用接口,允许解析几乎所有的 CSV 文件格式。

根据参数定义的顺序和字段存在性解析 CSV 文件

特定参数(或特定含义):

  • dataname

    要解析的文件名或类似文件的对象

  • datetime (默认值:0)包含日期(或日期时间)字段的列

  • time (默认值:-1)包含时间字段的列,如果与日期时间字段分离(-1 表示不存在)

  • open (默认值:1), high (默认值:2), low (默认值:3), close (默认值:4), volume (默认值:5), openinterest (默认值:6) 含有对应字段的列索引

    如果传递了一个负值(例如:-1),表示该字段在CSV数据中不存在

  • nullvalue (默认值:float(‘NaN’))

    如果缺少应该存在的值(CSV字段为空),将使用的值

  • dtformat (默认值: %Y-%m-%d %H:%M:%S)

    用于解析日期时间CSV字段的格式

  • tmformat (默认值: %H:%M:%S)

    用于解析时间CSV字段的格式,如果“存在”(对于“时间”CSV字段,默认情况下应该不存在)

满足以下要求的示例用法:

  • 限制输入到2000年

  • 使用HLOC顺序而不是OHLC

  • 将缺失值替换为零(0.0)

  • 提供每天的数据条和日期时间仅为YYYY-MM-DD格式

  • 没有“openinterest”列存在The code:

import datetime import backtrader as bt import backtrader.feeds as btfeeds

data = btfeeds.GenericCSVData(

dataname=’mydata.csv’,

fromdate=datetime.datetime(2000, 1, 1), todate=datetime.datetime(2000, 12, 31),

nullvalue=0.0,

dtformat=(‘%Y-%m-%d’),

datetime=0, high=1, low=2, open=3, close=4, volume=5, openinterest=-1

)

…- 限制输入年份为2000

  • 使用HLOC顺序而不是OHLC顺序

  • 将缺失值替换为零(0.0)

  • 提供了逐笔回测数据,包含单独的日期和时间列 - 日期的格式为YYYY-MM-DD - 时间的格式为HH.MM.SS(不同于通常的HH:MM:SS)

  • 没有 openinterest 列存在

import datetime
import backtrader as bt
import backtrader.feeds as btfeed

...
...

data = btfeeds.GenericCSVData(
    dataname='mydata.csv',

    fromdate=datetime.datetime(2000, 1, 1),
    todate=datetime.datetime(2000, 12, 31),

    nullvalue=0.0,

    dtformat=('%Y-%m-%d'),
    tmformat=('%H.%M.%S'),

    datetime=0,
    time=1,
    high=2,
    low=3,
    open=4,
    close=5,
    volume=6,
    openinterest=-1
)

通过子类化也可以使这些设定 永久化 ::```python

import datetime import backtrader.feeds as btfeed

class MyHLOC(btfreeds.GenericCSVData):

params = (

(‘fromdate’, datetime.datetime(2000, 1, 1)), (‘todate’, datetime.datetime(2000, 12, 31)), (‘nullvalue’, 0.0), (‘dtformat’, (‘%Y-%m-%d’)), (‘tmformat’, (‘%H.%M.%S’)),

(‘datetime’, 0), (‘time’, 1), (‘high’, 2), (‘low’, 3), (‘open’, 4), (‘close’, 5), (‘volume’, 6), (‘openinterest’, -1)

)

现在可以通过提供 dataname 来重用这个新的类了:

data = btfeeds.MyHLOC(dataname='mydata.csv')

未完待续........

十天不更新就喊我退钱

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿雄不会写代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值