Python量化交易学习笔记(31)——backtrader的StopTrail订单

本文将继续对backtrader的order进行介绍,具体介绍StopTrail订单的使用。在笔记(20)已经对StopTrail的使用进行了简要的介绍,本文将通过更多的示例展示StopTrail订单在回测中应用。

选取平安银行(000001)2019年1月1日至2019年12月31日的日线数据进行回测。为了便于分析,回测过程中设置佣金为0,交易单位大小为100。

执行规则

给订单设置跟踪量(trail amount)或者跟踪比例(trail percent),用于计算跟踪价差,订单会使用跟踪价差按照下面描述的触发价格(跟踪价格/trail price/stop price/trigger price)计算规则计算更新触发价格,若价格回撤至触发价格,则订单成交。

触发价格计算规则

假设已经通过buy方法建仓,那么对于使用sell方法的平仓StopTrail订单,将会按照下面的规则计算跟踪价格:

  • 指定price值,如果没有指定,则以最新的收盘价close作为price值

  • 根据trailamount或trailpercent计算触发价格

    • 如果指定了trailamount,触发价格stopprice = price - trailamount

    • 如果指定了trailpercent,触发价格stopprice = price * (1 - trailpercent)

    • 如果同时制定了trailamount和trailpercent,则按照上面给定trailamount的情况计算触发价格

  • 检查在下一根K线是否达到触发价格

    • 如果达到触发价格,那么订单将按照Stop订单规则成交(官网上描述订单会按照Market订单规则成交,经过实验和查看源代码,应该是按照Stop订单规则成交)

    • 如果未达到触发价格,那么触发价格将会使用新的收盘价close结合trailamount及trailpercent,按照上面的计算方式计算出一个新的触发价格newstopprice

    • 如果新的触发价格高于原触发价格(newstopprice > stopprice),那么就更新触发价格(stopprice = newstopprice)

    • 如果新的触发价格低于或等于原触发价格(newstopprice <= stopprice),那么触发价格保持不变

也就是说,触发价格随收盘价的上涨而上涨,如果收盘价不变或下跌,触发价格则保持不变,由此来实现保护利润的目的。

类似地,假设已经通过sell方法建仓,那么对于使用buy方法的平仓StopTrail订单,成交规则与上述描述的过程相反,即触发价格随收盘价下跌而更新。

示例

策略:

  • 买入条件:收盘价高于15日均线时买入
  • 卖出条件:价格较进场后最高收盘价回撤3%时卖出
        # 检查是否持仓
        if self.position:
            # 检查是否达到卖出条件
            if self.buysell < 0 and self.p.exectype not in ['StopTrail']:
								...
            elif self.p.exectype in ['StopTrail']:
                st_order = self.sell(exectype=bt.Order.StopTrail,
                                trailamount=self.p.trailamount,
                                trailpercent=self.p.trailpercent)
                if self.p.trailamount:
                    check = self.data.close - self.p.trailamount
                else:
                    check = self.data.close * (1.0 - self.p.trailpercent)
                txt = 'SELL CREATE, exectype StopTrail, close %.2f, stop price %.2f, check price %.2f'
                self.log(txt % (self.data.close[0], st_order.created.price, check))

        # 不在场内且出现买入信号
        elif self.buysell > 0:
        		...
            if self.p.exectype in ['Market', 'StopTrail', 'StopTrailLimit']:
                self.buy(exectype=bt.Order.Market)  # Market是默认的订单类型
                self.log('BUY CREATE, exectype Market, close %.2f' %
                         self.data.close[0])

在代码中使用p.trailamount = 0,p.trailpercent = 0.03,来控制回撤幅度。

例1:

将图表显示时间范围调整为2019年3月,输出图形如下:
Python量化交易学习笔记(31)——backtrader的StopTrail订单_第1张图片

上图中,红色的曲线表示15日均线。

部分输出结果为:

2019-03-18, BUY CREATE, exectype Market, close 12.91
2019-03-19, ORDER SUBMITTED
2019-03-19, ORDER ACCEPTED
2019-03-19, Open: 12.92, High: 12.94, Low: 12.61, Close: 12.79
2019-03-19, BUY EXECUTED, Price: 12.92, Cost: 1292.00.
2019-03-19, SELL CREATE, exectype StopTrail, close 12.79, stop price 12.41, check price 12.41
2019-03-20, ORDER SUBMITTED
2019-03-20, ORDER ACCEPTED
2019-03-20, Open: 12.68, High: 12.88, Low: 12.62, Close: 12.75, Stop Price: 12.41, Check Price: 12.37
2019-03-21, Open: 12.77, High: 12.80, Low: 12.58, Close: 12.69, Stop Price: 12.41, Check Price: 12.31
2019-03-22, Open: 12.69, High: 12.74, Low: 12.50, Close: 12.59, Stop Price: 12.41, Check Price: 12.21
2019-03-25, Open: 12.40, High: 12.40, Low: 12.10, Close: 12.11
2019-03-25, SELL EXECUTED, Price: 12.40, Cost: 1292.00.

下面结合输出打印结果及图形进行分析。

(1) 3月18日,收盘价高于15日均线,达到了买入条件,创建买单。

(2) 3月19日,收到买单提交通知。(18日创建的订单,在19日收到订单状态通知,是因为notify_order方法会在Strategy的next方法前被调用,即在18日的next方法中创建了买单,在19日的notify_order方法中通知订单被提交、接受。)

(3) 3月19日,收到买单接受通知。

(4) 3月19日,买单成交,成交价格为12.92,与3月19日的开盘价相同。(Market订单以下一根K线的开盘价成交,即18日达到买入条件创建了订单,会以19日的开盘价成交。)

(5) 3月19日,创建卖单,订单类型为StopTrail,触发价格按照stopprice = price * (1 - trailpercent)计算,这里price值为当日收盘价12.79,trailpercent在程序中设定为0.03,计算得到触发价格stopprice为12.41。

(6) 3月20日,收到卖单提交通知。

(7) 3月20日,收到卖单接受通知。

(8) 3月20日,最低价12.62,最高价12.88,未达到触发价格stopprice值12.41。收盘价12.75,未高于进场后最高收盘价12.79,因此不更新触发价格12.41。

(9) 3月21日,最低价12.58,最高价12.80,未达到触发价格stopprice值12.41。收盘价12.69,未高于进场后最高收盘价12.79,因此不更新触发价格12.41。

(10) 3月22日,最低价12.50,最高价12.74,未达到触发价格stopprice值12.41。收盘价12.59,未高于进场后最高收盘价12.79,因此不更新触发价格12.41。

(11) 3月25日(23日和24日为周末,未开市),开盘价12.40,达到(低于)触发价格stopprice值12.41,将按照Stop订单的价格匹配规则执行。

(12) 3月25日,按照Stop订单的价格匹配规则,开盘价12.40低于触发价格12.41,就按照开盘价格12.40成交。

例2:

将图表显示时间范围调整为2019年6月,输出图形如下:
Python量化交易学习笔记(31)——backtrader的StopTrail订单_第2张图片
部分输出结果为:

2019-06-10, BUY CREATE, exectype Market, close 12.34
2019-06-11, ORDER SUBMITTED
2019-06-11, ORDER ACCEPTED
2019-06-11, Open: 12.34, High: 12.72, Low: 12.30, Close: 12.65
2019-06-11, BUY EXECUTED, Price: 12.34, Cost: 1234.00.
2019-06-11, SELL CREATE, exectype StopTrail, close 12.65, stop price 12.27, check price 12.27
2019-06-12, ORDER SUBMITTED
2019-06-12, ORDER ACCEPTED
2019-06-12, Open: 12.63, High: 12.66, Low: 12.44, Close: 12.57, Stop Price: 12.27, Check Price: 12.19
2019-06-13, Open: 12.54, High: 12.68, Low: 12.43, Close: 12.59, Stop Price: 12.27, Check Price: 12.21
2019-06-14, Open: 12.59, High: 12.69, Low: 12.45, Close: 12.49, Stop Price: 12.27, Check Price: 12.12
2019-06-17, Open: 12.48, High: 12.79, Low: 12.48, Close: 12.67, Stop Price: 12.29, Check Price: 12.29
2019-06-18, Open: 12.67, High: 12.85, Low: 12.59, Close: 12.80, Stop Price: 12.42, Check Price: 12.42
2019-06-19, Open: 13.29, High: 13.39, Low: 13.01, Close: 13.07, Stop Price: 12.68, Check Price: 12.68
2019-06-20, Open: 13.17, High: 13.95, Low: 13.12, Close: 13.80, Stop Price: 13.39, Check Price: 13.39
2019-06-21, Open: 13.76, High: 13.87, Low: 13.58, Close: 13.64, Stop Price: 13.39, Check Price: 13.23
2019-06-24, Open: 13.69, High: 13.83, Low: 13.61, Close: 13.69, Stop Price: 13.39, Check Price: 13.28
2019-06-25, Open: 13.72, High: 13.72, Low: 13.07, Close: 13.43
2019-06-25, SELL EXECUTED, Price: 13.39, Cost: 1234.00.

下面结合输出打印结果及图形进行分析。

(1) 6月10日,收盘价高于15日均线,达到了买入条件,创建买单。

(2) 6月11日,收到买单提交通知。

(3) 6月11日,收到买单接受通知。

(4) 6月11日,买单成交,成交价格为12.34,与6月11日的开盘价相同。

(5) 6月11日,创建卖单,订单类型为StopTrail,触发价格按照stopprice = price * (1 - trailpercent)计算,这里price值为当日收盘价12.65,trailpercent在程序中设定为0.03,计算得到触发价格stopprice为12.27。

(6) 6月12日,收到卖单提交通知。

(7) 6月12日,收到卖单接受通知。

(8) 6月12日,最低价12.44,最高价12.66,未达到触发价格stopprice值12.27。收盘价12.57,未高于进场后最高收盘价12.65,因此不更新触发价格12.27。

(9) 6月13日,最低价12.43,最高价12.68,未达到触发价格stopprice值12.27。收盘价12.59,未高于进场后最高收盘价12.65,因此不更新触发价格12.27。

(10) 6月14日,最低价12.45,最高价12.69,未达到触发价格stopprice值12.27。收盘价12.49,未高于进场后最高收盘价12.65,因此不更新触发价格12.27。

(11) 6月17日(15日和16日为周末,未开市),最低价12.48,最高价12.79,未达到触发价格stopprice值12.27。收盘价12.67,高于进场后最高收盘价12.65,因此需要更新触发价格。触发价格按照stopprice = price * (1 - trailpercent)计算,这里price值为当日收盘价12.67,trailpercent在程序中设定为0.03,计算得到新的触发价格stopprice为12.29。

(12) 6月18日,最低价12.59,最高价12.85,未达到触发价格stopprice值12.29。收盘价12.80,高于进场后最高收盘价12.67,因此需要更新触发价格。触发价格按照stopprice = price * (1 - trailpercent)计算,这里price值为当日收盘价12.80,trailpercent在程序中设定为0.03,计算得到新的触发价格stopprice为12.42。

(12) 6月19日,最低价13.01,最高价13.39,未达到触发价格stopprice值12.42。收盘价13.07,高于进场后最高收盘价12.80,因此需要更新触发价格。触发价格按照stopprice = price * (1 - trailpercent)计算,这里price值为当日收盘价13.07,trailpercent在程序中设定为0.03,计算得到新的触发价格stopprice为12.68。

(13) 6月20日,最低价13.12,最高价13.95,未达到触发价格stopprice值12.68。收盘价13.80,高于进场后最高收盘价13.07,因此需要更新触发价格。触发价格按照stopprice = price * (1 - trailpercent)计算,这里price值为当日收盘价13.80,trailpercent在程序中设定为0.03,计算得到新的触发价格stopprice为13.39。

(14) 6月21日,最低价13.58,最高价13.87,未达到触发价格stopprice值13.39。收盘价13.64,未高于进场后最高收盘价13.80,因此不更新触发价格13.39。

(15) 6月24日(22日和23日为周末,未开市),最低价13.61,最高价13.83,未达到触发价格stopprice值13.39。收盘价13.69,未高于进场后最高收盘价13.80,因此不更新触发价格13.39。

(16) 6月25日,开盘价13.72高于触发价格stopprice值13.39,但最低点13.07低于触发价格13.39,订单被触发,将按照Stop订单的价格匹配规则执行。

(17) 6月25日,按照Stop订单的价格匹配规则,开盘价13.72高于触发价格stopprice值13.39,最低点13.07低于触发价格13.39,就按照stopprice值13.39成交。(验证了官网文档里说的StopTrail订单触发后按Market订单规则执行是错误的。)

总结

  • 在backtrader源代码中,StopTrail订单与Stop订单实现逻辑基本一致,区别在于Stop订单的触发价格为固定值,而StopTrail订单会根据收盘价的变化更新触发价格。
  • StopTrail订单可以发挥保护利润的作用,保护点(触发价格)会随着价格的上升不断更新,不断提高,一旦价格跌破保护点,就会进行卖出,实现利润的有效保护。同时可以看到,即使价格一直下跌,StopTrail订单也可以有效止损。

为了便于相互交流学习,新建了微信群,感兴趣的读者请加微信。
在这里插入图片描述

你可能感兴趣的:(Python量化交易)