BackTrader 中文文档(十一)

原文:www.backtrader.com/

基准测试

原文:www.backtrader.com/docu/observer-benchmark/benchmarking/

票号 #89 是关于添加对资产的基准测试的。理智的做法是,即使有一种策略,即使是正的,也低于简单跟踪资产将提供的内容。

backtrader 包含两种不同类型的对象,可以帮助进行跟踪:

  • 观察者

  • 分析器

分析器 领域已经有一个 TimeReturn 对象,用于跟踪整个投资组合价值的收益演变(即:包括现金)

这也显然可以是一个 观察者,因此在添加一些 基准测试 的同时,还进行了一些工作,以便能够将 观察者分析器 插接在一起,这些 观察者分析器 旨在跟踪相同的事物。

注意

观察者分析器 之间的主要区别是 观察者线性 特性,记录每个值,这使它们适用于

用于绘图和实时查询。当然,这会消耗内存。

分析器 另一方面通过 get_analysis 返回一组结果,实现可能不会在 运行 结束之前提供任何结果。

分析器 - 基准测试

标准的 TimeReturn 分析器已被扩展以支持跟踪 数据源。其中涉及的两个主要参数:

  • timeframe(默认值:None)如果是 None,那么将报告整个回测期间的完整回报

    传递 TimeFrame.NoTimeFrame 以考虑整个数据集,没有时间约束

  • data(默认值:None

    参考资产以跟踪,而不是投资组合价值。

    注意

    此数据必须已添加到一个 cerebro 实例中,使用 addataresampledatareplaydata

更多详细信息和参数:分析器参考

因此,可以这样跟踪投资组合的年度回报率

import backtrader as bt

cerebro = bt.Cerebro()
cerebro.addanalyzer(bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.Years)

...  # add datas, strategies ...

results = cerebro.run()
strat0 = results[0]

# If no name has been specified, the name is the class name lowercased
tret_analyzer = strat0.analyzers.getbyname('timereturn')
print(tret_analyzer.get_analysis())

如果我们想要跟踪 数据 的收益

import backtrader as bt

cerebro = bt.Cerebro()

data = bt.feeds.OneOfTheFeeds(dataname='abcde', ...)
cerebro.adddata(data)

cerebro.addanalyzer(bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.Years,
                    data=data)

...  # add strategies ...

results = cerebro.run()
strat0 = results[0]

# If no name has been specified, the name is the class name lowercased
tret_analyzer = strat0.analyzers.getbyname('timereturn')
print(tret_analyzer.get_analysis())

如果两者都要跟踪,则最好为 分析器 分配名称

import backtrader as bt

cerebro = bt.Cerebro()

data = bt.feeds.OneOfTheFeeds(dataname='abcde', ...)
cerebro.adddata(data)

cerebro.addanalyzer(bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.Years,
                    data=data, _name='datareturns')

cerebro.addanalyzer(bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.Years)
                    _name='timereturns')

...  # add strategies ...

results = cerebro.run()
strat0 = results[0]

# If no name has been specified, the name is the class name lowercased
tret_analyzer = strat0.analyzers.getbyname('timereturns')
print(tret_analyzer.get_analysis())
tdata_analyzer = strat0.analyzers.getbyname('datareturns')
print(tdata_analyzer.get_analysis())

观察者 - 基准测试

多亏了背景机制允许在 观察者 内使用 分析器,已经添加了两个新的观察者:

  • TimeReturn

  • Benchmark

两者都使用 bt.analyzers.TimeReturn 分析器来收集结果。

与上述的代码片段不同,一个完整的样本带有一些运行,以展示它们的功能。

观察 TimeReturn

执行:

$ ./observer-benchmark.py --plot --timereturn --timeframe notimeframe

image

注意执行选项:

  • --timereturn 告诉样本做到这一点

  • --timeframe notimeframe 告诉分析器考虑整个数据集,而不考虑时间范围边界。

最后绘制的值为 -0.26

  • 起始现金(从图表中清楚可见)为 50K 货币单位,策略最终以 36,970 货币单位结束,因此价值下降了 -26%

观察基准测试

因为基准测试也将显示时间回报结果,让我们运行相同的事情,但激活基准测试

$ ./observer-benchmark.py --plot --timeframe notimeframe

image

嘿,嘿,嘿!!!

  • 策略优于资产:-0.26 vs -0.33

    这不应该是一个值得庆祝的事情,但至少清楚的是策略甚至不如资产糟糕。

转到年度基础上跟踪事物:

$ ./observer-benchmark.py --plot --timeframe years

image

注意!

  • 策略最终值从-0.26略微变为-0.27

  • 另一方面,资产显示最终值为-0.35(而上文为-0.33

值如此接近的原因是,从 2005 年到 2006 年,无论是策略还是基准资产,几乎都是从 2005 年初开始的起始水平。

切换到较低的时间框架,如,整个情况就会改变:

$ ./observer-benchmark.py --plot --timeframe weeks

.. image:: 04-benchmarking-weeks.png

现在:

  • Benchmark 观察者显示更加紧张的情况。事情上上下下,因为现在对组合和数据的weekly回报进行跟踪

  • 因为在年末的最后一周没有交易活动,且资产几乎没有变动,所以上次显示的值为 0.00(上周最后一周收盘前的最后收盘价为25.54,示例数据收盘价为25.55,差异首次在第 4 个小数点处显现)

观察基准测试 - 另一个数据

示例允许针对不同的数据进行基准测试。默认情况下,使用--benchdata1时,以Oracle作为基准测试。考虑整个数据集时,使用--timeframe notimeframe

$ ./observer-benchmark.py --plot --timeframe notimeframe --benchdata1

image

现在清楚了为什么上面没有理由庆祝:

  • 策略的结果对于notimeframe没有变化,仍为-26%-0.26

  • 但是当针对另一个数据进行基准测试时,这个数据在同一时期内增长了+23%0.23

要么策略需要变化,要么最好交易另一个资产。

结论

现在有两种方式,使用相同的基础代码/计算,来跟踪TimeReturnBenchmark

  • 观察者TimeReturnBenchmark

  • 分析器(带有data参数的TimeReturnTimeReturn

当然,基准测试并不保证盈利,只是比较

示例的用法:

$ ./observer-benchmark.py --help
usage: observer-benchmark.py [-h] [--data0 DATA0] [--data1 DATA1]
                             [--benchdata1] [--fromdate FROMDATE]
                             [--todate TODATE] [--printout] [--cash CASH]
                             [--period PERIOD] [--stake STAKE] [--timereturn]
                             [--timeframe {months,days,notimeframe,years,None,weeks}]
                             [--plot [kwargs]]

Benchmark/TimeReturn Observers Sample

optional arguments:
  -h, --help            show this help message and exit
  --data0 DATA0         Data0 to be read in (default:
                        ../../datas/yhoo-1996-2015.txt)
  --data1 DATA1         Data1 to be read in (default:
                        ../../datas/orcl-1995-2014.txt)
  --benchdata1          Benchmark against data1 (default: False)
  --fromdate FROMDATE   Starting date in YYYY-MM-DD format (default:
                        2005-01-01)
  --todate TODATE       Ending date in YYYY-MM-DD format (default: 2006-12-31)
  --printout            Print data lines (default: False)
  --cash CASH           Cash to start with (default: 50000)
  --period PERIOD       Period for the crossover moving average (default: 30)
  --stake STAKE         Stake to apply for the buy operations (default: 1000)
  --timereturn          Use TimeReturn observer instead of Benchmark (default:
                        None)
  --timeframe {months,days,notimeframe,years,None,weeks}
                        TimeFrame to apply to the Observer (default: None)
  --plot [kwargs], -p [kwargs]
                        Plot the read data applying any kwargs passed For
                        example: --plot style="candle" (to plot candles)
                        (default: None)

代码

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

import argparse
import datetime
import random

import backtrader as bt

class St(bt.Strategy):
    params = (
        ('period', 10),
        ('printout', False),
        ('stake', 1000),
    )

    def __init__(self):
        sma = bt.indicators.SMA(self.data, period=self.p.period)
        self.crossover = bt.indicators.CrossOver(self.data, sma)

    def start(self):
        if self.p.printout:
            txtfields = list()
            txtfields.append('Len')
            txtfields.append('Datetime')
            txtfields.append('Open')
            txtfields.append('High')
            txtfields.append('Low')
            txtfields.append('Close')
            txtfields.append('Volume')
            txtfields.append('OpenInterest')
            print(','.join(txtfields))

    def next(self):
        if self.p.printout:
            # Print only 1st data ... is just a check that things are running
            txtfields = list()
            txtfields.append('%04d' % len(self))
            txtfields.append(self.data.datetime.datetime(0).isoformat())
            txtfields.append('%.2f' % self.data0.open[0])
            txtfields.append('%.2f' % self.data0.high[0])
            txtfields.append('%.2f' % self.data0.low[0])
            txtfields.append('%.2f' % self.data0.close[0])
            txtfields.append('%.2f' % self.data0.volume[0])
            txtfields.append('%.2f' % self.data0.openinterest[0])
            print(','.join(txtfields))

        if self.position:
            if self.crossover < 0.0:
                if self.p.printout:
                    print('CLOSE {} @%{}'.format(size,
                                                 self.data.close[0]))
                self.close()

        else:
            if self.crossover > 0.0:
                self.buy(size=self.p.stake)
                if self.p.printout:
                    print('BUY {} @%{}'.format(self.p.stake,
                                                self.data.close[0]))

TIMEFRAMES = {
    None: None,
    'days': bt.TimeFrame.Days,
    'weeks': bt.TimeFrame.Weeks,
    'months': bt.TimeFrame.Months,
    'years': bt.TimeFrame.Years,
    'notimeframe': bt.TimeFrame.NoTimeFrame,
}

def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()
    cerebro.broker.set_cash(args.cash)

    dkwargs = dict()
    if args.fromdate:
        fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
        dkwargs['fromdate'] = fromdate

    if args.todate:
        todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
        dkwargs['todate'] = todate

    data0 = bt.feeds.YahooFinanceCSVData(dataname=args.data0, **dkwargs)
    cerebro.adddata(data0, name='Data0')

    cerebro.addstrategy(St,
                        period=args.period,
                        stake=args.stake,
                        printout=args.printout)

    if args.timereturn:
        cerebro.addobserver(bt.observers.TimeReturn,
                            timeframe=TIMEFRAMES[args.timeframe])
    else:
        benchdata = data0
        if args.benchdata1:
            data1 = bt.feeds.YahooFinanceCSVData(dataname=args.data1, **dkwargs)
            cerebro.adddata(data1, name='Data1')
            benchdata = data1

        cerebro.addobserver(bt.observers.Benchmark,
                            data=benchdata,
                            timeframe=TIMEFRAMES[args.timeframe])

    cerebro.run()

    if args.plot:
        pkwargs = dict()
        if args.plot is not True:  # evals to True but is not True
            pkwargs = eval('dict(' + args.plot + ')')  # args were passed

        cerebro.plot(**pkwargs)

def parse_args(pargs=None):

    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Benchmark/TimeReturn Observers Sample')

    parser.add_argument('--data0', required=False,
                        default='../../datas/yhoo-1996-2015.txt',
                        help='Data0 to be read in')

    parser.add_argument('--data1', required=False,
                        default='../../datas/orcl-1995-2014.txt',
                        help='Data1 to be read in')

    parser.add_argument('--benchdata1', required=False, action='store_true',
                        help=('Benchmark against data1'))

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

    parser.add_argument('--todate', required=False,
                        default='2006-12-31',
                        help='Ending date in YYYY-MM-DD format')

    parser.add_argument('--printout', required=False, action='store_true',
                        help=('Print data lines'))

    parser.add_argument('--cash', required=False, action='store',
                        type=float, default=50000,
                        help=('Cash to start with'))

    parser.add_argument('--period', required=False, action='store',
                        type=int, default=30,
                        help=('Period for the crossover moving average'))

    parser.add_argument('--stake', required=False, action='store',
                        type=int, default=1000,
                        help=('Stake to apply for the buy operations'))

    parser.add_argument('--timereturn', required=False, action='store_true',
                        default=None,
                        help=('Use TimeReturn observer instead of Benchmark'))

    parser.add_argument('--timeframe', required=False, action='store',
                        default=None, choices=TIMEFRAMES.keys(),
                        help=('TimeFrame to apply to the Observer'))

    # Plot options
    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const=True,
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example:\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))

    if pargs:
        return parser.parse_args(pargs)

    return parser.parse_args()

if __name__ == '__main__':
    runstrat()

观察者参考

原文:www.backtrader.com/docu/observers-reference/

基准

类 backtrader.observers.Benchmark()

此观察者存储策略的回报和作为系统传递的数据之一的参考资产的回报

参数:

  • timeframe(默认:None)如果为None,则将报告整个回测期间的完整收益

  • compression(默认:None

    仅用于子日时间框架,例如通过指定“TimeFrame.Minutes”和 60 作为压缩来处理小时时间框架

  • data(默认:None

    用于跟踪的参考资产,以进行比较。

    注意:此数据必须已添加到 cerebro 实例中,并使用 addataresampledatareplaydata

  • _doprenext(默认:False

    基准将从策略启动的时间点开始进行(即:当策略的最小周期已满足时)。

    将此设置为True将记录从数据源的起始点开始的基准值。

  • firstopen(默认:False

    将其保持为False可确保价值与基准之间的第一个比较点从 0%开始,因为基准不会使用其开盘价。

    查看 TimeReturn 分析器参考以获取参数含义的全面解释

  • fund(默认:None

    如果为None,则经纪人的实际模式(fundmode - True/False)将被自动检测以决定收益是基于总净资产值还是基金价值。请参阅经纪人文档中的 set_fundmode

    将其设置为TrueFalse以获取特定行为

请记住,在run的任何时刻,可以通过查看lines的名称以索引0来检查当前值。

经纪人

类 backtrader.observers.Broker(*args, **kwargs)

此观察者在经纪人中跟踪当前现金金额和投资组合价值(包括现金)

参数:无

经纪人 - 现金

类 backtrader.observers.Cash(*args, **kwargs)

此观察者跟踪经纪人中当前现金金额

参数:无

经纪人 - 值

类 backtrader.observers.Value(*args, **kwargs)

此观察者在经纪人中跟踪当前投资组合价值,包括现金。

参数:

  • fund(默认:None

    如果为None,则经纪人的实际模式(fundmode - True/False)将被自动检测以决定收益是基于总净资产值还是基金价值。请参阅经纪人文档中的 set_fundmode

    将其设置为TrueFalse以获取特定行为

BuySell

类 backtrader.observers.BuySell(*args, **kwargs)

此观察者跟踪个别买入/卖出订单(单独执行),并将它们绘制在图表上,沿着执行价格水平附近的数据

参数:

* `barplot` (default: `False`) Plot buy signals below the minimum and
  sell signals above the maximum.

  If `False` it will plot on the average price of executions during a
  bar

* `bardist` (default: `0.015` 1.5%) Distance to max/min when
  `barplot` is `True

回撤

类 backtrader.observers.DrawDown()

此观察者跟踪当前回撤水平(绘制)和最大回撤(不绘制)水平

参数:

  • fund(默认:None

    如果为None,则会自动检测经纪人的实际模式(基金模式 - True/False),以决定收益是基于总净资产值还是基金价值。请参阅经纪人文档中的set_fundmode

    将其设置为TrueFalse以实现特定行为

时间收益

backtrader.observers.TimeReturn()

该观察器存储策略的收益

参数:

  • timeframe(默认:None)如果为None,则将报告整个回测期间的完整收益

    TimeFrame.NoTimeFrame传递以考虑整个数据集而没有时间约束

  • compression(默认:None

    仅用于子日时间框架,例如通过指定“TimeFrame.Minutes”和 60 作为压缩来处理小时时间框架

  • fund(默认:None

    如果为None,则会自动检测经纪人的实际模式(基金模式 - True/False),以决定收益是基于总净资产值还是基金价值。请参阅经纪人文档中的set_fundmode

    将其设置为TrueFalse以实现特定行为

请记住,在run的任何时刻,可以通过查看索引为0名称来检查当前值。

交易

backtrader.observers.Trades()

该观察器跟踪完整交易并绘制在关闭交易时实现的 PnL 水平。

当仓位从 0(或越过 0)变为 X 时,交易处于开放状态,然后在回到 0(或以相反方向越过 0)时关闭

参数:

* `pnlcomm` (def: `True`)

  Show net/profit and loss, i.e.: after commission. If set to `False`
  if will show the result of trades before commission

对数收益

backtrader.observers.LogReturns()

该观察器存储策略或 a 的对数收益

参数:

  • timeframe(默认:None)如果为None,则将报告整个回测期间的完整收益

    TimeFrame.NoTimeFrame传递以考虑整个数据集而没有时间约束

  • compression(默认:None

    仅用于子日时间框架,例如通过指定“TimeFrame.Minutes”和 60 作为压缩来处理小时时间框架

  • fund(默认:None

    如果为None,则会自动检测经纪人的实际模式(基金模式 - True/False),以决定收益是基于总净资产值还是基金价值。请参阅经纪人文档中的set_fundmode

    将其设置为TrueFalse以实现特定行为

请记住,在run的任何时刻,可以通过查看索引为0名称来检查当前值。

对数收益 2

backtrader.observers.LogReturns2()

扩展观察器 LogReturns 以显示两个工具

基金价值

backtrader.observers.FundValue(*args, **kwargs)

该观察器跟踪当前基金样式的值

参数:无

基金份额

backtrader.observers.FundShares(*args, **kwargs)

该观察器跟踪当前基金样式的份额

参数:无

Sizers

Sizers

原文:www.backtrader.com/docu/sizers/sizers/

  • 智能押注

Strategy提供了交易方法,即:buysellclose。让我们看看buy的签名:

def buy(self, data=None,
        size=None, price=None, plimit=None,
        exectype=None, valid=None, tradeid=0, **kwargs):

注意,如果调用者没有指定,size的默认值为None。这就是Sizers发挥重要作用的地方:

  • size=None请求Strategy向其Sizer请求实际押注

这显然意味着Strategies有一个Sizer:是的,确实!背景机制会为Strategy添加一个默认的sizer,如果用户没有添加一个。添加到strategy的默认SizerSizerFix。定义的初始行:

class SizerFix(SizerBase):
    params = (('stake', 1),)

很容易猜到这个Sizer只是使用1单位的stake买入/卖出(无论是股票、合约等)

使用Sizers

Cerebro

Sizers可以通过 2 种不同的方法通过Cerebro添加:

  • addsizer(sizercls, *args, **kwargs)

    添加一个将应用于添加到cerebro的任何策略的Sizer。这可以说是默认的Sizer。例如:

    cerebro = bt.Cerebro()
    cerebro.addsizer(bt.sizers.SizerFix, stake=20)  # default sizer for strategies` 
    
  • addsizer_byidx(idx, sizercls, *args, **kwargs)

    Sizer只会添加到由idx引用的Strategy

    这个idx可以从addstrategy的返回值中获取。就像这样:

    cerebro = bt.Cerebro()
    cerebro.addsizer(bt.sizers.SizerFix, stake=20)  # default sizer for strategies
    
    idx = cerebro.addstrategy(MyStrategy, myparam=myvalue)
    cerebro.addsizer_byidx(idx, bt.sizers.SizerFix, stake=5)
    
    cerebro.addstrategy(MyOtherStrategy)` 
    

    在这个例子中:

    • 系统中已经添加了一个默认的Sizer。这适用于所有没有分配特定Sizer的策略

    • 对于MyStrategy,在收集到其插入idx之后,会添加一个特定的调整sizer参数的sizer

    • 第二个策略,MyOtherStrategy,被添加到系统中。没有为其添加特定的Sizer

    • 这意味着:

      • MyStrategy最终将拥有一个内部特定的Sizer

      • MyOtherStrategy将获得默认的sizer

注意

default并不意味着策略共享一个单一的Sizer实例。每个strategy都会收到一个不同的默认sizer实例

要共享一个单一实例,要共享的sizer应该是一个单例类。如何定义一个超出了backtrader的范围

Strategy

Strategy类提供了一个 API:setsizergetsizer(以及一个property sizer)来管理Sizer。签名如下:

  • def setsizer(self, sizer): 接受一个已实例化的Sizer

  • def getsizer(self): 返回当前的Sizer实例

  • sizer是可以直接get/set的属性

在这种情况下,Sizer可以是:

  • 作为参数传递给策略

  • __init__期间使用Sizersetsizer属性设置,如:

    class MyStrategy(bt.Strategy):
        params = (('sizer', None),)
    
        def __init__(self):
            if self.p.sizer is not None:
                self.sizer = self.p.sizer` 
    

    例如,这将允许在与cerebro调用发生在同一级别的Sizer并将其作为参数传递给系统中的所有策略,从而有效地实现共享Sizer

Sizer开发

这样做很容易:

  1. backtrader.Sizer继承

    这使您可以访问self.strategyself.broker,尽管在大多数情况下不需要。可以通过broker访问的内容

    • 通过 self.strategy.getposition(data) 获取数据的持仓

    • 通过 self.broker.getvalue() 获取完整的投资组合价值

      请注意,当然也可以用 self.strategy.broker.getvalue() 来实现

    其他一些东西已经作为参数列在下面了

  2. 覆盖方法 _getsizing(self, comminfo, cash, data, isbuy)

    • comminfo: 包含有关数据委托的委员会信息的 CommissionInfo 实例,并允许计算持仓价值、操作成本和操作的委员会费用

    • cash: 经纪人当前可用的现金

    • data: 操作目标

    • isbuy: 对于 买入 操作将为 True,对于 卖出 操作将为 False

    该方法返回 买入/卖出 操作的期望 size

    返回的符号无关紧要,即:如果操作是 卖出 操作(isbuy 将为 False),则该方法可能返回 5-5卖出 操作仅使用绝对值。

    Sizer 已经去到 broker 并为给定的 data 请求了 委员会信息,实际的 现金 水平,并提供了对操作目标 data 的直接引用

让我们来看一下 FixedSize 尺寸器的定义:

import backtrader as bt

class FixedSize(bt.Sizer):
    params = (('stake', 1),)

    def _getsizing(self, comminfo, cash, data, isbuy):
        return self.params.stake

这很简单,因为 Sizer 不做任何计算,参数就在那里。

但是这种机制应该允许构建复杂的 大小(又称 定位)系统,在进入/退出市场时管理利害关系。

另一个例子:头寸翻转者

class FixedRerverser(bt.FixedSize):

    def _getsizing(self, comminfo, cash, data, isbuy):
        position = self.broker.getposition(data)
        size = self.p.stake * (1 + (position.size != 0))
        return size

这个构建在现有的 FixedSize 基础上,继承了 params 并覆盖了 _getsizing 以实现:

  • 通过属性 broker 获取 dataposition

  • 使用 position.size 来决定是否加倍固定赌注

  • 返回计算出的值

这将从 策略 上卸下决定是否反向或开仓的负担,Sizer 控制着,可以随时替换而不影响逻辑。

实际 Sizer 应用

在不考虑复杂的大小算法的情况下,可以使用两个不同的 Sizer 将策略从单向变为双向。只需在 cerebro 执行中更改 Sizer,策略就会改变行为。一个非常简单的 close 穿越 SMA 算法:

class CloseSMA(bt.Strategy):
    params = (('period', 15),)

    def __init__(self):
        sma = bt.indicators.SMA(self.data, period=self.p.period)
        self.crossover = bt.indicators.CrossOver(self.data, sma)

    def next(self):
        if self.crossover > 0:
            self.buy()

        elif self.crossover < 0:
            self.sell()

注意策略不考虑当前的 持仓(通过查看 self.position)来决定是否实际执行 买入卖出。只考虑 CrossOver信号Sizers 将负责一切。

这个尺寸器将在已经开仓的情况下仅在卖出时返回 非零 大小:

class LongOnly(bt.Sizer):
    params = (('stake', 1),)

    def _getsizing(self, comminfo, cash, data, isbuy):
      if isbuy:
          return self.p.stake

      # Sell situation
      position = self.broker.getposition(data)
      if not position.size:
          return 0  # do not sell if nothing is open

      return self.p.stake

将所有内容放在一起(并假设 backtrader 已经被导入并且一个 data 已经被添加到系统中):

...
cerebro.addstrategy(CloseSMA)
cerebro.addsizer(LongOnly)
...
cerebro.run()
...

图表(从源代码中包含的示例中获取)。

image

简单地将 Sizer 更改为上面显示的 FixedReverser 即可获得 长-短 版本:

...
cerebro.addstrategy(CloseSMA)
cerebro.addsizer(FixedReverser)
...
cerebro.run()
...

输出图表。

image

注意区别:

  • 交易数量已经翻了一番

  • 现金水平永远不会回到,因为策略总是处于市场中

两种方法都是负面的,但这只是一个例子。

bt.Sizer 参考

类 backtrader.Sizer()

这是Sizers的基类。任何sizer都应该是这个的子类,并覆盖_getsizing方法

成员属性:

  • strategy:将由工作在其中的调整器的策略设置

    提供了策略的整个 api 访问权限,例如如果在_getsizing中需要实际数据位置:

    position = self.strategy.getposition(data)` 
    
  • broker:将由工作在其中的 Sizer 的策略设置

    提供了一些复杂调整器可能需要的信息,如投资组合价值,…

_getsizing(comminfo, cash, data, isbuy)

这个方法必须被 Sizer 的子类覆盖,以提供大小调整功能

参数:

* `comminfo`: The CommissionInfo instance that contains
  information about the commission for the data and allows
  calculation of position value, operation cost, commision for the
  operation

* `cash`: current available cash in the *broker*

* `data`: target of the operation

* `isbuy`: will be `True` for *buy* operations and `False`
  for *sell* operations

该方法必须返回要执行的实际大小(一个整数)。如果返回0,则不会执行任何操作。

返回值的绝对值将被使用

调整器参考

www.backtrader.com/docu/sizers-reference/原文:

FixedSize

类 backtrader.sizers.FixedSize()

这个调整器只是为任何操作返回一个固定的大小。大小可以通过系统希望使用的分期数量来控制,方法是通过指定tranches参数来缩放到交易中。

参数:

* `stake` (default: `1`)

* `tranches` (default: `1`)

FixedReverser

类 backtrader.sizers.FixedReverser()

这个调整器返回需要的固定大小以反转开仓位置或开仓大小。

  • 开仓位置:返回参数stake

  • 反转仓位:返回 2 * stake

参数:

* `stake` (default: `1`)

PercentSizer

类 backtrader.sizers.PercentSizer()

这个调整器返回可用现金的百分比。

参数:

* `percents` (default: `20`)

AllInSizer

类 backtrader.sizers.AllInSizer()

这个调整器返回经纪人的所有可用现金。

参数:

* `percents` (default: `100`)

PercentSizerInt

类 backtrader.sizers.PercentSizerInt()

这个调整器以截断为整数的形式返回可用现金的百分比。

参数:

* `percents` (default: `20`)

AllInSizerInt

类 backtrader.sizers.AllInSizerInt()

这个调整器将经纪人的所有可用现金返回,并将大小截断为整数。

参数:

 * `percents` (default: `100`)

实时交易

实时数据源和实时交易

原文:www.backtrader.com/docu/live/live/

从版本 1.5.0 开始,backtrader 支持实时数据和实时交易。

  • 交互经纪商

  • 可视化图表

  • Oanda

交互式经纪人

原文:www.backtrader.com/docu/live/ib/ib/

与交互式经纪人的集成支持两种:

  • 实时数据提供

  • 实时交易

注意

尽管尝试测试尽可能多的错误条件和情况,但代码可能(像任何其他软件一样)包含错误。

在进入生产之前,彻底测试任何策略都使用模拟交易帐户或 TWS 演示

注意

使用IbPy模块进行与交互式经纪人的交互必须事先安装。在写作时,Pypi 中没有包,但可以使用以下命令使用pip进行安装:

pip install git+https://github.com/blampe/IbPy.git

如果您的系统中没有git可用(Windows 安装?),则以下内容也应该有效:

pip install https://github.com/blampe/IbPy/archive/master.zip

示例代码

源代码包含完整的示例:

  • samples/ibtest/ibtest.py

示例不能涵盖每种可能的用例,但它试图提供广泛的见解,并应该突出显示在使用回测模块或实时数据模块时没有真正的区别

有一件事可以确定:

  • 示例在进行任何交易活动之前等待data.LIVE数据状态通知。

    这可能是在任何实时策略中考虑的事情

商店模型 vs 直接模型

与交互式经纪人的交互支持通过 2 种模式:

  1. 商店模型(首选

  2. 与数据源类和经纪人类的直接交互

商店模型在创建经纪人数据源时提供了清晰的分离模式。两个代码片段应该更好地作为示例。

首先是Store模型:

import backtrader as bt

ibstore = bt.stores.IBStore(host='127.0.0.1', port=7496, clientId=35)
data = ibstore.getdata(dataname='EUR.USD-CASH-IDEALPRO')

这里是参数:

  • hostportclientId被传递到它们所属的地方IBStore,该地方使用这些参数打开连接。

然后使用getdata创建一个与backtrader中所有数据源共用的数据源。

  • dataname请求EUR/USD外汇对。

现在可以直接使用了:

import backtrader as bt

data = bt.feeds.IBData(dataname='EUR.USD-CASH-IDEALPRO',
                       host='127.0.0.1', port=7496, clientId=35)

这里:

  • 用于商店的参数被传递给数据。

  • 这些将用于在后台创建IBStore实例

缺点:

  • 清晰度大大降低,因为不清楚什么属于数据,什么属于商店。

IBStore - 商店

商店是实时数据源/交易支持的关键,提供了一个在IbPy模块和数据源和经纪人代理的需求之间的适应层。

商店是一个涵盖以下功能的概念:

  • 作为一个实体的中心商店:在这种情况下,实体是 IB。

    这可能需要或不需要参数

  • 提供通过该方法获取经纪人实例的访问权限:

    • IBStore.getbroker(*args, **kwargs)
  • 提供获取数据源实例的访问权限

    • IBStore.getdata(*args, **kwargs)

    在这种情况下,许多**kwargs与数据源相同,如datanamefromdatetodatesessionstartsessionendtimeframecompression

    数据可能提供其他参数。请查看下面的参考资料。

IBStore 提供:

  • 连接目标(hostport 参数)

  • 标识(clientId 参数)

  • 重新连接控制(reconnecttimeout 参数)

  • 时间偏移检查(timeoffset 参数,请参见下文)

  • 通知和调试

    notifyall(默认:False):在这种情况下,IB 发送的任何错误消息(许多只是信息性的)都将被转发给Cerebro/Strategy

    _debug(默认:False):在这种情况下,从 TWS 收到的每条消息都将打印到标准输出

IBData 数据源

数据选项

无论是直接还是通过 getdataIBData 数据源支持以下数据选项:

  • 历史下载请求

    如果持续时间超过 IB 对于给定时间框架/压缩组合施加的限制,这些将分成多个请求

  • 3 种实时数据

    • tickPrice 事件(通过 IB reqMktData

    用于CASH产品(至少 TWS API 9.70 的实验表明不支持其他类型)

    通过查看BID价格接收tick价格事件,根据非官方互联网文献,这似乎是跟踪CASH市场价格的方法。

    时间戳是在系统中本地生成的。如果用户希望,可以使用与 IB 服务器时间的偏移量(从 IB reqCurrentTime 计算)

    • tickString 事件(又名 RTVolume(通过 IB reqMktData))

    大约每 250 毫秒从 IB 接收一个OHLC/Volume快照(如果没有发生交易,则间隔可能更长)

    • RealTimeBars 事件(通过 IB reqRealTimeBars

    每 5 秒接收历史数据条(由 IB 固定持续时间)

    如果所选的时间框架/组合低于秒/5级别,此功能将自动禁用。

    !!! 注意

     `RealTimeBars` do not work with the TWS Demo` 
    

    默认行为是在大多数情况下使用:tickString,除非用户明确希望使用RealTimeBars

  • Backfilling

    除非用户要求只进行历史下载,否则数据源将自动进行回填:

    • 在开始时:使用最大可能的持续时间。例如:对于天/1时间框架/压缩)组合,IB 的最大默认持续时间是1 年,这是将进行回填的时间量

    • 在数据断开连接后:在这种情况下,用于回填操作的数据量将通过查看断开连接前接收的最新数据来减少。

注意

请注意,最终考虑的时间框架/压缩组合可能不是在数据源创建期间指定的,而是在系统中插入期间指定的。请参见以下示例:

data = ibstore.getdata(dataname='EUR.USD-CASH-IDEALPRO',
                       timeframe=bt.TimeFrame.Seconds, compression=5)

cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes, compression=2)

如现在应该清楚的是,最终考虑的时间框架/压缩组合是分钟/2

数据合同检查

在启动阶段,数据源将尝试下载指定合同的详细信息(查看如何指定的参考资料)。如果找不到这样的合同或找到多个匹配项,则数据将拒绝继续并将其通知系统。一些例子。

简单但明确的合同规范:

data = ibstore.getdata(dataname='TWTR')  # Twitter

只会找到一个实例(2016-06),因为对于默认类型STK、交易所SMART和货币(默认为空)的单一合同交易将被找到。

使用AAPL的类似方法会失败:

data = ibstore.getdata(dataname='AAPL')  # Error -> multiple contracts

因为SMART可以在几个真实交易所找到合同,并且AAPL在其中一些交易所以不同的货币交易。以下是可以的:

data = ibstore.getdata(dataname='AAPL-STK-SMART-USD')  # 1 contract found

数据通知

数据源将通过以下一种或多种方式报告当前状态(查看CerebroStrategy参考资料)

  • Cerebro.notify_data(如果重写)

  • 使用Cerebro.adddatacb添加回调

  • Strategy.notify_data(如果重写)

strategy内部的一个例子:

class IBStrategy(bt.Strategy):

    def notify_data(self, data, status, *args, **kwargs):

        if status == data.LIVE:  # the data has switched to live data
           # do something
           pass

系统发生更改后,将发送以下通知:

  • CONNECTED

    成功初始连接时发送

  • DISCONNECTED

    在这种情况下,不再能够检索数据,并且数据将指示系统无法执行任何操作。可能的条件:

    • 指定的合同有误

    • 在历史下载期间中断

    • 尝试重新连接到 TWS 的次数已超过限制

  • CONNBROKEN

    与 TWS 或数据中心的连接已断开。数据源将尝试(通过存储)重新连接和回溯填充,必要时,并恢复操作

  • NOTSUBSCRIBED

    合同和连接都正常,但由于权限不足,无法检索数据。

    数据将向系统指示无法检索数据

  • DELAYED

    表示正在进行历史/回溯操作,并且由策略处理的数据不是实时数据

  • LIVE

    表示从此处开始由strategy处理的数据是实时数据

策略的开发者应该考虑在发生断开连接或接收延迟数据等情况时要采取哪些行动。

数据时间框架和压缩

backtrader生态系统中的数据源,在创建时支持timeframecompression参数。这些参数也可以通过data._timeframedata._compression属性访问

timeframe/compression组合的重要性在将数据通过resampledatareplaydata传递给cerebro实例时具有特定目的,以便内部重新采样器/重播器对象了解预期目标是什么。当重新采样/重播时,._timeframe._compression将在数据中被覆盖。

但在另一方面,对于实时数据源,这些信息可能起重要作用。请参阅以下示例:

data = ibstore.getdata(dataname='EUR.USD-CASH-IDEALPRO',
                       timeframe=bt.TimeFrame.Ticks,
                       compression=1,  # 1 is the default
                       rtbar=True,  # use RealTimeBars
                      )
cerebro.adddata(data)

用户正在请求tick数据,这很重要,因为:

  • 不会进行回溯填充(IB 支持的最小单位是Seconds/1

  • 即使请求和支持datanameRealTimeBars,也不会使用,因为RealTimeBar的最小分辨率是Seconds/5

无论如何,除非使用Ticks/1的分辨率,否则数据必须进行重新采样/重播。上述情况下与实时条和工作:

data = ibstore.getdata(dataname='TWTR-STK-SMART', rtbar=True)
cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=20)

在这种情况下,并且如上所述,在resampledata期间将覆盖数据的._timeframe._compression属性。这是会发生的事情:

  • 将会发生回溯填充,请求分辨率为Seconds/20

  • RealTimeBars将用于实时数据,因为分辨率等于/大于Seconds/5且数据支持(不是CASH产品)

  • TWS 发送给系统的事件最多每 5 秒发生一次。这可能不重要,因为系统每 20 秒只向策略发送一个条。

没有RealTimeBars的情况下相同:

data = ibstore.getdata(dataname='TWTR-STK-SMART')
cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=20)

在这种情况下:

  • 将会发生回溯填充,请求分辨率为Seconds/20

  • tickString将用于实时数据,因为(不是CASH产品)

  • TWS 发送给系统的事件最多每 250 毫秒发生一次。这可能不重要,因为系统每 20 秒只向策略发送一个条。

最后,对于CASH产品和最多 20 秒:

data = ibstore.getdata(dataname='EUR.USD-CASH-IDEALPRO')
cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=20)

在这种情况下:

  • 将会发生回溯填充,请求分辨率为Seconds/20

  • tickPrice将用于实时数据,因为这是现金产品

    即使添加了rtbar=True

  • TWS 发送给系统的事件最多每 250 毫秒发生一次。这可能不重要,因为系统每 20 秒只向策略发送一个条。

时间管理

数据源将自动从TWS报告的ContractDetails对象中确定时区。

注意

这要求安装pytz。如果未安装,则用户应为数据源的tz参数提供与所需输出时区兼容的tzinfo实例

注意

如果安装了pytz并且用户认为自动时区确定不起作用,则tz参数可以包含一个时区名称的字符串。backtrader将尝试使用给定名称实例化一个pytz.timezone

报告的datetime将是与产品相关的时区的时间。一些示例:

  • 产品: 欧洲证券交易所的 EuroStoxxx 50(股票代码:ESTX50-YYYYMM-DTB)

    时区将是CET(中欧时间),又名Europe/Berlin

  • 产品: ES-Mini(股票代码:ES-YYYYMM-GLOBEX)

    时区将是EST5EDT,又名EST,又名US/Eastern

  • 产品: EUR.JPY 外汇对(股票代码EUR.JPY-CASH-IDEALPRO)

    时区将是EST5EDT,又名EST,又名US/Eastern

    实际上这是一个交互式经纪商的设置,因为外汇交易几乎连续 24 小时进行,因此对于它们不会有真正的时区。

这种行为确保交易保持一致,无论交易者的实际位置如何,因为计算机很可能具有实际位置的时区,而不是交易场所的时区。

请阅读手册的时间管理部分。

注意

TWS Demo 在没有数据下载权限的资产的时区报告方面并不准确(欧洲斯托克 50 期货就是这种情况的一个例子)

实时数据源和重采样/重播

关于何时为实时数据源交付条的设计决策是:

  • 尽可能实时地交付它们

这可能看起来很明显,对于Ticks的时间框架来说是这样,但如果重采样/重播起作用,延迟可能会发生。用例:

  • 重采样配置为Seconds/5,具有:

    cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=5)` 
    
  • 一个时间为23:05:27.325000的 tick 被交付

  • 在市场上交易速度很慢,下一个 tick 将在23:05:59.025000时到达

也许并不明显,但backtrader并不知道交易速度非常慢,下一个 tick 大约会在32秒后到来。如果没有适当的措施,一个时间为23:05:30.000000的重采样条可能会被延迟约29 秒

这就是为什么实时数据源每隔x秒(float值)唤醒一次重采样器/重播器并让它知道没有新数据输入。这是在创建实时数据源时通过参数qcheck(默认值:0.5秒)来控制的。

这意味着重采样器每隔qcheck秒就有机会交付一个条,如果本地时钟显示,重采样周期已经结束。这样一来,上述情景(23:05:30.000000)的重采样条最多会在报告时间后qcheck秒交付。

因为默认值是0.5,所以最晚时间是:23:05:30.500000。几乎比以前早了 29 秒。

缺点:

  • 有些 tick 可能会对已经交付的重采样/重播的条造成延迟

如果在交付后,TWS 从服务器收到一个时间戳为23:05:29.995\000的延迟消息,这对于已经向系统报告的时间23:05.30.000000`来说就太晚了

这通常发生在以下情况下:

  • timeoffsetIBStore\中被禁用(设置为False),IB报告的时间与本地时钟的时间差很大。

避免大部分延迟样本的最佳方法是:

  • 增加qcheck值,以考虑延迟消息:

    data = ibstore.getdata('TWTR', qcheck=2.0, ...)` 
    

这应该增加额外的空间,即使延迟了重采样/重播条的交付

注意

当然,对于Seconds/5的重采样来说,2.0 秒的延迟意义不同于Minutes/10的重采样

如果由于某种原因,最终用户希望禁用timeoffset并且不通过qcheck进行管理,则仍然可以接受延迟样本:

  • getdata / IBData的参数中设置_latethroughTrue

    data = ibstore.getdata('TWTR', _latethrough=True, ...)` 
    
  • 重采样/重播时设置takelateTrue

    cerebro.resampledata(data, takelate=True)` 
    

IBBroker - 实时交易

注意

backtrader中的经纪人模拟中实现了tradeid功能。这允许正确地跟踪在同一资产上并行执行的交易,并将佣金正确地分配给适当的tradeid

此概念在此实时经纪人中不受支持,因为佣金是由经纪人报告的,而在某些情况下,不可能将其分离为不同的tradeid值。

tradeid仍然可以指定,但不再有意义。

使用经纪人

要使用IB Broker,必须替换由cerebro创建的标准经纪人模拟实例。

使用Store模型(首选):

import backtrader as bt

cerebro = bt.Cerebro()
ibstore = bt.stores.IBStore(host='127.0.0.1', port=7496, clientId=35)
cerebro.broker = ibstore.getbroker()  # or cerebro.setbroker(...)

使用直接方法:

import backtrader as bt

cerebro = bt.Cerebro()
cerebro.broker = bt.brokers.IBBroker(host='127.0.0.1', port=7496, clientId=35)

经纪人参数

不管是直接还是通过getbrokerIBBroker经纪人都不支持任何参数。这是因为经纪人只是一个真实经纪人的代理。真实经纪人给予的,不应被剥夺。

一些限制

现金和价值报告

当内部的backtrader经纪人模拟在调用策略的next方法之前对value(净清算价值)和cash进行计算时,无法保证与实时经纪人相同。

  • 如果请求了值,则可能会延迟next的执行,直到回答到达

  • 经纪人可能尚未计算出这些值

backtrader告诉 TWS 在它们更改时提供更新的值(backtrader订阅accounUpdate消息),但它不知道消息何时到达。

IBBrokergetcashgetvalue方法报告的值始终是从 IB 接收到的最新值。

注意

进一步的限制是,即使有更多货币的值可用,这些值也以帐户的基本货币报告。这是一个设计选择。

位置

backtrader使用 TWS 报告的资产的Position(价格和大小)。在order executionorder status消息后,内部计算可以被使用,但是如果其中一些消息被错过(套接字有时会丢失数据包),则计算将无法进行。

当然,如果连接到 TWS 时将执行交易的资产已经有一个开放的头寸,由于初始偏移,策略所做的Trades的计算将不会像通常一样工作

与其进行交易

就标准用法而言,没有变化。只需使用策略中可用的方法(有关完整说明,请参阅Strategy参考资料)

  • buy

  • sell

  • close

  • cancel

返回的订单对象

  • 与 backtrader 的Order对象兼容(在同一层次结构中的子类)

订单执行类型

IB 支持各种执行类型,其中一些由 IB 模拟,一些由交易所本身支持。最初支持哪些订单执行类型的决定有一个动机:

  • backtrader中可用的经纪人模拟兼容性

    这是因为经过回测的内容将会投入生产。

因此,订单执行类型仅限于broker simulation中可用的类型:

  • Order.Market

  • Order.Close

  • Order.Limit

  • Order.Stop(当Stop触发时,Market订单跟随)

  • Order.StopLimit(当Stop触发时,Limit订单跟随)

注意

停止触发是根据不同的策略由 IB 执行的。backtrader不修改默认设置,即为0

0 - the default value. The "double bid/ask" method will be used for
orders for OTC stocks and US options. All other orders will use the
"last" method.

如果用户希望修改此项,可以根据 IB 文档提供的额外**kwargsbuysell提供。例如,在策略的next方法中:

def next(self):
    # some logic before
    self.buy(data, m_triggerMethod=2)

这已更改策略为2“last”方法,其中停止订单基于最后价格触发

请参阅 IB API 文档以获取有关停止触发的进一步澄清

订单有效期

在回测期间可用的相同有效性概念(使用validbuysell)也可用,并具有相同的含义。因此,对于以下IB 订单valid参数翻译如下:

  • None -> GTC(Good Til Cancelled)

    因为没有指定有效期,所以理解为订单必须有效直到取消

  • datetime/date翻译为GTD(Good Til Date)

    传递datetime.datetime/datetime.date实例表示订单必须有效直到给定时间点。

  • timedelta(x)翻译为GTD(这里timedelta(x) != timedelta()

    这被解释为指示订单从now + timedelta(x)开始有效

  • float翻译为GTD

    如果该值来自backtrader使用的原始float日期时间存储,则订单必须有效直到由该float指示的日期时间

  • timedelta() or 0翻译为DAY

    已有一个值(而不是None),但是为空,被解释为当前day(session)有效的订单

通知

标准的Order状态将通过方法notify_order(如果已重写)通知给strategy

  • Submitted - 订单已发送到 TWS

  • Accepted - 订单已下达

  • Rejected - 订单放置失败或在其生命周期内被系统取消

  • Partial - 已经部分执行

  • Completed - 订单已完全执行

  • Canceled(或Cancelled

    这在 IB 下有几个意思:

    • 手动用户取消

    • 服务器/交易所取消了订单

    • 订单有效期已过期

      将应用启发式方法,如果已从 TWS 接收到带有orderState指示为PendingCancelCanceledopenOrder消息,则订单将被标记为已过期

  • 已过期 - 请参阅上文的解释

参考

IBStore

类 backtrader.stores.IBStore()

封装一个 ibpy ibConnection 实例的单例类。

参数也可以在使用此存储的类中指定,如IBDataIBBroker

参数:

  • host(默认:127.0.0.1):IB TWS 或 IB Gateway 实际运行的位置。尽管这通常是本地主机,但不应该是

  • port(默认值:7496):连接的端口。演示系统使用7497

  • clientId(默认值:None):要用于连接到 TWS 的客户端 ID。

    None:生成 1 到 65535 之间的随机 ID。一个整数:将作为要使用的值传递。

  • notifyall(默认值:False

    如果为False,则只会将error消息发送到CerebroStrategynotify_store方法。

    如果为True,则会通知从 TWS 接收到的每条消息

  • _debug(默认值:False

    打印从 TWS 接收到的所有消息到标准输出

  • reconnect(默认值:3

    在第 1 次连接尝试失败后,尝试重新连接的次数。

    将其设置为-1值以永远保持重新连接

  • timeout(默认值:3.0

    重新连接尝试之间的秒数

  • timeoffset(默认值:True

    如果为 True,则将从reqCurrentTime(IB 服务器时间)获得的时间用于计算到本地时间的偏移量,并且此偏移量将用于价格通知(例如用于 CASH 市场的 tickPrice 事件)以修改本地计算的时间戳。

    时间偏移将传播到backtrader生态系统的其他部分,例如重新采样,以使用计算出的偏移量对齐重新采样时间戳。

  • timerefresh(默认值:60.0

    秒数:时间偏移量必须刷新的频率

  • indcash(默认值:True

    将 IND 代码视为现金进行价格检索

IBBroker

backtrader.brokers.IBBroker(**kwargs)

用于 Interactive Brokers 的经纪实现。

此类将 Interactive Brokers 的订单/持仓映射到backtrader的内部 API。

注意

  • 实际上不支持tradeid,因为利润和损失直接来自 IB。因为(如预期的那样)以 FIFO 方式计算,所以对于tradeid,利润和损失并不准确。

  • 仓位

如果在操作开始时有资产的持仓或通过其他方式给出的订单改变了持仓,那么在cerebro中计算的交易将不反映现实。

为了避免这种情况,该经纪商将不得不进行自己的持仓管理,这也将允许使用多个 ID 进行交易(利润和损失也将在本地计算),但可能被认为是与实时经纪商合作的目的相悖。

IBData

backtrader.feeds.IBData(**kwargs)

Interactive Brokers 数据源。

支持参数dataname中的以下合同规格:

  • TICKER # 股票类型和 SMART 交易所

  • TICKER-STK # 股票和 SMART 交易所

  • TICKER-STK-EXCHANGE # 股票

  • TICKER-STK-EXCHANGE-CURRENCY # 股票

  • TICKER-CFD # 差价合约和 SMART 交易所

  • TICKER-CFD-EXCHANGE # 差价合约

  • TICKER-CDF-EXCHANGE-CURRENCY # 股票

  • TICKER-IND-EXCHANGE # 指数

  • TICKER-IND-EXCHANGE-CURRENCY # 指数

  • TICKER-YYYYMM-EXCHANGE # 期货

  • TICKER-YYYYMM-EXCHANGE-CURRENCY # 期货

  • TICKER-YYYYMM-EXCHANGE-CURRENCY-MULT # 期货

  • TICKER-FUT-EXCHANGE-CURRENCY-YYYYMM-MULT # 期货

  • TICKER-YYYYMM-EXCHANGE-CURRENCY-STRIKE-RIGHT # 期权

  • TICKER-YYYYMM-EXCHANGE-CURRENCY-STRIKE-RIGHT-MULT # 期权

  • TICKER-FOP-EXCHANGE-CURRENCY-YYYYMM-STRIKE-RIGHT # 期权组合

  • TICKER-FOP-EXCHANGE-CURRENCY-YYYYMM-STRIKE-RIGHT-MULT # 期权组合

  • CUR1.CUR2-CASH-IDEALPRO # 外汇

  • TICKER-YYYYMMDD-EXCHANGE-CURRENCY-STRIKE-RIGHT # 期权

  • TICKER-YYYYMMDD-EXCHANGE-CURRENCY-STRIKE-RIGHT-MULT # 期权

  • TICKER-OPT-EXCHANGE-CURRENCY-YYYYMMDD-STRIKE-RIGHT # 期权

  • TICKER-OPT-EXCHANGE-CURRENCY-YYYYMMDD-STRIKE-RIGHT-MULT # 期权

Params:

  • sectype(默认:STK

    如果在dataname规范中未提供证券类型,则应用的默认值

  • exchange(默认:SMART

    如果在dataname规范中未提供交易所,则应用的默认值

  • currency(默认:''

    如果在dataname规范中未提供货币,则应用的默认值

  • historical(默认:False

    如果设置为True,数据源将在第一次下载数据后停止。

    将使用标准数据源参数fromdatetodate作为参考。

    如果请求的持续时间大于由 IB 给定的允许的数据时间段/压缩,则数据源将发出多个请求。

  • what(默认:None

    如果为None,则历史数据请求将使用不同资产类型的默认值:

    • 对于 CASH 资产,为‘BID’

    • 对于任何其他交易

    如果希望使用另一个值,请查看 IB API 文档

  • rtbar(默认:False

    如果为True,则将使用由 Interactive Brokers 提供的5 秒实时数据条作为最小刻度。根据文档,它们对应于实时值(一旦被 IB 整理和筛选)

    如果为False,则将使用基于接收到的刻度的RTVolume价格。对于CASH资产(例如 EUR.JPY),将始终使用RTVolume,并从中获取bid价格(根据互联网上零散的文献,这是 IB 的行业事实标准)

    即使设置为True,如果数据被重新采样/保留到低于秒/5 的时间段/压缩,也不会使用实时数据,因为 IB 不会在该级别以下提供它们

  • qcheck(默认:0.5

    如果未收到数据,等待的时间(秒)以便适当地对数据包进行重新采样/重播并将通知传递给链上

  • backfill_start(默认:True

    在开始时执行回填。将在单个请求中获取尽可能多的历史数据。

  • backfill(默认:True

    在断开连接/重新连接周期后执行回填。间隙持续时间将用于下载尽可能少的数据

  • backfill_from(默认:None

    可以传递附加数据源来进行初始回填。一旦数据源用尽,并且如果需要,将从 IB 进行回填。理想情况下,这意味着从已存储的源(如磁盘上的文件)进行回填,但不限于此。

  • latethrough(默认:False

    如果数据源被重采样/重播,一些 ticks 可能来得太晚,已经交付的重采样/重播 bar 了。如果设置为 True,那些 ticks 将无论如何通过。

    检查重采样文档以了解如何考虑这些 ticks。

    这种情况可能特别发生在 IBStore 实例中 timeoffset 设置为 False,且 TWS 服务器时间与本地计算机时间不同步时

  • tradename(默认:None)对于某些特定情况很有用,比如 CFD,其中价格由一种资产提供,交易发生在另一种资产上。

    • SPY-STK-SMART-USD -> 标普 500 ETF(将被指定为 dataname

    • SPY-CFD-SMART-USD -> 对应的 CFD 提供的不是价格跟踪,而是交易资产(指定为 tradename

参数中的默认值是允许类似 \TICKER这样的东西,其中参数sectype(默认:STK)和 exchange(默认:SMART`)被应用。

一些资产如 AAPL 需要完整的规范,包括 currency(默认:‘’),而其他资产如 TWTR 可以直接传递。

  • AAPL-STK-SMART-USD 将是 dataname 的完整规范

    或者:IBData 作为 IBData(dataname='AAPL', currency='USD'),它使用默认值(STKSMART),并覆盖货币为 USD

Oanda

原文:www.backtrader.com/docu/live/oanda/oanda/

与 Oanda 的集成支持:

  • 实时数据馈送

  • 实时交易

要求

  • oandapy

    使用以下命令安装:pip install git+https://github.com/oanda/oandapy.git

  • pytz(可选且不推荐)

    鉴于外汇市场的全球性和 24 小时运作的特点,选择使用UTC时间。如果愿意,您仍然可以使用您期望的输出时区。

示例代码

源代码包含完整示例:

  • samples/oandatest/oandatest.py

Oanda - 存储

存储是实时数据提要/交易支持的关键,提供了Oanda API 与数据提要和经纪人代理的需求之间的适配层。

  • 提供访问使用方法获取经纪人实例:

    • OandaStore.getbroker(*args, **kwargs)
  • 提供访问数据提要实例的方法

    • OandaStore.getedata(\*args, **kwargs)

    在这种情况下,许多**kwargs与数据提要(例如datanamefromdatetodatesessionstartsessionendtimeframecompression)是共同的

    数据可能提供其他参数。请查看下面的参考资料。

强制性参数

为了成功连接到Oanda,以下参数是强制性的:

  • token(默认值:None):API 访问令牌

  • account(默认值:None):账户 ID

这些由Oanda提供

是否连接到测试服务器或真实服务器,请使用:

  • practice(默认值:False):使用测试环境

必须定期检查账户以获取现金价值。刷新周期可以通过以下方式控制:

  • account_tmout(默认值:10.0):帐户价值/现金刷新周期

Oanda 提要

实例化数据:

  • 根据 Oanda 的指南传递符号

    • 根据 Oanda 的指南,EUR/USDD必须指定为EUR_USD。实例化如下:
    data = oandastore.getdata(dataname='EUR_USD', ...)` 
    

时间管理

除非将tz参数(pytz 兼容对象)传递给数据提要,否则所有时间输出均为UTC格式,如上所述。

回填

backtraderOanda没有特殊要求。对于小时间框架,在测试服务器上由Oanda返回的回填长度为500

OandaBroker - 实时交易

使用经纪人

要使用OandaBroker,必须替换由cerebro创建的标准经纪人模拟实例。

使用Store模型(首选):

import backtrader as bt

cerebro = bt.Cerebro()
oandastore = bt.stores.OandaStore()
cerebro.broker = oandastore.getbroker()  # or cerebro.setbroker(...)

经纪人 - 初始持仓

经纪人支持一个参数:

  • use_positions(默认值:True):连接到经纪人提供商时,使用现有持仓来启动经纪人。

    在实例化时设置为False,以忽略任何现有持仓

操作

关于标准用法没有变化。只需使用策略中可用的方法(详见Strategy参考资料以获取完整解释)

  • buy

  • sell

  • close

  • cancel

订单执行类型

Oanda几乎支持backtrader所需的所有订单执行类型,但不包括Close

因此,订单执行类型受到限制:

  • Order.Market

  • Order.Limit

  • Order.Stop

  • Order.StopLimit(使用 StopupperBound / lowerBound 价格)

  • Order.StopTrail

  • Bracket 订单受到支持,使用 takeprofitstoploss 订单成员并在内部创建模拟订单。

订单有效性

在回测期间(使用 validbuysell)可用的相同的有效性概念可用,并且具有相同的含义。因此,对于以下值,Oanda Ordersvalid 参数将如下翻译:

  • None 转换为 Good Til Cancelled

    因为未指定有效性,所以理解为订单必须有效直至取消

  • datetime/date 转换为 Good Til Date

  • timedelta(x) 转换为 Good Til Date(这里 timedelta(x) != timedelta()

    这被解释为信号,要求订单从 now + timedelta(x) 开始有效。

  • timedelta() 或 0 转换为 Session

    已传递一个值(而不是 None)但为 Null,并被解释为当前 day(会话)有效的订单

通知

标准的 Order 状态将通过 notify_order 方法(如果已重写)通知到策略

  • Submitted - 订单已发送到 TWS

  • Accepted - 订单已下达

  • Rejected - 用于实际拒绝和在订单创建期间未知其他状态时使用

  • Partial - 部分执行已经发生

  • Completed - 订单已完全执行

  • Canceled(或 Cancelled

  • Expired - 当订单因到期而取消时

参考

OandaStore

class backtrader.stores.OandaStore()

单例类,用于控制与 Oanda 的连接。

参数:

  • token(默认值:None):API 访问令牌

  • account(默认值:None):账户 ID

  • practice(默认值:False):使用测试环境

  • account_tmout(默认值:10.0):账户价值/现金刷新的刷新周期

OandaBroker

class backtrader.brokers.OandaBroker(**kwargs)

Oanda 的经纪人实现。

此类将来自 Oanda 的订单/持仓映射到 backtrader 的内部 API。

参数:

  • use_positions(默认值:True):连接到经纪人提供者时,使用现有仓位启动经纪人。

    在实例化期间设置为 False 以忽略任何现有仓位

OandaData

class backtrader.feeds.OandaData(**kwargs)

Oanda 数据源。

参数:

  • qcheck(默认值:0.5

    如果未收到数据,则在苏醒的秒数内将给出重新采样/重播数据包的机会,并将通知传递给链上

  • historical(默认值:False

    如果设置为 True,数据源将在第一次下载数据后停止。

    将使用标准数据源参数 fromdatetodate 作为参考。

    如果请求的持续时间大于 IB 允许的时间跨度/压缩所选择的数据的持续时间,数据源将进行多次请求。

  • backfill_start(默认值:True

    在开始时执行回填。将通过单个请求获取最大可能的历史数据。

  • backfill(默认:True

    在断开/重新连接周期后执行回填。间隙持续时间将用于下载可能的最小数据量

  • backfill_from(默认:None

    可以传递额外的数据源来进行初始的回填层。一旦数据源用尽并且如果请求,将从 IB 进行回填。理想情况下,这是为了从已存储的源(如磁盘上的文件)进行回填,但不限于此。

  • bidask(默认:True

    如果为True,则历史/回填请求将从服务器请求bid/ask价格

    如果为False,则将请求midpoint

  • useask(默认:False

    如果为True,则将使用bidask价格的ask部分,而不是默认的bid使用方式

  • includeFirst(默认:True

    通过直接设置参数到 Oanda API 调用来影响历史/回填请求的第一个柱条的交付

  • reconnect(默认:True

    当网络连接断开时重新连接

  • reconnections(默认:-1

    重新连接尝试的次数:-1表示永远

  • reconntimeout(默认:5.0

    在重新连接尝试之间等待的时间(秒)

此数据源仅支持timeframecompression的以下映射,这些映射符合 OANDA API 开发人员指南中的定义:

(TimeFrame.Seconds, 5): 'S5',
(TimeFrame.Seconds, 10): 'S10',
(TimeFrame.Seconds, 15): 'S15',
(TimeFrame.Seconds, 30): 'S30',
(TimeFrame.Minutes, 1): 'M1',
(TimeFrame.Minutes, 2): 'M3',
(TimeFrame.Minutes, 3): 'M3',
(TimeFrame.Minutes, 4): 'M4',
(TimeFrame.Minutes, 5): 'M5',
(TimeFrame.Minutes, 10): 'M10',
(TimeFrame.Minutes, 15): 'M15',
(TimeFrame.Minutes, 30): 'M30',
(TimeFrame.Minutes, 60): 'H1',
(TimeFrame.Minutes, 120): 'H2',
(TimeFrame.Minutes, 180): 'H3',
(TimeFrame.Minutes, 240): 'H4',
(TimeFrame.Minutes, 360): 'H6',
(TimeFrame.Minutes, 480): 'H8',
(TimeFrame.Days, 1): 'D',
(TimeFrame.Weeks, 1): 'W',
(TimeFrame.Months, 1): 'M',

任何其他组合都将被拒绝

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/544822.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第⑭讲:Ceph集群管理:守护进程管理、日志管理和端口号配置

文章目录 1.Ceph各组件守护进程的管理方式2.守护进程管理操作2.1.Ceph所有组件的守护进程列表2.2.重启当前主机中所有的Ceph组件2.3.重启主机中所有的Monitor组件2.4.重启指定主机的Monitor组件2.5.重启指定的OSD组件 3.Ceph的日志管理4.Ceph集群各组件的守护进程5.Ceph集群各组…

All in One:Prometheus 多实例数据统一管理最佳实践

作者&#xff1a;淡唯&#xff08;啃唯&#xff09;、阳其凯&#xff08;逸陵&#xff09; 引言 Prometheus 作为目前最主流的可观测开源项目之一&#xff0c;已经成为云原生监控的事实标准&#xff0c;被众多企业广泛应用。在使用 Prometheus 的时候&#xff0c;我们经常会遇…

自然语言处理: 第二十六章大模型基底之Mistral 8x7B

文章地址: 2401.04088.pdf (arxiv.org) 项目地址: mistralai/mistral-src: Reference implementation of Mistral AI 7B v0.1 model 前言: 本文意在一文深度剖析Mistral 8X7B的关键改进点。 Mistral AI是一个由DeepMind和Meta的三位前员工在巴黎共同创立的AI公司。其在23年…

tsconfig.json文件常用配置

最近在学ts&#xff0c;因为tsconfig的配置实在太多啦&#xff0c;所以写此文章用作记录&#xff0c;也作分享 作用&#xff1f; tsconfig.jsono是ts编译器的配置文件&#xff0c;ts编译器可以根据它的信息来对代码进行编译 初始化一个tsconfig文件 tsc -init配置参数解释 …

股票开户佣金最低多少?万一!A股开户多少钱合适?

开户佣金 通常情况下&#xff0c;股票开户佣金只要在达成交易的前提才收手续的费用&#xff0c;即买入和卖出的时候。目前&#xff0c;国规定收取最高佣金的比例为千分之三。 也就是说&#xff0c;最高为成交金额的3%&#xff0c;一般都会小于这个比例。最低交易佣金是5元起&a…

mac中的VirtualBox不能分配USB设备到虚拟电脑

mac中的VirtualBox不能分配USB设备到虚拟电脑 检查工具-> 扩展包是否安装 Oracle_VM_VirtualBox_Extension_Pack-7.0.14.vbox-extpack检查usb设备是否打开 检查权限 允许VirtualBox访问&#xff1a;在“安全性与隐私”窗口中&#xff0c;选择“隐私”标签。 在左侧的列表中…

微信过期文件怎么恢复?四个高招助你轻松解决(2024新版)

“微信的文件未下载的情况下过期了&#xff0c;平时微信也没有登录在电脑上&#xff0c;之前也没有进行过数据备份&#xff0c;如何找回这个文件啊&#xff01;&#xff01;感谢回答&#xff01;” “急求&#xff0c;当时忘记点击下载&#xff0c;现在急用微信文件下载不了&a…

Go gin框架(详细版)

目录 0. 为什么会有Go 1. 环境搭建 2. 单-请求&&返回-样例 3. RESTful API 3.1 首先什么是RESTful API 3.2 Gin框架支持RESTful API的开发 4. 返回前端代码 go.main index.html 5. 添加静态文件 main.go 改动的地方 index.html 改动的地方 style.css 改动…

【Linux网络编程】TCP协议

TCP协议 1.TCP协议段格式4位首位长度序号和确认序号16位窗口大小6个标志位 2.确认应答机制3.超时重传机制4.连接管理机制如何理解连接如何理解三次握手如何理解四次挥手 5.流量控制6.滑动窗口7.拥塞控制8.延迟应答9.捎带应答10.面向字节流11.粘包问题12.TCP异常情况13.TCP小结1…

通讯录的实现(单链表版本)

我们首先要知道通讯录的实现是基于单链表的基础上的&#xff0c;所以我们首先要搞懂单链表。&#xff08;注意&#xff1a;今天的代码量较多&#xff09;&#xff0c;但这不是阻挡我们前进的脚步&#xff0c;冲冲冲&#xff01;&#xff01;&#xff01; 单链表的简要概述 我们…

剖析 SPI 在 Spring 中的应用

一、概述 SPI&#xff08;Service Provider Interface&#xff09;&#xff0c;是Java内置的一种服务提供发现机制&#xff0c;可以用来提高框架的扩展性&#xff0c;主要用于框架的开发中&#xff0c;比如Dubbo&#xff0c;不同框架中实现略有差异&#xff0c;但核心机制相同…

构建第一个ArkTS应用之stateStyles:多态样式

Styles和Extend仅仅应用于静态页面的样式复用&#xff0c;stateStyles可以依据组件的内部状态的不同&#xff0c;快速设置不同样式。这就是我们本章要介绍的内容stateStyles&#xff08;又称为&#xff1a;多态样式&#xff09;。 概述 stateStyles是属性方法&#xff0c;可以…

如何发布自己的Python库?

Python包发布 1、背景概述2、操作指南 1、背景概述 为什么我们要发布自己的Python库&#xff1f;如果你想让你的Python代码&#xff0c;通过pip install xxx的方式供所有人下载&#xff0c;那就需要将代码上传到PyPi上&#xff0c;这样才能让所有人使用 那么&#xff0c;如何发…

【最新整理】3ds Max 大佬都在用的10款爆火插件推荐!

在3D建模和渲染领域&#xff0c;熟悉使用各种插件已经成为了大佬们的标配&#xff0c;而3ds Max作为最受欢迎的三维建模软件之一&#xff0c;更是有着丰富的插件资源。今天&#xff0c;小编将为大家盘点一下最新整理的10款爆火插件&#xff0c;这些插件不仅能够提升你的工作效率…

集合体系java

Collection:单列集合&#xff1a;每个元素只包含一个值 Collection集合存储的是地址 Collection的三种遍历方法如下 //迭代器是用来遍历集合的专用方式&#xff08;数组没有迭代器&#xff09;&#xff0c;在java中迭代器的代表是Iterator //boolean hasNext():询问当前位置…

10万字208道Java经典面试题总结(2024修订版)- SSM篇

&#x1f345; 作者简介&#xff1a;哪吒&#xff0c;CSDN2021博客之星亚军&#x1f3c6;、新星计划导师✌、博客专家&#x1f4aa; &#x1f345; 哪吒多年工作总结&#xff1a;Java学习路线总结&#xff0c;搬砖工逆袭Java架构师 &#x1f345; 技术交流&#xff1a;定期更新…

(三)C++自制植物大战僵尸游戏项目结构说明

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/ErelL 一、项目结构 打开项目后&#xff0c;在解决方案管理器中有五个项目&#xff0c;分别是libbox2d、libcocos2d、librecast、libSpine、PlantsVsZombies五个项目&#xff0c;除PlantsVsZombies外&#xff0c;其他四个…

map与set

set使用 set在我们就是我们前面学习的k模型&#xff0c;它可以用来比对数据&#xff0c;增删查的时间复杂度都是O&#xff08;logn&#xff09;效率非常高&#xff0c;由于它底层的原因&#xff0c;它也可以实现排序&#xff0c;通过中序遍历可以输出我们的有序的数据&#xff…

#新版Onenet云平台使用(ESP8266 AT指令上报数据以及公网MQTT服务器连接测试)

1.上云方式&#xff1a;MQTT 参考&#xff1a; 新版ONENET物联网开放平台ATMQTT指令连接_at指令连接onenet的mqtt-CSDN博客https://blog.csdn.net/lilbye/article/details/131770196 ESP8266-01s入门&#xff1a;AT指令讲解、上云与MQTT通信教程-物联沃-IOTWORD物联网https:…

软考 系统架构设计师系列知识点之大数据设计理论与实践(5)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之大数据设计理论与实践&#xff08;4&#xff09; 所属章节&#xff1a; 第19章. 大数据架构设计理论与实践 第3节 Lambda架构 19.3.1 Lambda架构对大数据处理系统的理解 Lambda架构由Storm的作者Nathan Marz提出&…