Backtrader数据同步

  |  

在最新版本中,次要编号已从 8 移至 9,以指示即使已考虑兼容性,也可能会对行为产生一些影响。

在 1.9.0.99 版中,使用 datetime 同步多个数据的整个机制已经重新设计(适用于下一个和一次模式)。

注意

所有标准测试用例都可以从nosetests中获得很好的OK,但是复杂的用例可能会发现未涵盖的角落案例。

之前的行为在工单 #39#76#115#129 中讨论过,这是弃用旧行为的基础。

现在,检查传入价格的日期时间时间戳以对齐数据并提供新功能(首先是旧柱)。好处:

  • 现在可以使用非时间对齐数据。

  • 在即时源中,行为得到改善,因为重新同步会自动

让我们回想一下,旧行为使用系统中引入的第 1 数据作为时间同步的主数据,没有其他数据可以更快。现在,在系统中引入数据的顺序不起作用。

部分返工解决了绘图问题,这天真地假设所有数据最终具有相同的长度,这是拥有时间母版的结果。新的绘图代码允许不同长度的数据。

注意

旧行为仍可通过使用:

cerebro = bt.Cerebro(oldsync=True)

或:

cerebro.run(oldsync=True)

用样品看

multidata-strategy 范例已用作范例的基础 multidata-strategy-unaligned (位于同一文件夹中)。已手动更改了两个数据样本以删除一些柱。两者都有 756 柱线,并且 753 被限制在两个不同的时间点

  • 2004年底、2005年初YHOO

  • 2005年底ORCL

与往常一样,一次运行胜过千言万语。

首先是旧行为

运行:

$ ./multidata-strategy-unaligned.py --oldsync --plot

从输出来看,重要的部分就在最后:

...
Self  len: 753
Data0 len: 753
Data1 len: 750
Data0 len == Data1 len: False
Data0 dt: 2005-12-27 23:59:59
Data1 dt: 2005-12-27 23:59:59
...

要注意:

  • 该策略的长度为753

  • 第1 数据(时间主站)也有 753

  • 第2 数据(时间从站)具有 750

从输出中看不出来,YHOO 但档包含的数据直到 2005-12-30,系统未对其进行处理。

可视化图表

新行为

运行:

$ ./multidata-strategy-unaligned.py --plot

从输出来看,重要的部分就在最后:

...
Self  len: 756
Data0 len: 753
Data1 len: 753
Data0 len == Data1 len: True
Data0 dt: 2005-12-27 23:59:59
Data1 dt: 2005-12-30 23:59:59
...

行为得到了改善:

  • 该策略将756 每个数据的长度转换为完整的 753 数据点。

  • 由于删除的数据点不重叠,因此策略最终成为3 比数据更长的单位。

  • 2005-12-30 已到达 data1 (它是删除 data0的数据点之一),因此所有数据都已处理到最后

可视化图表

虽然图表没有表现出重大差异,但它们实际上是在幕后不同的。

另一个检查

对于感兴趣的用户,data-multitimeframe 示例已更新为也支持 --oldsync 参数。由于现在正在绘制不同长度的数据,因此较大时间范围的视觉方面更好。

使用新的同步模型运行

使用旧的同步模型运行

示例用法

$ ./multidata-strategy-unaligned.py --help
usage: multidata-strategy-unaligned.py [-h] [--data0 DATA0] [--data1 DATA1]
                                       [--fromdate FROMDATE] [--todate TODATE]
                                       [--period PERIOD] [--cash CASH]
                                       [--runnext] [--nopreload] [--oldsync]
                                       [--commperc COMMPERC] [--stake STAKE]
                                       [--plot] [--numfigs NUMFIGS]

MultiData Strategy

optional arguments:
  -h, --help            show this help message and exit
  --data0 DATA0, -d0 DATA0
                        1st data into the system
  --data1 DATA1, -d1 DATA1
                        2nd data into the system
  --fromdate FROMDATE, -f FROMDATE
                        Starting date in YYYY-MM-DD format
  --todate TODATE, -t TODATE
                        Starting date in YYYY-MM-DD format
  --period PERIOD       Period to apply to the Simple Moving Average
  --cash CASH           Starting Cash
  --runnext             Use next by next instead of runonce
  --nopreload           Do not preload the data
  --oldsync             Use old data synchronization method
  --commperc COMMPERC   Percentage commission (0.005 is 0.5%
  --stake STAKE         Stake to apply in each operation
  --plot, -p            Plot the read data
  --numfigs NUMFIGS, -n NUMFIGS
                        Plot using numfigs figures

示例代码

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

import argparse
import datetime

# The above could be sent to an independent module
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind


class MultiDataStrategy(bt.Strategy):
    '''
    This strategy operates on 2 datas. The expectation is that the 2 datas are
    correlated and the 2nd data is used to generate signals on the 1st

      - Buy/Sell Operationss will be executed on the 1st data
      - The signals are generated using a Simple Moving Average on the 2nd data
        when the close price crosses upwwards/downwards

    The strategy is a long-only strategy
    '''
    params = dict(
        period=15,
        stake=10,
        printout=True,
    )

    def log(self, txt, dt=None):
        if self.p.printout:
            dt = dt or self.data.datetime[0]
            dt = bt.num2date(dt)
            print('%s, %s' % (dt.isoformat(), txt))

    def notify_order(self, order):
        if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
            return  # Await further notifications

        if order.status == order.Completed:
            if order.isbuy():
                buytxt = 'BUY COMPLETE, %.2f' % order.executed.price
                self.log(buytxt, order.executed.dt)
            else:
                selltxt = 'SELL COMPLETE, %.2f' % order.executed.price
                self.log(selltxt, order.executed.dt)

        elif order.status in [order.Expired, order.Canceled, order.Margin]:
            self.log('%s ,' % order.Status[order.status])
            pass  # Simply log

        # Allow new orders
        self.orderid = None

    def __init__(self):
        # To control operation entries
        self.orderid = None

        # Create SMA on 2nd data
        sma = btind.MovAv.SMA(self.data1, period=self.p.period)
        # Create a CrossOver Signal from close an moving average
        self.signal = btind.CrossOver(self.data1.close, sma)

    def next(self):
        if self.orderid:
            return  # if an order is active, no new orders are allowed

        if self.p.printout:
            print('Self  len:', len(self))
            print('Data0 len:', len(self.data0))
            print('Data1 len:', len(self.data1))
            print('Data0 len == Data1 len:',
                  len(self.data0) == len(self.data1))

            print('Data0 dt:', self.data0.datetime.datetime())
            print('Data1 dt:', self.data1.datetime.datetime())

        if not self.position:  # not yet in market
            if self.signal > 0.0:  # cross upwards
                self.log('BUY CREATE , %.2f' % self.data1.close[0])
                self.buy(size=self.p.stake)

        else:  # in the market
            if self.signal < 0.0:  # crosss downwards
                self.log('SELL CREATE , %.2f' % self.data1.close[0])
                self.sell(size=self.p.stake)

    def stop(self):
        print('==================================================')
        print('Starting Value - %.2f' % self.broker.startingcash)
        print('Ending   Value - %.2f' % self.broker.getvalue())
        print('==================================================')


def runstrategy():
    args = parse_args()

    # Create a cerebro
    cerebro = bt.Cerebro()

    # Get the dates from the args
    fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
    todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')

    # Create the 1st data
    data0 = btfeeds.YahooFinanceCSVData(
        dataname=args.data0,
        fromdate=fromdate,
        todate=todate)

    # Add the 1st data to cerebro
    cerebro.adddata(data0)

    # Create the 2nd data
    data1 = btfeeds.YahooFinanceCSVData(
        dataname=args.data1,
        fromdate=fromdate,
        todate=todate)

    # Add the 2nd data to cerebro
    cerebro.adddata(data1)

    # Add the strategy
    cerebro.addstrategy(MultiDataStrategy,
                        period=args.period,
                        stake=args.stake)

    # Add the commission - only stocks like a for each operation
    cerebro.broker.setcash(args.cash)

    # Add the commission - only stocks like a for each operation
    cerebro.broker.setcommission(commission=args.commperc)

    # And run it
    cerebro.run(runonce=not args.runnext,
                preload=not args.nopreload,
                oldsync=args.oldsync)

    # Plot if requested
    if args.plot:
        cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False)


def parse_args():
    parser = argparse.ArgumentParser(description='MultiData Strategy')

    parser.add_argument('--data0', '-d0',
                        default='../../datas/orcl-2003-2005.txt',
                        help='1st data into the system')

    parser.add_argument('--data1', '-d1',
                        default='../../datas/yhoo-2003-2005.txt',
                        help='2nd data into the system')

    parser.add_argument('--fromdate', '-f',
                        default='2003-01-01',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--todate', '-t',
                        default='2005-12-31',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--period', default=15, type=int,
                        help='Period to apply to the Simple Moving Average')

    parser.add_argument('--cash', default=100000, type=int,
                        help='Starting Cash')

    parser.add_argument('--runnext', action='store_true',
                        help='Use next by next instead of runonce')

    parser.add_argument('--nopreload', action='store_true',
                        help='Do not preload the data')

    parser.add_argument('--oldsync', action='store_true',
                        help='Use old data synchronization method')

    parser.add_argument('--commperc', default=0.005, type=float,
                        help='Percentage commission (0.005 is 0.5%%')

    parser.add_argument('--stake', default=10, type=int,
                        help='Stake to apply in each operation')

    parser.add_argument('--plot', '-p', action='store_true',
                        help='Plot the read data')

    parser.add_argument('--numfigs', '-n', default=1,
                        help='Plot using numfigs figures')

    return parser.parse_args()


if __name__ == '__main__':
    runstrategy()

推荐阅读

相关文章

Backtrader教程:观察者 - 参考

基准 backtrader类 .observers.基准() 此 observer 存储策略的回报和参考资产的回报,参考资产是传递到系统的数据之一。

BacktraderPython Hidden Powers 2

让我们进一步讨论一下Python的隐藏功能如何在 backtrader 中使用,以及如何实现它以尝试实现主要目标:易用性 这些定义是什么? 例如指针: import backtrader as bt class MyIndicator(bt.

Backtrader对逐笔报价数据重新采样

backtrader 已经可以从分钟数据中重新采样。接受价格变动数据不是问题,只需将 4 个常用字段(open、 high、 low、 close)设置为价格变动值。 但是传递要重新采样的逐笔报价数据再次生成相同的数据。作为或版本 1.1.11.88,情况已不再如此。

Backtrader教程:指针 - 开发

如果必须开发任何东西(除了一个或多个获胜策略之外),那么这个东西就是一个自定义指针。 根据作者的说法,平台内的这种开发很容易。 需要满足以下条件: 从指针派生的类(直接或从现有的子类派生) 定义它将保持lines 指针必须至少具有 1 line。

Backtrader教程:数据馈送 - 重新采样

如果数据仅在单个时间范围内可用,并且必须在不同的时间范围内进行分析,则是时候进行一些重新采样了。 “重采样”实际上应该称为“上采样”,因为一个人从源时间帧到更大的时间帧(例如:几天到几周) backtrader 内置支持通过筛选器对象传递原始数据,从而进行重采样。

Backtrader同步不同市场

使用次数越多, backtrader 必须面对的想法和意外场景的混合就越多。对于每个新平台,一个挑战是要看看平台是否能够达到开发开始时设置的期望,灵活性和易用性是目标,Python被选为基石。 工单#76 提出了一个问题,即是否可以完成具有不同交易日历的同步市场。

Backtrader迪克森移动平均线

下面的reddit帖子以自己的作者Nathan Dickson(reddit句柄)命名了这个平均值Dickson移动平均线。 在一次对reddit Algotrading 的定期访问中,我发现了一篇关于移动平均线的帖子,该移动平均线试图模仿Jurik移动平均线(又名JMA)。

Backtrader数据同步

在最新版本中,次要编号已从 8 移至 9,以指示即使已考虑兼容性,也可能会对行为产生一些影响。 在 1.9.0.99 版中,使用 datetime 同步多个数据的整个机制已经重新设计(适用于下一个和一次模式)。

Backtrader卡尔曼等

注意 对以下指令的支持从提交开始 发布1.9.30.x 将是包含它的第1个版本 。 backtrader的原始目标之一是成为纯python,即:仅使用标准发行版中可用的软件包。只有一个例外是matplotlib在没有重新发明轮子的情况下进行绘图。

Backtrader终极振荡器

backtrader开发启动时的目标之一是使开发新的指针变得非常容易(至少对作者本人而言),以在数学和视觉上测试想法。 门票#102 是关于将 UltimateOscillator 添加到 backtrader 注意 它将在下一个版本中添加,同时可以使用下面的代码使用它。 票证中所示的参考: 以及: 无需在这里重复。