Backtrader逃离 OHLC 土地

  |  

backtrader的概念和开发过程中应用的关键概念之一是灵活性。 Python 的元编程和自省功能曾经是(现在仍然是)保持许多东西灵活同时仍然能够交付的基础。

一篇旧帖子显示了扩展概念。

基础知识:

from backtrader.feeds import GenericCSVData

class GenericCSV_PE(GenericCSVData):
    lines = ('pe',)  # Add 'pe' to already defined lines

完毕。 backtrader在后台定义了最常用的:OHLC。

如果我们深入研究GenericCSV_PE的最后一个方面,继承的加上新定义的的总和将产生以下

('close', 'open', 'high', 'low', 'volume', 'openinterest', 'datetime', 'pe',)

这可以随时使用getlinealiases方法检查(适用于 DataFeeds、Indicators、Strategies 和Observers

该机制是灵活的,通过深入了解内部结构,您实际上可以获得任何东西,但事实证明这还不够。

Ticket #60询问是否支持高频数据,即:Bid/Ask 数据。这意味着 OHLC 形式的预定义层次结构是不够的。买入价和卖出价、交易量和交易数量可以根据现有的 OHLC 字段进行调整,但感觉不自然。如果只关心买价和卖价,就会有太多的领域没有触及。

这需要一个已与Release 1.2.1.88一起实施的解决方案。这个想法可以概括为:

  • 现在不仅可以扩展现有的层次结构,还可以用新的层次结构替换层次结构

只有一个约束:

  • 必须存在一个datetime时间字段(希望包含有意义的datetime时间信息)

    之所以如此,是因为backtrader需要一些东西来进行同步(多个数据、多个时间框架、重新采样、重放),就像阿基米德需要杠杆一样。

这是它的工作原理:

from backtrader.feeds import GenericCSVData

class GenericCSV_BidAsk(GenericCSVData):
    linesoverride = True
    lines = ('bid', 'ask', 'datetime')  # Replace hierarchy with this one

完毕。

好吧,不完全。但这仅仅是因为我们正在考虑从 csv 源加载。由于linesoverride= True设置,层次结构实际上已经被出价替换,询问日期时间定义。

原始的GenericCSVData类解析 csv 文档并需要提示与对应的字段所在的位置。原来的定义是:

class GenericCSVData(feed.CSVDataBase):
    params = (
        ('nullvalue', float('NaN')),
        ('dtformat', '%Y-%m-%d %H:%M:%S'),
        ('tmformat', '%H:%M:%S'),

        ('datetime', 0),
        ('time', -1),  # -1 means not present
        ('open', 1),
        ('high', 2),
        ('low', 3),
        ('close', 4),
        ('volume', 5),
        ('openinterest', 6),
    )

轻触即可完成新的层次结构重新定义类:

from backtrader.feeds import GenericCSVData

class GenericCSV_BidAsk(GenericCSVData):
    linesoverride = True
    lines = ('bid', 'ask', 'datetime')  # Replace hierarchy with this one

    params = (('bid', 1), ('ask', 2))

表明买价是 csv 流中的第 1 字段,而卖价是第 2 字段。我们没有改变基类中的 datetime #0 定义。

为这种场合制作一个小数据文档有助于:

TIMESTAMP,BID,ASK
02/03/2010 16:53:50,0.5346,0.5347
02/03/2010 16:53:51,0.5343,0.5347
02/03/2010 16:53:52,0.5543,0.5545
02/03/2010 16:53:53,0.5342,0.5344
02/03/2010 16:53:54,0.5245,0.5464
02/03/2010 16:53:54,0.5460,0.5470
02/03/2010 16:53:56,0.5824,0.5826
02/03/2010 16:53:57,0.5371,0.5374
02/03/2010 16:53:58,0.5793,0.5794
02/03/2010 16:53:59,0.5684,0.5688

在方程中添加一个小测试脚本(为那些直接访问源代码中的示例的人提供更多内容)(参见最后的完整代码):

$ ./bidask.py

输出不言自明:

 1: 2010-02-03T16:53:50 - Bid 0.5346 - 0.5347 Ask
 2: 2010-02-03T16:53:51 - Bid 0.5343 - 0.5347 Ask
 3: 2010-02-03T16:53:52 - Bid 0.5543 - 0.5545 Ask
 4: 2010-02-03T16:53:53 - Bid 0.5342 - 0.5344 Ask
 5: 2010-02-03T16:53:54 - Bid 0.5245 - 0.5464 Ask
 6: 2010-02-03T16:53:54 - Bid 0.5460 - 0.5470 Ask
 7: 2010-02-03T16:53:56 - Bid 0.5824 - 0.5826 Ask
 8: 2010-02-03T16:53:57 - Bid 0.5371 - 0.5374 Ask
 9: 2010-02-03T16:53:58 - Bid 0.5793 - 0.5794 Ask
10: 2010-02-03T16:53:59 - Bid 0.5684 - 0.5688 Ask

瞧!买入/卖出价格已被正确读取、解析和解释,并且该策略已经能够通过 self.data 访问数据馈送中的 .bid 和 .ask

不过,重新定义线条层次结构会引发一个广泛的问题,那就是已经预定义的指针的使用。

  • 示例:随机指针是一个依靠收盘价、最高价和最低价来计算其输出的指针

    即使我们将 Bid 作为收盘价(因为是第一个),也只有一个其他价格元素(Ask)而不是另外两个。从概念上讲,Ask 与高低无关

    从事这些领域工作并在高频交易领域进行操作(或研究)的人很可能并不关心随机指针作为选择指针

  • 移动平均线等其他指针非常好。他们对这些领域的含义或暗示没有任何假设,并且很乐意接受任何东西。因此可以这样做:

    mysma = backtrader.indicators.SMA(self.data.bid, period=5)
    

    最后5 个投标价格的移动平均线将被交付

测试脚本已经支持添加 SMA。让我们运行:

$ ./bidask.py --sma --period=3

输出:

 3: 2010-02-03T16:53:52 - Bid 0.5543 - 0.5545 Ask - SMA: 0.5411
 4: 2010-02-03T16:53:53 - Bid 0.5342 - 0.5344 Ask - SMA: 0.5409
 5: 2010-02-03T16:53:54 - Bid 0.5245 - 0.5464 Ask - SMA: 0.5377
 6: 2010-02-03T16:53:54 - Bid 0.5460 - 0.5470 Ask - SMA: 0.5349
 7: 2010-02-03T16:53:56 - Bid 0.5824 - 0.5826 Ask - SMA: 0.5510
 8: 2010-02-03T16:53:57 - Bid 0.5371 - 0.5374 Ask - SMA: 0.5552
 9: 2010-02-03T16:53:58 - Bid 0.5793 - 0.5794 Ask - SMA: 0.5663
10: 2010-02-03T16:53:59 - Bid 0.5684 - 0.5688 Ask - SMA: 0.5616

笔记

绘图仍然依赖于数据馈送中存在的openhigh价、 lowclosevolume

某些情况可以通过简单地在关闭时绘制一条线并仅采用对像中定义的第一条线来直接覆盖。但是必须开发一个健全的模型。对于即将发布的backtrader版本

测试脚本用法:

$ ./bidask.py --help
usage: bidask.py [-h] [--data DATA] [--dtformat DTFORMAT] [--sma]
                 [--period PERIOD]

Bid/Ask Line Hierarchy

optional arguments:
  -h, --help            show this help message and exit
  --data DATA, -d DATA  data to add to the system (default:
                        ../../datas/bidask.csv)
  --dtformat DTFORMAT, -dt DTFORMAT
                        Format of datetime in input (default: %m/%d/%Y
                        %H:%M:%S)
  --sma, -s             Add an SMA to the mix (default: False)
  --period PERIOD, -p PERIOD
                        Period for the sma (default: 5)

以及测试脚本本身(包含在backtrader资源中)

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

import argparse

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


class BidAskCSV(btfeeds.GenericCSVData):
    linesoverride = True  # discard usual OHLC structure
    # datetime must be present and last
    lines = ('bid', 'ask', 'datetime')
    # datetime (always 1st) and then the desired order for
    params = (
        # (datetime, 0), # inherited from parent class
        ('bid', 1),  # default field pos 1
        ('ask', 2),  # default field pos 2
    )


class St(bt.Strategy):
    params = (('sma', False), ('period', 3))

    def __init__(self):
        if self.p.sma:
            self.sma = btind.SMA(self.data, period=self.p.period)

    def next(self):
        dtstr = self.data.datetime.datetime().isoformat()
        txt = '%4d: %s - Bid %.4f - %.4f Ask' % (
            (len(self), dtstr, self.data.bid[0], self.data.ask[0]))

        if self.p.sma:
            txt += ' - SMA: %.4f' % self.sma[0]
        print(txt)


def parse_args():
    parser = argparse.ArgumentParser(
        description='Bid/Ask Line Hierarchy',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )

    parser.add_argument('--data', '-d', action='store',
                        required=False, default='../../datas/bidask.csv',
                        help='data to add to the system')

    parser.add_argument('--dtformat', '-dt',
                        required=False, default='%m/%d/%Y %H:%M:%S',
                        help='Format of datetime in input')

    parser.add_argument('--sma', '-s', action='store_true',
                        required=False,
                        help='Add an SMA to the mix')

    parser.add_argument('--period', '-p', action='store',
                        required=False, default=5, type=int,
                        help='Period for the sma')

    return parser.parse_args()


def runstrategy():
    args = parse_args()

    cerebro = bt.Cerebro()  # Create a cerebro

    data = BidAskCSV(dataname=args.data, dtformat=args.dtformat)
    cerebro.adddata(data)  # Add the 1st data to cerebro
    # Add the strategy to cerebro
    cerebro.addstrategy(St, sma=args.sma, period=args.period)
    cerebro.run()


if __name__ == '__main__':
    runstrategy()

推荐阅读

相关文章

Backtrader砖块

Renko Bricks 是呈现价格演变的另一种方式,其中价格比时间发挥更重要的作用。这已在1.9.54.122的1.9.54.122版本中作为过滤器引入Stockcharts 对 Renko Bricks 有很好的参考。

Backtrader改进代码

时不时地,带有 backtrader 代码的示例会在互联网上弹出。在我看来,有几个是中国人。最新的一个在这里: 标题是: backtrader-学习笔记2,这显然(感谢谷歌)翻译成 backtrader- 学习笔记2。

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

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

Backtrader实际使用方式

最后,似乎已经付出了开发 backtrader是值得的。 在观察 last 周的欧洲市场时,似乎世界末日了,一位朋友问我是否可以看看我们图表包中的数据,看看与以前类似情况相比,下跌幅度如何。 当然可以,但我说我可以做的不仅仅是查看图表,因为我可以快速: 创建一个快速LegDown 指示器来测量跌落的范围。

Backtrader数据同步

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

Backtrader蟒蛇隐藏的力量3

Last,但并非最不重要的一点是,在这个系列中,关于如何在 backtrader 中使用Python的隐藏功能是一些神奇变量是如何出现的。

Backtrader 多数据范例

社区中的几个主题似乎以如何跟踪订单为导向,特别是当几个data feeds在起作用时,还包括当多个订单一起工作时,

Backtrader佣金计划

发布 backtrader 使用示例使我对缺失的东西有了深刻的了解。

Backtrader教程:分析仪 - PyFolio

注意 从(至少)2017-07-25pyfolio 开始,API已更改,不再 create_full_tear_sheet 具有 gross_lev 作为命名参数的参数。

Backtrader教程:数据馈送 - 多个时间帧

有时投资决策是使用不同的时间框架做出的: 每周评估趋势 每天运行条目 或者5分钟对60分钟。 这意味着需要组合多个时间帧的数据backtrader 来支持这种组合。 对它的本机支持已经内置。