StopTrail(Limit)#

备注

这只在回测中实现了,还没有用于实盘交易的实现。

备注

更新至 1.9.36.116 版。交互经纪商支持 StopTrailStopTrailLimit ` 和 ` OCO

  • OCO 参数中总是指定第一笔订单作为参数 oco

  • StopTrailLimit : 经纪商模拟和 IB 经纪商具有相同的行为。指定:初始止损触发价(同时指定 ` trailamount `)作为 ` price `,然后将 ` plimi ` 作为初始限价。两者之间的差异将决定 ` limitoffset`(限价与止损触发价之间的距离)

用法模式已完全集成到策略实例的标准 buysell ` 和 ` close 市场操作方法中。请注意:

  • exectype=bt.Order.StopTrail 中指明所需的执行类型

  • 以固定距离或基于百分比的距离计算追踪价格

    • 固定距离: trailamount=10

    • 基于百分比的距离: trailpercent=0.02 (即 2%

如果通过 buy 命令进入了市场,使用带有 StopTrailtrailamountsell 命令会产生以下效果:- 如果未指定``价格``,将使用最新的``收盘``价格 - 从价格中减去``trailamount``以找到``停止``(或触发)价格

  • 经纪人 的下一轮迭代检查触发价格是否已达到

    • 如果**是**:使用``市价``执行类型来执行订单

    • 如果**否**,则使用最新的``收盘``价格减去``trailamount``距离来重新计算``停止``价格

      • 如果新价格上升,将其更新

      • 如果新价格将下降(或不变),则将其丢弃

也就是说:追踪停止价格随着价格上涨而上升,但如果价格开始下跌,则保持不变,以潜在地确保利润。

如果以``卖出``的方式进入市场,那么以``StopTrail``发出``购买``订单只是相反的操作,即:价格会向下跟随。

一些使用模式

# 对于向下的StopTrail
# 最新的价格将作为参考
self.buy(size=1, exectype=bt.Order.StopTrail, trailamount=0.25)
# 或者
self.buy(size=1, exectype=bt.Order.StopTrail, price=10.50, trailamount=0.25)

# 对于向上的StopTrail
# 最新的价格将作为参考
self.sell(size=1, exectype=bt.Order.StopTrail, trailamount=0.25)
# 或者
self.sell(size=1, exectype=bt.Order.StopTrail, price=10.50, trailamount=0.25)还可以指定``trailpercent``而不是``trailamount``,距离价格的距离将以价格的百分比计算:
# 如果距离为2%且向下的停止追踪
# 最后价格将被用作参考
self.buy(size=1, exectype=bt.Order.StopTrail, trailpercent=0.02)
# 或者
self.buy(size=1, exectype=bt.Order.StopTrail, price=10.50, trailpercent=0.0.02)

# 如果距离为2%且向上的停止追踪
# 最后价格将被用作参考
self.sell(size=1, exectype=bt.Order.StopTrail, trailpercent=0.02)
# 或者
self.sell(size=1, exectype=bt.Order.StopTrail, price=10.50, trailpercent=0.02)

对于``StopTrailLimit``

  • 唯一的区别在于触发追踪止损价时会发生什么。

  • 在这种情况下,订单将作为``Limit``订单执行(与``StopLimit``订单的行为相同,但此处的触发价格是动态的)

    备注

    必须在``buy``或``sell``中指定``plimit=x.x``,这将是*限价*价格

    备注

    *限价*价格不像停止/触发价格那样动态改变

一个例子胜过千言万语,下面是典型的 backtrader 示例,其中

  • 使用移动平均线交叉点向上进入市场

  • 使用追踪止损退出市场具有“50”点固定价格距离的执行方式:

$ ./trail.py --plot --strat trailamount=50.0

生成以下图表

和以下输出:

2005-02-15,3086.95,3036.95,3036.95 2005-02-16,3068.55,3036.95,3018.55 2005-02-17,3067.34,3036.95,3017.34 2005-02-18,3072.04,3036.95,3022.04 2005-02-21,3063.64,3036.95,3013.64 … … ********************************************** 2005-05-19,3051.79,3001.79,3001.79 ———- 2005-05-20,3050.45,3001.79,3000.45 2005-05-23,3070.98,3020.98,3020.98 2005-05-24,3066.55,3020.98,3016.55 2005-05-25,3059.84,3020.98,3009.84 2005-05-26,3086.08,3036.08,3036.08 2005-05-27,3084.0,3036.08,3034.0 2005-05-30,3096.54,3046.54,3046.54 2005-05-31,3076.75,3046.54,3026.75 2005-06-01,3125.88,3075.88,3075.88 2005-06-02,3131.03,3081.03,3081.03 2005-06-03,3114.27,3081.03,3064.27 2005-06-06,3099.2,3081.03,3049.2 2005-06-07,3134.82,3084.82,3084.82 2005-06-08,3125.59,3084.82,3075.59 2005-06-09,3122.93,3084.82,3072.93 2005-06-10,3143.85,3093.85,3093.85 2005-06-13,3159.83,3109.83,3109.83 2005-06-14,3162.86,3112.86,3112.86 2005-06-15,3147.55,3112.86,3097.55 2005-06-16,3160.09,3112.86,3110.09 2005-06-17,3178.48,3128.48,3128.48 2005-06-20,3162.14,3128.48,3112.14 2005-06-21,3179.62,3129.62,3129.62 2005-06-22,3182.08,3132.08,3132.08 2005-06-23,3190.8,3140.8,3140.8 2005-06-24,3161.0,3140.8,3111.0 … … … ********************************************** 2006-12-19,4100.48,4050.48,4050.48 ———- 2006-12-20,4118.54,4068.54,4068.54 2006-12-21,4112.1,4068.54,4062.1 2006-12-22,4073.5,4068.54,4023.5 2006-12-27,4134.86,4084.86,4084.86 2006-12-28,4130.66,4084.86,4080.66 2006-12-29,4119.94,4084.86,4069.94

系统使用移动止损来退出市场,而不是等待通常的下降趋势。以第一次操作为例:

  • 进入多头时的收盘价格:3075.76

  • 系统计算的移动止损价格:``3025.76``(与每行所示的最后价格相距``50``个单位)

  • 实际计算的移动止损价格:3025.76

在第一次计算之后:

  • 收盘价格上涨至``3086.95``,止损价格调整为``3036.95``

  • 接下来的收盘价格不超过``3086.95``,触发价格不变其他两个操作中也可以看到相同的模式。

为了进行比较,只有``30``个点的固定距离(只有图表)的执行

$ ./trail.py --plot --strat trailamount=30.0

和这个图表

接着是最后一次执行,使用``trailpercent=0.02``

$ ./trail.py --plot --strat trailpercent=0.02

对应的图表。

样例使用 ::``` $ ./trail.py –help 用法: trail.py [-h] [–data0 DATA0] [–fromdate FROMDATE] [–todate TODATE]

[–cerebro kwargs] [–broker kwargs] [–sizer kwargs] [–strat kwargs] [–plot [kwargs]]

StopTrail 示例

可选参数:
-h, --help

显示帮助信息并退出

--data0 DATA0

要读取的数据(默认值: ../../datas/2005-2006-day-001.txt)

--fromdate FROMDATE

日期[时间]以YYYY-MM-DD[THH:MM:SS]格式表示(默认值: )

--todate TODATE

日期[时间]以YYYY-MM-DD[THH:MM:SS]格式表示(默认值: )

--cerebro kwargs

以key=value格式的kwargs(默认值: )

--broker kwargs

以key=value格式的kwargs(默认值: )

--sizer kwargs

以key=value格式的kwargs(默认值: )

--strat kwargs

以key=value格式的kwargs(默认值: )

–plot [kwargs] 以key=value格式的kwargs(默认值: )

示例代码

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=10,
        p2=30,
        stoptype=bt.Order.StopTrail,
        trailamount=0.0,
        trailpercent=0.0,
    )

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.crup = bt.ind.CrossUp(ma1, ma2)
        self.order = None

    def next(self):
        if not self.position:
            if self.crup:
                o = self.buy()
                self.order = None
                print('*' * 50)

        elif self.order is None:
            self.order = self.sell(exectype=self.p.stoptype,
                                   trailamount=self.p.trailamount,
                                   trailpercent=self.p.trailpercent)

            if self.p.trailamount:
                tcheck = self.data.close - self.p.trailamount
            else:
                tcheck = self.data.close * (1.0 - self.p.trailpercent)
            print(','.join(
                map(str, [self.datetime.date(), self.data.close[0],
                          self.order.created.price, tcheck])
                )
            )
            print('-' * 10)
        else:
            if self.p.trailamount:
                tcheck = self.data.close - self.p.trailamount
            else:
                tcheck = self.data.close * (1.0 - self.p.trailpercent)
            print(','.join(
                map(str, [self.datetime.date(), self.data.close[0],
                          self.order.created.price, tcheck])
                )
            )


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=(
            'StopTrail Sample'
        )
    )

    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()

```