Backtrader多核优化

  |  

利用所有可用的内核是我为反向交易者考虑的事情,但从未完成。支持自然运算,删除数组符号,包含新指针和 bla, bla, bla。

实际上,我不是优化的忠实拥护者,因此也不是为优化使用所有内核的忠实拥护者。恕我直言,一个好主意值得一百万次优化。

笔记

最初的多核支持已经存在,并且适用于众所周知的测试用例集。鉴于pickle表现出的行为,预计其他一些调整可以确保在进行多核优化时所有指针和函数可以在进程之间来回传递。

笔记

在 1.0.10.88 版本中已经推送了一些针对多核的额外更正,以使更多“不可挑选”的东西变得可挑选。到目前为止,指针测试没有显示任何问题。

但是 BigMikeTrading 论坛中的某个人询问了该平台与其他平台相比必须提供的功能,我提到了一些功能,例如PyAlgoTrade已经具备(甚至是多机)

完成它所需的小而正确的推动就在那里。根据过去的经验,并且因为互联网上充满了我已经知道的参考数据:即使是最简单的多线程(无论 GIL 律师怎么说)在 Python 中都是不行的,无论版本如何。多线程在 Python 中是假的,因为您有多个线程但没有并行运行代码。使用 IO 绑定线程创建抽象和单独的代码路径运行可能很好,但它确实是一个杀手。

然后只剩下一个选择:模块multiprocessing或类似的。

展望光明的未来,我决定选择现代版本: concurrent.futures (后来证明是一个错误的选择)即使这意味着为 Python 2.6/2.7 支持添加外部依赖项。

历史:

  • Python 的一些动态特性不能很好地在进程之间来回发送数据

  • 酸洗(串行化)诸如未在模块级别定义的类,lambda,对实例方法的引用和没有唯一名称的动态类(即使这些类本身是唯一的)时,所涉及的模块( pickle )会阻塞

我把这些东西分散在代码中。然后我从 pathos multiprocess https://pypi.python.org/pypi/multiprocess找到了dill和兄弟姐妹。显然他们会解决串行化问题,但会增加更多的外部依赖……不,不。

回到绘图板上,看看是否可以将不可拾取的项目设为可拾取,即使pickle模块产生了错误,这会让一些老 GCC 开发人员非常高兴。

它完成了……还是没有?

  • 将不可拾取的物品重做为可拾取的东西

  • 使用 Python 2.7.9 运行测试并轻而易举地运行……使用我机器的 8 个内核流畅而令人耳目一新

  • 使用 Python 3.4.3 运行测试,8 核开始运行,但经过一些优化后,每个后续策略的运行时间会越来越长……直到无法忍受。

    显然,将结果(一个完整的运行策略)提取到主进程中遇到了与内存分配相关的一些限制(而且我的机器有足够的可用 RAM……对于几个小时的并行优化来说绰绰有余)

一些额外的阅读让我考虑简化我的场景:

  • 使用concurrent.futures似乎是未来的证明

  • 但是标准的multiprocessing模块已经具备了backtrader需求

闻起来一直在使用矫枉过正,一些很快被重新设计,并且:

  • 测试在 Python 2.7 上运行良好(甚至比以前更快)

  • 测试运行速度与 Python 3.4 一样快

是时候进行清理,运行完整的测试并运行推送和发布 1.0.9.88。没有新指针……只是简单的旧多核优化

阅读完所有内容……是时候编写一个关于如何控制优化以使用多个内核的令人耳目一新的脚本了

  • 好消息……无需做任何事情……无需用户干预即可完成

当用户希望优化strategy时, Strategy子类被添加到Cerebro实例中,如下所示:

cerebro.optstrategy(StrategyClass, *args, **kwargs)

与将策略传递给Cerebro的常规方式相反:

cerebro.addstrategy(StrategyClass, *args, **kwargs)

这一直如此,并没有改变。背景是:

  • Cerebro需要了解是否要优化策略以正确处理策略的参数,这些参数可能已经是常规策略的可iterables对象

现在……通过optstrategy传递给cerebrooptstrategy获得了使用机器所有可用内核的额外好处。

当然,如果最终用户希望对使用过的内核进行细粒度控制……这是可能的。创建Cerebro的标准方法:

大脑= bt。 Cerebro () # runonce 是True , preload 是True并且 “new” maxcpus 是 None

maxcpus (此版本的新参数)是控制键:

  • maxcpus = None -> 使用所有可用的 CPU

  • maxcpus = 1 -> 不要运行多核

  • maxcpues = 2 … -> 使用指定的内核数

这是一种选择退出策略,因为多核已经加入。

在具有 16 GB RAM、运行 Windows 8.1 和 Python 64bit 2.7.9 的 4 核(每个核 2x 线程 - 总共 8 个逻辑处理器)机器上进行比较

  • 使用 1 个内核运行:326 秒

  • 8核运行:127秒

不同的测试运行表明,该比率平均约为 2.75:1。

不幸的是,进程的创建/销毁和对象的来回酸洗具有潜在的好处,但加速仍然很重要。

该图显示了正在使用的 8 个内核。

代码如下。只需将maxcpus参数更改为1即可将测试限制为 1 个内核。

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

import time

from six.moves import xrange

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


class OptimizeStrategy(bt.Strategy):
    params = (('smaperiod', 15),
              ('macdperiod1', 12),
              ('macdperiod2', 26),
              ('macdperiod3', 9),
              )

    def __init__(self):
        # Add indicators to add load

        btind.SMA(period=self.p.smaperiod)
        btind.MACD(period_me1=self.p.macdperiod1,
                   period_me2=self.p.macdperiod2,
                   period_signal=self.p.macdperiod3)


if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro(maxcpus=None)

    # Add a strategy
    cerebro.optstrategy(
        OptimizeStrategy,
        smaperiod=xrange(5, 40),
        macdperiod1=xrange(12, 20),
        macdperiod2=xrange(26, 30),
        macdperiod3=xrange(9, 15),
    )

    # Create a Data Feed
    datapath = ('../datas/2006-day-001.txt')
    data = bt.feeds.BacktraderCSVData(dataname=datapath)

    # Add the Data Feed to Cerebro
    cerebro.adddata(data)

    # clock the start of the process
    tstart = time.clock()

    # Run over everything
    stratruns = cerebro.run()

    # clock the end of the process
    tend = time.clock()

    print('==================================================')
    for stratrun in stratruns:
        print('**************************************************')
        for strat in stratrun:
            print('--------------------------------------------------')
            print(strat.p._getkwargs())
    print('==================================================')

    # print out the result
    print('Time used:', str(tend - tstart))

推荐阅读

相关文章

Backtrader策略选择

休士顿我们有一个问题: cerebro 不应多次运行。这不是第1次 ,而不是认为用户做错了,这似乎是一个用例。 这个有趣的用例是通过票证177出现的。在这种情况下, cerebro 被多次用于评估从外部数据源获取的不同策略。 backtrader 仍然可以支持此用例,但不能以直接尝试的方式支持。

Backtrader教程:分析仪

无论是回溯测试还是交易,能够分析交易系统的性能是了解是否不仅获得了利润,而且是否在风险太大的情况下实现了利润,或者与参考资产(或无风险资产)相比,是否真的值得付出努力的关键。 这就是对象家族的用武之Analyzer 地:提供对所发生事件甚至实际发生的事情的分析。

Backtrader追踪订单

版本1.9.35.116将StopTrail和StopTrailLimit订单运行类型添加到回测库中。笔记这仅在回测中实现,还没有针对实时经纪人的实现笔记更新为1.9.36.116版本。盈透证券支持StopTrail 、 StopTrailLimit和OCO 。

Backtrader分数大小

首先,让我们用两行总结反向交易方法的工作原理:它就像一个带有基本构建块 ( Cerebro ) 的建筑套件,可以插入许多不同的部件基本分布包含许多部分,如指针、分析器、观察器、 Sizer 、过滤器、数据馈送、经纪人、佣金/资产信息方案,...

Backtrader教程:筛检程序

此功能是 backtrader 的相对较新的补充,必须安装到已经存在的内部结构中。这使得它不像希望的那样灵活且100%功能齐全,但在许多情况下它仍然可以达到目的。 尽管该实现试图允许随插即用的筛检程序链接,但预先存在的内部结构使得很难确保始终可以实现。因此,某些筛选器可能是链接的,而其他一些筛选器可能不是。

Backtrader教程:日期时间 - 管理

在 1.5.0 版之前, backtrader 使用直接的方法来进行时间管理,因为数据源计算的任何日期时间都只是按面值使用。 对于任何用户输入也是如此,例如可以提供给任何数据源的参数fromdate (或 sessionstart)的情况 考虑到直接控制冻结的数据源以进行回溯测试,这种方法很好。

Backtrader 教程:指针 - 时间框架混合

版本 1.3.0.92提供了混合来自不同时间范围的数据(来自数据馈送和/或指针)的可能性。背景:指针是智能的哑对象。他们很聪明,因为他们可以进行复杂的计算。他们是愚蠢的,因为他们在不知道哪些来源为计算提供数据的情况下进行操作像这样:如果提供值的数据源在Cerebro引擎内具有不同的时间范围、不同的长度,则指针将中断。

Backtrader教程:分析仪 - PyFolio

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

Backtrader开发指针

经过 backtrader 微调(因为它已经运行了一段时间),我决定不仅通过GitHub分享它,还告诉世界它在那里,并在“Reddit”中发布它的存在。 在评论了为什么交易/算法交易平台会弹出,以及关于支持许多同时交易的即时交易的平台的私人问题之后,我得出的结论是,我自己的孩子应该拥有自己的博客。 我们来了。

Backtrader动量策略

在另一篇伟大的文章中,泰迪·科克(Teddy Koker)再次展示了算法交易策略的发展之路: 研究优先应用 pandas 回溯测试,然后使用 backtrader 荣誉!!! 该帖子可以在以下位置找到: 泰迪·科克(Teddy Koker)给我留言,问我是否可以评论 backtrader的用法。