Skip to content

括号订单

原文: https://www.backtrader.com/blog/posts/2017-04-01-bracket/bracket/

发行版1.9.37.116增加了bracket订单,提供了非常广泛的订单,并得到了回溯测试经纪人的支持(MarketLimitCloseStopStopLimitStopTrailStopTrailLimitOCO

笔记

这是为回溯测试交互经纪人商店实施的

bracket订单不是单个订单,但它实际上由3订单组成。让我们考虑长边

  • 主面buy指令,通常设置为LimitStopLimit指令

  • 低端sell指令,通常设置为Stop指令以限制损失

  • 高端sell订单,通常设置为Limit订单以获取利润

短端对应sell和 2 个buy订单。

低端/高端订单实际上在主端订单周围创建了一个括号。

要将一些逻辑放在其中,以下规则适用:

  • 这 3 个订单一起提交,以避免单独触发其中任何一个订单

  • 低端/高端订单标记为主端的子级

  • 在执行主侧之前,子级不会处于活动状态

  • 主侧的取消将同时取消低端和高端

  • 主侧的执行同时激活低压侧和高压侧

  • 活跃时

    • 执行或取消任何低端/高端订单都会自动取消另一个订单

使用模式

创建订单的括号集有两种可能性

  • 单次发出 3 份订单

  • 手动发出 3 份订单

单次发出括号

反向交易者Strategy中提供了两种新的方法来控制括号订单。

  • buy_bracketsell_bracket

笔记

签名和信息在下方或Strategy参考部分。

用一条语句就可以得到一套完整的 3 个订单。例如:

brackets = self.buy_bracket(limitprice=14.00, price=13.50, stopprice=13.00) 

注意stoppricelimitprice如何包装主price

这应该足够了。实际目标data将是data0,而size将由默认的大小器自动确定。当然,可以指定这两个参数和许多其他参数,以便对执行进行精细控制。

返回值为:

  • Alist包含此订单中的 3 个订单:[main, stop, limit]

因为在发出sell_bracket指令时,低端和高端都会被调到 aound,参数的命名遵循约定stoplimit

  • stop用于停止损失(长时间运行时低压侧,短时间运行时高压侧)

  • limit是为了获取利润(长操作高,短操作低)

人工签发括号

这涉及到 3 个命令的生成,以及对transmitparent参数的处理。规则:

  • 主侧订单必须创建 1st且有transmit=False

  • 低端/高端订单必须有parent=main_side_order

  • 要创建的 1st低端/高端订单必须有transmit=False

  • 最后一个要创建的订单(低端或高端)设置为transmit=True

下面是一个实际示例,它实现了上面的单个命令的功能:

mainside = self.buy(price=13.50, exectype=bt.Order.Limit, transmit=False)
lowside  = self.sell(price=13.00, size=mainsize.size, exectype=bt.Order.Stop,
                     transmit=False, parent=mainside)
highside = self.sell(price=14.00, size=mainsize.size, exectype=bt.Order.Limit,
                     transmit=True, parent=mainside) 

还有很多事情要做:

  • 跟踪mainside订单,表明它是其他订单的父项

  • 控制transmit以确保只有最后一个命令触发联合传输

  • 指定执行类型

  • 指定低侧和高侧的size

    因为size必须相同。如果没有手动指定参数,并且最终用户引入了一个 sizer,那么 sizer 实际上可以为订单指示一个不同的值。这就是为什么在为mainside订单设置后,必须手动将其添加到调用中。

它的样本

从下面运行示例将生成此输出(为简洁起见,设置了上限)

$ ./bracket.py --plot

2005-01-28: Oref 1 / Buy at 2941.11055
2005-01-28: Oref 2 / Sell Stop at 2881.99275
2005-01-28: Oref 3 / Sell Limit at 3000.22835
2005-01-31: Order ref: 1 / Type Buy / Status Submitted
2005-01-31: Order ref: 2 / Type Sell / Status Submitted
2005-01-31: Order ref: 3 / Type Sell / Status Submitted
2005-01-31: Order ref: 1 / Type Buy / Status Accepted
2005-01-31: Order ref: 2 / Type Sell / Status Accepted
2005-01-31: Order ref: 3 / Type Sell / Status Accepted
2005-02-01: Order ref: 1 / Type Buy / Status Expired
2005-02-01: Order ref: 2 / Type Sell / Status Canceled
2005-02-01: Order ref: 3 / Type Sell / Status Canceled
...
2005-08-11: Oref 16 / Buy at 3337.3892
2005-08-11: Oref 17 / Sell Stop at 3270.306
2005-08-11: Oref 18 / Sell Limit at 3404.4724
2005-08-12: Order ref: 16 / Type Buy / Status Submitted
2005-08-12: Order ref: 17 / Type Sell / Status Submitted
2005-08-12: Order ref: 18 / Type Sell / Status Submitted
2005-08-12: Order ref: 16 / Type Buy / Status Accepted
2005-08-12: Order ref: 17 / Type Sell / Status Accepted
2005-08-12: Order ref: 18 / Type Sell / Status Accepted
2005-08-12: Order ref: 16 / Type Buy / Status Completed
2005-08-18: Order ref: 17 / Type Sell / Status Completed
2005-08-18: Order ref: 18 / Type Sell / Status Canceled
...
2005-09-26: Oref 22 / Buy at 3383.92535
2005-09-26: Oref 23 / Sell Stop at 3315.90675
2005-09-26: Oref 24 / Sell Limit at 3451.94395
2005-09-27: Order ref: 22 / Type Buy / Status Submitted
2005-09-27: Order ref: 23 / Type Sell / Status Submitted
2005-09-27: Order ref: 24 / Type Sell / Status Submitted
2005-09-27: Order ref: 22 / Type Buy / Status Accepted
2005-09-27: Order ref: 23 / Type Sell / Status Accepted
2005-09-27: Order ref: 24 / Type Sell / Status Accepted
2005-09-27: Order ref: 22 / Type Buy / Status Completed
2005-10-04: Order ref: 24 / Type Sell / Status Completed
2005-10-04: Order ref: 23 / Type Sell / Status Canceled
... 

其中显示了 3 种不同的结果:

  • 在 1st案例中,主辅助订单过期,自动取消了其他两个

  • 在第 2案例中,主侧订单已完成,低(买入案例中的停止)已执行,以限制损失

  • 在 3rd案例中,完成了主侧指令,并执行了高侧(限制)

    可以注意到这一点,因为完成的id 为2224,而侧订单最后发出,这意味着未执行的低侧订单 id 为 23。

视觉上

!image

可以立即看到,亏损交易与赢家交易的价值相同,这就是回笼的目的。控制双方。

样本运行时手动发出 3 个订单,但可以告知使用buy_bracket。让我们看看输出:

$ ./bracket.py --strat usebracket=True 

同样的结果

!image

一些参考资料

参见新的buy_bracketsell_bracket方法

def buy_bracket(self, data=None, size=None, price=None, plimit=None,
                exectype=bt.Order.Limit, valid=None, tradeid=0,
                trailamount=None, trailpercent=None, oargs={},
                stopprice=None, stopexec=bt.Order.Stop, stopargs={},
                limitprice=None, limitexec=bt.Order.Limit, limitargs={},
                **kwargs):
    '''
    Create a bracket order group (low side - buy order - high side). The
    default behavior is as follows:

      - Issue a **buy** order with execution ``Limit``

      - Issue a *low side* bracket **sell** order with execution ``Stop``

      - Issue a *high side* bracket **sell** order with execution
        ``Limit``.

    See below for the different parameters

      - ``data`` (default: ``None``)

        For which data the order has to be created. If ``None`` then the
        first data in the system, ``self.datas[0] or self.data0`` (aka
        ``self.data``) will be used

      - ``size`` (default: ``None``)

        Size to use (positive) of units of data to use for the order.

        If ``None`` the ``sizer`` instance retrieved via ``getsizer`` will
        be used to determine the size.

        **Note**: The same size is applied to all 3 orders of the bracket

      - ``price`` (default: ``None``)

        Price to use (live brokers may place restrictions on the actual
        format if it does not comply to minimum tick size requirements)

        ``None`` is valid for ``Market`` and ``Close`` orders (the market
        determines the price)

        For ``Limit``, ``Stop`` and ``StopLimit`` orders this value
        determines the trigger point (in the case of ``Limit`` the trigger
        is obviously at which price the order should be matched)

      - ``plimit`` (default: ``None``)

        Only applicable to ``StopLimit`` orders. This is the price at which
        to set the implicit *Limit* order, once the *Stop* has been
        triggered (for which ``price`` has been used)

      - ``trailamount`` (default: ``None``)

        If the order type is StopTrail or StopTrailLimit, this is an
        absolute amount which determines the distance to the price (below
        for a Sell order and above for a buy order) to keep the trailing
        stop

      - ``trailpercent`` (default: ``None``)

        If the order type is StopTrail or StopTrailLimit, this is a
        percentage amount which determines the distance to the price (below
        for a Sell order and above for a buy order) to keep the trailing
        stop (if ``trailamount`` is also specified it will be used)

      - ``exectype`` (default: ``bt.Order.Limit``)

        Possible values: (see the documentation for the method ``buy``

      - ``valid`` (default: ``None``)

        Possible values: (see the documentation for the method ``buy``

      - ``tradeid`` (default: ``0``)

        Possible values: (see the documentation for the method ``buy``

      - ``oargs`` (default: ``{}``)

        Specific keyword arguments (in a ``dict``) to pass to the main side
        order. Arguments from the default ``**kwargs`` will be applied on
        top of this.

      - ``**kwargs``: additional broker implementations may support extra
        parameters. ``backtrader`` will pass the *kwargs* down to the
        created order objects

        Possible values: (see the documentation for the method ``buy``

        **Note**: this ``kwargs`` will be applied to the 3 orders of a
        bracket. See below for specific keyword arguments for the low and
        high side orders

      - ``stopprice`` (default: ``None``)

        Specific price for the *low side* stop order

      - ``stopexec`` (default: ``bt.Order.Stop``)

        Specific execution type for the *low side* order

      - ``stopargs`` (default: ``{}``)

        Specific keyword arguments (in a ``dict``) to pass to the low side
        order. Arguments from the default ``**kwargs`` will be applied on
        top of this.

      - ``limitprice`` (default: ``None``)

        Specific price for the *high side* stop order

      - ``stopexec`` (default: ``bt.Order.Limit``)

        Specific execution type for the *high side* order

      - ``limitargs`` (default: ``{}``)

        Specific keyword arguments (in a ``dict``) to pass to the high side
        order. Arguments from the default ``**kwargs`` will be applied on
        top of this.

    Returns:

      - A list containing the 3 orders [order, stop side, limit side]

    '''

def sell_bracket(self, data=None,
                 size=None, price=None, plimit=None,
                 exectype=bt.Order.Limit, valid=None, tradeid=0,
                 trailamount=None, trailpercent=None,
                 oargs={},
                 stopprice=None, stopexec=bt.Order.Stop, stopargs={},
                 limitprice=None, limitexec=bt.Order.Limit, limitargs={},
                 **kwargs):
    '''
    Create a bracket order group (low side - buy order - high side). The
    default behavior is as follows:

      - Issue a **sell** order with execution ``Limit``

      - Issue a *high side* bracket **buy** order with execution ``Stop``

      - Issue a *low side* bracket **buy** order with execution ``Limit``.

    See ``bracket_buy`` for the meaning of the parameters

    Returns:

      - A list containing the 3 orders [order, stop side, limit side]

    ''' 

样本使用

$ ./bracket.py --help
usage: bracket.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE]
                  [--cerebro kwargs] [--broker kwargs] [--sizer kwargs]
                  [--strat kwargs] [--plot [kwargs]]

Sample Skeleton

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --fromdate FROMDATE  Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --todate TODATE      Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --cerebro kwargs     kwargs in key=value format (default: )
  --broker kwargs      kwargs in key=value format (default: )
  --sizer kwargs       kwargs in key=value format (default: )
  --strat kwargs       kwargs in key=value format (default: )
  --plot [kwargs]      kwargs in key=value format (default: ) 

示例代码

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

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        ma=bt.ind.SMA,
        p1=5,
        p2=15,
        limit=0.005,
        limdays=3,
        limdays2=1000,
        hold=10,
        usebracket=False,  # use order_target_size
        switchp1p2=False,  # switch prices of order1 and order2
    )

    def notify_order(self, order):
        print('{}: Order ref: {} / Type {} / Status {}'.format(
            self.data.datetime.date(0),
            order.ref, 'Buy' * order.isbuy() or 'Sell',
            order.getstatusname()))

        if order.status == order.Completed:
            self.holdstart = len(self)

        if not order.alive() and order.ref in self.orefs:
            self.orefs.remove(order.ref)

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.cross = bt.ind.CrossOver(ma1, ma2)

        self.orefs = list()

        if self.p.usebracket:
            print('-' * 5, 'Using buy_bracket')

    def next(self):
        if self.orefs:
            return  # pending orders do nothing

        if not self.position:
            if self.cross > 0.0:  # crossing up

                close = self.data.close[0]
                p1 = close * (1.0 - self.p.limit)
                p2 = p1 - 0.02 * close
                p3 = p1 + 0.02 * close

                valid1 = datetime.timedelta(self.p.limdays)
                valid2 = valid3 = datetime.timedelta(self.p.limdays2)

                if self.p.switchp1p2:
                    p1, p2 = p2, p1
                    valid1, valid2 = valid2, valid1

                if not self.p.usebracket:
                    o1 = self.buy(exectype=bt.Order.Limit,
                                  price=p1,
                                  valid=valid1,
                                  transmit=False)

                    print('{}: Oref {} / Buy at {}'.format(
                        self.datetime.date(), o1.ref, p1))

                    o2 = self.sell(exectype=bt.Order.Stop,
                                   price=p2,
                                   valid=valid2,
                                   parent=o1,
                                   transmit=False)

                    print('{}: Oref {} / Sell Stop at {}'.format(
                        self.datetime.date(), o2.ref, p2))

                    o3 = self.sell(exectype=bt.Order.Limit,
                                   price=p3,
                                   valid=valid3,
                                   parent=o1,
                                   transmit=True)

                    print('{}: Oref {} / Sell Limit at {}'.format(
                        self.datetime.date(), o3.ref, p3))

                    self.orefs = [o1.ref, o2.ref, o3.ref]

                else:
                    os = self.buy_bracket(
                        price=p1, valid=valid1,
                        stopprice=p2, stopargs=dict(valid=valid2),
                        limitprice=p3, limitargs=dict(valid=valid3),)

                    self.orefs = [o.ref for o in os]

        else:  # in the market
            if (len(self) - self.holdstart) >= self.p.hold:
                pass  # do nothing in this case

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

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))

def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'Sample Skeleton'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat() 

我们一直在努力

apachecn/AiLearning

【布客】中文翻译组