Backtrader 教程:订单 - 目标订单

  |  

1.8.10.96版本之前,可以使用反向交易者通过策略方法进行智能质押: buysell 。这一切都是为了在负责赌注大小的等式中添加一个Sizer

Sizer不能做的是决定操作是买入还是卖出。这意味着需要一个新概念,在其中添加一个小的智能层来做出这样的决定。

这是 Strategy 中的order_target_xxx方法家族发挥作用的地方。受zipline中的启发,这些方法提供了简单地指定最终目标的机会,成为目标:

  • size -> 特定资产投资组合中的股份数量、合约数量

  • value -> 投资组合中资产的货币单位价值

  • percent -> 当前投资组合中资产的百分比(来自当前投资组合)

笔记

这些方法的参考可以在策略中找到。总结是这些方法使用与buysell相同的签名,除了参数size被参数target替换

在这种情况下,一切都与指定最终目标有关,并且该方法决定操作是买入还是卖出。相同的逻辑适用于 3 种方法。让我们从order_target_size

  • 如果目标大于发出买入的头寸,则差异target - position_size

    例子:

    • 位置: 0 ,目标: 7 -> 购买(尺寸=7 - 0)-> 购买(尺寸=7)

    • 位置: 3 ,目标: 7 -> 购买(尺寸=7 - 3)-> 购买(尺寸=4)

    • 位置: -3 ,目标: 7 -> 购买(尺寸=7 - -3)-> 购买(尺寸=10)

    • 位置: -3 ,目标: -2 -> 购买(尺寸=-2 - -3)-> 购买(尺寸=1)

  • 如果目标小于头寸,则发出卖出,差异为position_size - target

    例子:

    • 位置: 0 ,目标: -7 -> 销售(大小=0 - -7) -> 销售(大小=7)

    • 职位: 3 ,目标: -7 -> 出售(大小=3 - -7)-> 出售(大小=10)

    • 位置: -3 ,目标: -7 -> 出售(尺寸=-3 - -7)-> 出售(尺寸=4)

    • 职位: 3 ,目标: 2 -> 销售(大小=3 - 2) -> 销售(大小=1)

当使用order_target_value一个值时,投资组合中资产的当前价值和头寸规模都被考虑在内,以决定最终的基础操作是什么。推理:

  • 如果头寸规模为负(空头)并且目标值必须大于当前值,这意味着:卖出更多

因此,逻辑工作如下:

  • 如果target > valuesize >=0 -> 购买

  • 如果target > valuesize < 0 -> 卖出

  • 如果target < valuesize >= 0 -> 卖出

  • 如果target < valuesize < 0 -> 购买

order_target_value的逻辑与order_target_percent的逻辑相同。该方法仅考虑投资组合的当前总价值来确定资产的目标价值。

样本

backtrader尝试为每个新功能提供一个示例,这也不例外。没有花里胡哨的东西,只是测试一下结果是否符合预期。这个在示例中的order_target目录下。

示例中的逻辑相当愚蠢,仅用于测试:

  • 在奇数月(一月、三月、...),使用天作为目标(在order_target_value将天乘以1000的情况下)

    这模仿了一个不断增加的目标

  • 在偶数月(2 月、4 月……)使用31 - day作为目标

    这仿真了一个递减的目标

order_target_size

让我们看看 1 月和 2 月会发生什么。

$ ./order_target.py --target-size -- plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - Order Target Size: 03
0002 - 2005-01-04 - Position Size:     03 - Value 999994.39
0002 - 2005-01-04 - Order Target Size: 04
0003 - 2005-01-05 - Position Size:     04 - Value 999992.48
0003 - 2005-01-05 - Order Target Size: 05
0004 - 2005-01-06 - Position Size:     05 - Value 999988.79
...
0020 - 2005-01-31 - Position Size:     28 - Value 999968.70
0020 - 2005-01-31 - Order Target Size: 31
0021 - 2005-02-01 - Position Size:     31 - Value 999954.68
0021 - 2005-02-01 - Order Target Size: 30
0022 - 2005-02-02 - Position Size:     30 - Value 999979.65
0022 - 2005-02-02 - Order Target Size: 29
0023 - 2005-02-03 - Position Size:     29 - Value 999966.33
0023 - 2005-02-03 - Order Target Size: 28
...

1 月,目标从3点开始,从一年中的第一个交易日开始,然后增加。头寸大小最初从0移动到3 ,然后以1为增量。

1 月结束时,最后一个order_target 为31 ,并且在进入 2 月 1时报告仓位大小,此时新的目标方被要求为30 ,并且随着仓位的递减“1”而变化。

order_target_value

预计目标值会出现类似的行为

$ ./order_target.py --target-value --plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - data value 0.00
0001 - 2005-01-03 - Order Target Value: 3000.00
0002 - 2005-01-04 - Position Size:     78 - Value 999854.14
0002 - 2005-01-04 - data value 2853.24
0002 - 2005-01-04 - Order Target Value: 4000.00
0003 - 2005-01-05 - Position Size:     109 - Value 999801.68
0003 - 2005-01-05 - data value 3938.17
0003 - 2005-01-05 - Order Target Value: 5000.00
0004 - 2005-01-06 - Position Size:     138 - Value 999699.57
...
0020 - 2005-01-31 - Position Size:     808 - Value 999206.37
0020 - 2005-01-31 - data value 28449.68
0020 - 2005-01-31 - Order Target Value: 31000.00
0021 - 2005-02-01 - Position Size:     880 - Value 998807.33
0021 - 2005-02-01 - data value 30580.00
0021 - 2005-02-01 - Order Target Value: 30000.00
0022 - 2005-02-02 - Position Size:     864 - Value 999510.21
0022 - 2005-02-02 - data value 30706.56
0022 - 2005-02-02 - Order Target Value: 29000.00
0023 - 2005-02-03 - Position Size:     816 - Value 999130.05
0023 - 2005-02-03 - data value 28633.44
0023 - 2005-02-03 - Order Target Value: 28000.00
...

有一条额外的信息线说明实际数据值(在投资组合中)是什么。这有助于找出是否已达到目标值。

初始目标是3000.0 ,报告的初始值为2853.24 。这里的问题是这是否足够接近。答案是肯定的

  • 该示例使用每日柱结束时的Market单和最后可用价格来计算满足目标值的目标大小

  • 然后运行使用第二天的open ,这不太可能是前一个close

以任何其他方式这样做都意味着一个人在欺骗他/她自己。

下一个目标值和最终值更接近: 40003938.17

当更改为二月时,目标值开始从31000减少到3000029000 。从30580.0030706.56再到28633.44的数据值也是如此。等待:

  • 30580 -> 30706.56是一个积极的变化

    的确。在这种情况下,目标值的计算大小满足开盘价,将值推高至30706.56

如何避免这种影响:

  • 示例对订单使用Market类型运行,这种影响无法避免

  • order_target_xxx方法允许指定运行类型和价格。

    可以将Limit指定为运行订单,并将价格设为收盘价(如果没有提供其他信息,则由方法选择)甚至提供具体定价

order_target_percent

在这种情况下,它只是当前投资组合价值的百分比。

$ ./order_target.py --target-percent --plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - data percent 0.00
0001 - 2005-01-03 - Order Target Percent: 0.03
0002 - 2005-01-04 - Position Size:     785 - Value 998532.05
0002 - 2005-01-04 - data percent 0.03
0002 - 2005-01-04 - Order Target Percent: 0.04
0003 - 2005-01-05 - Position Size:     1091 - Value 998007.44
0003 - 2005-01-05 - data percent 0.04
0003 - 2005-01-05 - Order Target Percent: 0.05
0004 - 2005-01-06 - Position Size:     1381 - Value 996985.64
...
0020 - 2005-01-31 - Position Size:     7985 - Value 991966.28
0020 - 2005-01-31 - data percent 0.28
0020 - 2005-01-31 - Order Target Percent: 0.31
0021 - 2005-02-01 - Position Size:     8733 - Value 988008.94
0021 - 2005-02-01 - data percent 0.31
0021 - 2005-02-01 - Order Target Percent: 0.30
0022 - 2005-02-02 - Position Size:     8530 - Value 995005.45
0022 - 2005-02-02 - data percent 0.30
0022 - 2005-02-02 - Order Target Percent: 0.29
0023 - 2005-02-03 - Position Size:     8120 - Value 991240.75
0023 - 2005-02-03 - data percent 0.29
0023 - 2005-02-03 - Order Target Percent: 0.28
...

并且信息已更改以查看数据在投资组合中代表的%

示例使用

$ ./order_target.py --help
usage: order_target.py [-h] [--data DATA] [--fromdate FROMDATE]
                       [--todate TODATE] [--cash CASH]
                       (--target-size | --target-value | --target-percent)
                       [--plot [kwargs]]

Sample for Order Target

optional arguments:
  -h, --help            show this help message and exit
  --data DATA           Specific data to be read in (default:
                        ../../datas/yhoo-1996-2015.txt)
  --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)
  --cash CASH           Ending date in YYYY-MM-DD format (default: 1000000)
  --target-size         Use order_target_size (default: False)
  --target-value        Use order_target_value (default: False)
  --target-percent      Use order_target_percent (default: False)
  --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
from datetime import datetime

import backtrader as bt


class TheStrategy(bt.Strategy):
    '''
    This strategy is loosely based on some of the examples from the Van
    K. Tharp book: *Trade Your Way To Financial Freedom*. The logic:

      - Enter the market if:
        - The MACD.macd line crosses the MACD.signal line to the upside
        - The Simple Moving Average has a negative direction in the last x
          periods (actual value below value x periods ago)

     - Set a stop price x times the ATR value away from the close

     - If in the market:

       - Check if the current close has gone below the stop price. If yes,
         exit.
       - If not, update the stop price if the new stop price would be higher
         than the current
    '''

    params = (
        ('use_target_size', False),
        ('use_target_value', False),
        ('use_target_percent', False),
    )

    def notify_order(self, order):
        if order.status == order.Completed:
            pass

        if not order.alive():
            self.order = None  # indicate no order is pending

    def start(self):
        self.order = None  # sentinel to avoid operrations on pending order

    def next(self):
        dt = self.data.datetime.date()

        portfolio_value = self.broker.get_value()
        print('%04d - %s - Position Size:     %02d - Value %.2f' %
              (len(self), dt.isoformat(), self.position.size, portfolio_value))

        data_value = self.broker.get_value([self.data])

        if self.p.use_target_value:
            print('%04d - %s - data value %.2f' %
                  (len(self), dt.isoformat(), data_value))

        elif self.p.use_target_percent:
            port_perc = data_value / portfolio_value
            print('%04d - %s - data percent %.2f' %
                  (len(self), dt.isoformat(), port_perc))

        if self.order:
            return  # pending order execution

        size = dt.day
        if (dt.month % 2) == 0:
            size = 31 - size

        if self.p.use_target_size:
            target = size
            print('%04d - %s - Order Target Size: %02d' %
                  (len(self), dt.isoformat(), size))

            self.order = self.order_target_size(target=size)

        elif self.p.use_target_value:
            value = size * 1000

            print('%04d - %s - Order Target Value: %.2f' %
                  (len(self), dt.isoformat(), value))

            self.order = self.order_target_value(target=value)

        elif self.p.use_target_percent:
            percent = size / 100.0

            print('%04d - %s - Order Target Percent: %.2f' %
                  (len(self), dt.isoformat(), percent))

            self.order = self.order_target_percent(target=percent)


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

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

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

    # data
    data = bt.feeds.YahooFinanceCSVData(dataname=args.data, **dkwargs)
    cerebro.adddata(data)

    # strategy
    cerebro.addstrategy(TheStrategy,
                        use_target_size=args.target_size,
                        use_target_value=args.target_value,
                        use_target_percent=args.target_percent)

    cerebro.run()

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

        cerebro.plot(**pkwargs)


def parse_args(pargs=None):

    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Sample for Order Target')

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

    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('--cash', required=False, action='store',
                        type=float, default=1000000,
                        help='Ending date in YYYY-MM-DD format')

    pgroup = parser.add_mutually_exclusive_group(required=True)

    pgroup.add_argument('--target-size', required=False, action='store_true',
                        help=('Use order_target_size'))

    pgroup.add_argument('--target-value', required=False, action='store_true',
                        help=('Use order_target_value'))

    pgroup.add_argument('--target-percent', required=False,
                        action='store_true',
                        help=('Use order_target_percent'))

    # 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 is not None:
        return parser.parse_args(pargs)

    return parser.parse_args()


if __name__ == '__main__':
    runstrat()

推荐阅读

相关文章

Backtrader教程:观察者 - 参考

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

Backtrader期货补偿与现货补偿

版本1.9.32.116 增加了对社区中呈现的有趣用例 的支持 以期货开始交易,包括实物交割 让一个指针告诉你一些事情 如果需要, close 现货价格操作,有效地取消实物交割,无论是为了接收货物还是为了必须交付货物(并希望获利)来头寸。

Backtrader教程:操作平台

Line 反复运算器 为了参与操作,plaftorm使用 line 反复运算器的概念。它们已经松散地模仿了Python的反复运算器,但实际上与它们无关。 策略和指针是 line 反复运算器。

Backtrader扩展数据馈送

GitHub 中的问题实际上是在推动文档部分的完成,或者说明我了解 backtrader 是否具有我从最初时刻就设想的易用性和灵活性以及在此过程中做出的决定。 在本例中为问题 #9。

Backtrader节省内存

1.3.1.92版本已经重新设计并完全实现了以前到位的内存节省方案,尽管没有太多的吹捧和使用。

Backtrader卡尔曼等

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

Backtrader开场作弊

“发布”1.9.44.116 添加了对 Cheat-On-Open的支持。这似乎是那些全力以赴的人的需求功能,他们在酒吧 close 后进行了计算,但希望与 open 价格相匹配。 当开盘价跳空(上涨或下跌,取决于是否buysell有效)并且现金不足以进行全面运营时,这样的用例就会失败。这将强制代理拒绝该操作。

Backtrader现实咬合

上一篇文章设法拷贝了该BTFD 策略,发现真正的收益 16x 而不是 31x。 但正如拷贝期间所指出的: 不收取佣金 使用2x 杠杆不收取利息 这就提出了一个显而易见的问题: 当收取佣金和利息时,这16x中有多少会存在? 幸运的是,前面的示例足够灵活,可以对其进行试验。

Backtrader教程:指针 - ta-lib

即使 backtrader 提供了已经 high 数量的内置指针,并且开发指针主要是定义输入,输出和以自然的方式编写公式的问题,有些人也希望使用TA-LIB。

Backtrader终极振荡器

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