Backtrader目标订单

  |  

1.8.10.96版本之前,可以使用反向交易者通过策略方法进行智能质押:买入和卖出。这一切都是为了在负责赌注大小的等式中添加一个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

更改为 2 月时,目标值开始从31000减少到3000029000\ 。从 30580.00 to 30706.56 and then to 28633.44 的*数据值*也是如此。等待:

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

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

如何避免这种影响:

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

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

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

order_target_value

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

$ ./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砖块

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

Backtrader按日线交易

似乎在世界某个地方有一种权益(Interest)可以总结如下: 使用每日柱线引入订单,但使用开盘价 这来自工单#105订单执行逻辑与当前数据和#101动态投注计算中的对话 backtrader 尝试尽可能保持现实,并且在处理每日柱线时适用以下前提: 当每日柱被评估时,柱线已经结束 这是有道理的,

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

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

Backtrader期货补偿与现货补偿

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

Backtrader教程:日志记录 - 编写器

将以下内容写出到流中: csv 流,

Backtrader 教程:交易

交易的定义:当工具中的头寸从 0 变为大小 X 时,交易打开,对于多头/空头头寸可能为正/负)当头寸从 X 变为 0 时,交易平仓。以下两个动作:正转负负转正实际上被视为:一笔交易已关闭(仓位从 X 变为 0)新交易已开立(仓位从 0 变为 Y)交易只是提供信息,没有用户可调用的方法。

Backtrader信号策略

操作 backtrader 也是可能的,而无需编写策略。虽然这是首选方式,但由于构成机器的对象层次结构,使用信号也是可能的。

Backtrader 教程:数据馈送 - 开发 - 常规

笔记示例goog.fd中使用的二进制文档属于 VisualChart,不能与backtrader一起分发。对直接使用二进制文档感兴趣的人可以免费下载VisualChart 。 CSV数据馈送开发已经展示了如何添加新的基于 CSV 的数据馈送。

BacktraderCSV 数据馈送开发

backtrader已经提供了通用 CSV数据提要和一些特定的 CSV数据提要。

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

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