BackTrader 中文文档(二十三)(1)

简介: BackTrader 中文文档(二十三)


原文:www.backtrader.com/

基准测试

原文:www.backtrader.com/blog/posts/2016-07-22-benchmarking/benchmarking/

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

  • 观察者
  • 分析器

问题 #89是关于添加针对资产的基准测试。这是合理的,因为一个人可能实际上有一种策略,即使是正的,也低于简单跟踪资产所能提供的回报。

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

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

注意

观察者分析器之间的主要区别是观察者线性质,记录每��值并使其适合绘图和实时查询。当然,这会消耗内存。

另一方面,分析器通过get_analysis返回一组结果,实现可能直到运行结束才提供任何结果。

分析器 - 基准测试

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

  • timeframe(默认:None)如果为None,则将报告整个回测期间的完整回报
    传递TimeFrame.NoTimeFrame以考虑整个数据集而没有时间限制
  • data(默认:None
    用于跟踪而不是投资组合价值的参考资产。
    注意
    这些数据必须已经通过addataresampledatareplaydata添加到cerebro实例中

(有关更多详细信息和参数,请参阅文档中的参考资料)

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

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())

观察者 - 基准测试

由于背景机制允许在观察者内部使用分析器,因此添加了 2 个新的观察者:

  • TimeReturn
  • 基准

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

而不是像上面那样有代码片段,一个完整的示例带有一些运行来展示它们的功能。

观察 TimeReturn

执行:

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

输出。

注意执行选项:

  • --timereturn:我们告诉示例就是这样做的
  • --timeframe notimeframe:告诉分析器考虑整个数据集,而不考虑时间范围边界。

最后绘制的数值为-0.26

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

观察基准测试

因为基准也会显示时间回报率的结果,让我们运行相同的内容,但激活基准

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

输出。


嘿,嘿,嘿!!!

  • 策略比资产更好:-0.26-0.33
    这不应该是一个值得庆祝的事情,但至少很明显策略甚至没有资产那么糟糕。

转而以年度为基础追踪事物:

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

输出

注意!

  • 策略的最后一个值从-0.26变化到-0.27
  • 另一方面,资产显示的最后一个值是-0.35(与上述的-0.33相比)

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

转换到较低的时间范围,比如,整个情况都会改变:

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

输出


现在:

  • 基准 观察者显示出更加紧张的状态。事物上下移动,因为现在每周都在跟踪组合和数据的回报率
  • 因为在年底的最后一周没有交易活动,资产几乎没有变动,所以最后显示的值为 0.00(最后一周之前的最后收盘价为25.54,样本数据收盘价为25.55,差异首先在第 4 位小数点上感受到)

观察基准 - 另一数据

该样本允许针对不同的数据进行基准测试。默认情况下,使用--benchdata1时,基准测试的对象是Oracle。考虑整个数据集,使用--timeframe notimeframe

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

输出:

现在清楚为什么上面没有值得庆祝的原因了:

  • 策略的结果对于notimeframe没有改变,仍然为-26%-0.26
  • 但是当与另一数据进行基准比较时,这个数据在同一时期有+23%0.23)的增长

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

结论

现在有两种方法,使用相同的底层代码/计算,来跟踪时间回报率基准

  • 观察者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()

Pyfolio 集成

原文:www.backtrader.com/blog/posts/2016-07-17-pyfolio-integration/pyfolio-integration/

注意

2017 年 2 月

pyfolio的 API 已更改,create_full_tear_sheet不再具有gross_lev作为命名参数。

因此,下面的示例不起作用

初次查看教程时,由于 zipline 和 pyfolio 之间的紧密集成,被认为很难,但是 pyfolio 提供的用于其他用途的样本测试数据实际上非常有用,可以解码幕后运行的内容,从而实现集成的奇迹。

一个名为pyfolio投资组合工具的集成是在Ticket #108中提出的。

大部分组件已经就位在backtrader中:

  • 分析器基础设施
  • 子分析器
  • 一个 TimeReturn 分析器

只需要一个主要的PyFolio分析器和 3 个简单的分析器。再加上一个依赖于pyfolio已经需要的pandas的方法。

最具挑战性的部分是…“确保所有依赖项正确”。

  • 更新pandas
  • 更新numpy
  • 更新scikit-lean
  • 更新seaborn

在类 Unix 环境中使用C编译器,一切都是关于时间的。在 Windows 下,即使安装了特定的Microsoft编译器(在本例中是Python 2.7的链),事情也会失败。但是一个收集最新软件包的著名网站对Windows有所帮助。如果您需要,请访问:

如果没有经过测试,集成将不完整,这就是为什么通常的示例总是存在的原因。

没有 PyFolio

该示例使用random.randint来决定何时买入/卖出,因此这只是一个检查事情是否正常运行的方法:

$ ./pyfoliotest.py --printout --no-pyfolio --plot

输出:

Len,Datetime,Open,High,Low,Close,Volume,OpenInterest
0001,2005-01-03T23:59:59,38.36,38.90,37.65,38.18,25482800.00,0.00
BUY  1000 @%23.58
0002,2005-01-04T23:59:59,38.45,38.54,36.46,36.58,26625300.00,0.00
BUY  1000 @%36.58
SELL 500 @%22.47
0003,2005-01-05T23:59:59,36.69,36.98,36.06,36.13,18469100.00,0.00
...
SELL 500 @%37.51
0502,2006-12-28T23:59:59,25.62,25.72,25.30,25.36,11908400.00,0.00
0503,2006-12-29T23:59:59,25.42,25.82,25.33,25.54,16297800.00,0.00
SELL 250 @%17.14
SELL 250 @%37.01

有 3 个数据和几个买入卖出操作是随机选择并分散在测试运行的默认 2 年寿命中


BackTrader 中文文档(二十三)(2)https://developer.aliyun.com/article/1505435

相关文章
|
8天前
BackTrader 中文文档(二十三)(4)
BackTrader 中文文档(二十三)
12 0
|
8天前
|
编解码 API Windows
BackTrader 中文文档(二十三)(3)
BackTrader 中文文档(二十三)
14 0
|
8天前
|
编解码 索引
BackTrader 中文文档(二十三)(2)
BackTrader 中文文档(二十三)
11 0
|
8天前
BackTrader 中文文档(二十二)(1)
BackTrader 中文文档(二十二)
14 0
|
8天前
|
存储 Python
BackTrader 中文文档(二十二)(3)
BackTrader 中文文档(二十二)
19 0
|
8天前
|
存储 测试技术 Python
BackTrader 中文文档(二十二)(2)
BackTrader 中文文档(二十二)
16 0
|
8天前
|
测试技术
BackTrader 中文文档(二十二)(4)
BackTrader 中文文档(二十二)
17 0
|
8天前
BackTrader 中文文档(十七)(4)
BackTrader 中文文档(十七)
10 0
|
8天前
BackTrader 中文文档(十七)(2)
BackTrader 中文文档(十七)
11 0
|
8天前
|
测试技术 索引
BackTrader 中文文档(十七)(1)
BackTrader 中文文档(十七)
9 0

热门文章

最新文章