天天看點

Python量化交易學習筆記(32)——backtrader的StopTrailLimit訂單

本文将繼續對backtrader的order進行介紹,具體介紹StopTrailLimit訂單的使用。

在backtrader的官方文檔中,對StopTrailLimit的介紹一筆帶過,隻是提到StopTrailLimit和StopTrail訂單差別僅是訂單被觸發後的表現不同,但實際上通過分析源代碼可以發現StopTrailLimit訂單與StopTrail訂單差別還是比較大的。

其中,是否指定參數中的price值,StopTrailLimit會産生大不相同的兩種訂單執行邏輯。目前還不知道這是backtrader有意為之,還是實作出現了問題。下面分别就指定參數price值及不指定參數price值分開讨論(這裡假定限制價格plimit已指定,不指定plimit又是另一個故事了,考慮到我們是分析StopTrailLimit訂單,是以假定plimit已指定)。

在之前筆記中分析過,Limit類型的訂單不适合做平倉操作,因為可能造成長時間無法平倉,而造成重大的損失。是以,以下示例均使用buy方法進行建倉來示範StopTrailLimit訂單的執行邏輯。

指定參數price值

1. 訂單邏輯

  • 當指定參數price值時,訂單的執行邏輯與StopLimit訂單相似。二者的差別在于,在StopLimit訂單中,觸發價格stopprice和限制價格limitprice均為固定值,而在StopTrailLimit訂單中觸發價格stopprice和限制價格limitprice都會随着收盤價的變化進行變化。
  • 在StopTrailLimit訂單中,在觸發價格與限制價格更新的同時,會檢測新的K線是否滿足目前觸發價格與限制價格條件下StopLimit訂單的執行邏輯,若已滿足則按照StopLimit訂單的規則執行訂單,即達到觸發價格後,再按照StopLimit訂單的邏輯執行訂單。

2. 初始化

  • 在初始化階段,使用指定的price值及跟蹤比例trailpercent來計算觸發價格,即stopprice = price * (1 + trailpercent)
  • 然後使用指定的price值及plimit值計算一個偏內插補點limitoffset = price - plimit
  • 最後使用limitoffset及觸發價格stopprice來更新限制價格limitprice = stopprice -

    limitoffset。後續在更新觸發價格stopprice及限制價格limitprice時,始終保持二者存在這一固定的內插補點limitoffset。

3. 價格更新與訂單執行

  • 按照Stop訂單規則判斷訂單已被觸發,則按照StopLimit訂單規則執行訂單
  • 按照Stop訂單規則判斷訂單未被觸發
    • 如果收盤價高于(或等于)StopTrailLimit買單建立後的最低收盤價及初始指定的price值,則不更新觸發價格stopprice及限制價格limitprcie
    • 如果收盤價低于StopTrailLimit買單建立後的最低收盤價或初始指定的price值,則更新觸發價格stopprice及限制價格limitprcie。觸發價格stopprice = price * (1 + trailpercent),其中price為所在K線的收盤價,trailpercent為給定的跟蹤比例。限制價格limitprice = stopprice - limitoffset,limitprice為之前計算的觸發價格及限制價格之間的固定內插補點。

4. 示例

政策:

  • 買入條件:收盤價高于15日均線後,建立StopTrailLimit買單,當買單建立後,價格較最低收盤價向上突破3%,并回撤買單建立當日收盤價的2%時買入
  • 賣出條件:收盤價低于15日均線賣出
if self.order:
						...
            elif self.order.isbuy() and self.p.exectype in ['StopTrailLimit']:
                if self.p.trailamount:
                    check = self.data.close + self.p.trailamount
                else:
                    check = self.data.close * (1.0 + self.p.trailpercent)
                self.log('Open: %.2f, High: %.2f, Low: %.2f, Close: %.2f, Stop Price: %.2f, Check Price: %.2f, Limit Price: %.2f' %
                    (self.data.open[0],
                    self.data.high[0],
                    self.data.low[0], 
                    self.data.close[0],
                    self.order.created.price,
                    check,
                    self.order.created.pricelimit
                    ))

        # 檢查是否持倉
        if self.position:
            # 檢查是否達到賣出條件
            if self.buysell < 0 and self.p.exectype not in ['StopTrail']:
								...
                if self.p.exectype in ['Market', 'StopTrailLimit']:
                    self.sell(exectype = bt.Order.Market)
                    self.log('SELL CREATE, exectype Market, close %.2f' % 
                             self.data.close[0])
               ...
        # 不在場内且出現買入信号
        elif self.buysell > 0:
						...
            elif 'StopTrailLimit' == self.p.exectype:
                    price = self.data.close[0]
                    plimit = self.data.close[0] * (1.0 - self.p.traillimit)
                    st_order = self.buy(exectype=bt.Order.StopTrailLimit,
                                   trailamount=self.p.trailamount,
                                   trailpercent=self.p.trailpercent,
                                   price = price,
                                   plimit = plimit)
                    if self.p.trailamount:
                        check = self.data.close + self.p.trailamount
                    else:
                        check = self.data.close * (1.0 + self.p.trailpercent)
                        #check = plimit * (1.0 + self.p.trailpercent)
                    txt = 'BUY CREATE, exectype StopTrailLimit, close %.2f, stop price %.2f, check price %.2f, limit price %.2f'
                    self.log(txt % (self.data.close[0], st_order.created.price, check, st_order.created.pricelimit))
           

将圖表顯示時間範圍調整為2019年4、5月,輸出圖形如下:

Python量化交易學習筆記(32)——backtrader的StopTrailLimit訂單

上圖中,紅色的曲線表示15日均線。

部分輸出結果為:

2019-04-29, BUY CREATE, exectype StopTrailLimit, close 14.10, stop price 14.52, check price 14.52, limit price 14.24
2019-04-30, ORDER SUBMITTED
2019-04-30, ORDER ACCEPTED
2019-04-30, Open: 13.99, High: 14.05, Low: 13.59, Close: 13.85, Stop Price: 14.27, Check Price: 14.27, Limit Price: 13.98
2019-05-06, Open: 13.10, High: 13.35, Low: 12.71, Close: 12.87, Stop Price: 13.26, Check Price: 13.26, Limit Price: 12.97
2019-05-07, Open: 13.03, High: 13.09, Low: 12.72, Close: 12.95, Stop Price: 13.26, Check Price: 13.34, Limit Price: 12.97
2019-05-08, Open: 12.72, High: 12.91, Low: 12.50, Close: 12.60, Stop Price: 12.98, Check Price: 12.98, Limit Price: 12.70
2019-05-09, Open: 12.52, High: 12.58, Low: 12.05, Close: 12.16, Stop Price: 12.52, Check Price: 12.52, Limit Price: 12.24
2019-05-10, Open: 12.34, High: 12.75, Low: 12.10, Close: 12.68, Stop Price: 12.52, Check Price: 13.06, Limit Price: 12.24
2019-05-13, Open: 12.33, High: 12.54, Low: 12.23, Close: 12.30
2019-05-13, BUY EXECUTED, Price: 12.24, Cost: 1224.28.
           

下面結合輸出列印結果及圖形進行分析。

(1) 4月29日,收盤價高于15日均線,達到了買入條件,建立StopTrailLimit買單。當日收盤價close(也是設定的price值)為14.10,向上突破3%後的觸發價格stop price為14.52,較收盤價回撤2%指定的限制價格plimit為13.82,記錄price值(收盤價)與plimit間的偏內插補點limitoffset = price - plimit = 0.28,使用偏內插補點更新限制價格limitprice = stopprice - limitoffset = 14.24。

(2) 4月30日,收到買單送出通知。(29日建立的訂單,在30日收到訂單狀态通知,是因為notify_order方法會在Strategy的next方法前被調用,即在29日的next方法中建立了買單,在30日的notify_order方法中通知訂單被送出、接受。)

(3) 4月30日,收到買單接受通知。

(4) 4月30日,最低價13.59,最高價14.05,未達到觸發價格stopprice值14.52。收盤價13.85,低于買單建立後最低收盤價14.10,是以需要更新觸發價格。觸發價格按照stopprice = price * (1 + trailpercent)計算,這裡price值為當日收盤價13.85,trailpercent在程式中設定為0.03,計算得到新的觸發價格stopprice為14.27。需要同步更新限制價格limitprice = stopprice - limitoffset = 13.98。(按照小數點後2位計算的結果應該為13.99,但實際計算是精确到小數點後三位,我們這裡列印小數點後2位)

(5) 5月6日(5月1日至5日為假期,未開市),最低價12.71,最高價13.35,未達到觸發價格stopprice值14.27。收盤價12.87,低于買單建立後最低收盤價13.85,是以需要更新觸發價格。觸發價格按照stopprice = price * (1 + trailpercent)計算,這裡price值為當日收盤價12.87,trailpercent在程式中設定為0.03,計算得到新的觸發價格stopprice為13.26。需要同步更新限制價格limitprice = stopprice - limitoffset = 12.97。(按照小數點後2位計算的結果應該為12.98,但實際計算是精确到小數點後三位,我們這裡列印小數點後2位)

(6) 5月7日,最低價12.72,最高價13.09,未達到觸發價格stop price值13.26。收盤價12.95,未低于買單建立後最低收盤價12.87,是以不更新觸發價格13.26和限制價格12.97。

(7) 5月8日,最低價12.50,最高價12.91,未達到觸發價格stopprice值13.26。收盤價12.60,低于買單建立後最低收盤價12.87,是以需要更新觸發價格。觸發價格按照stopprice = price * (1 + trailpercent)計算,這裡price值為當日收盤價12.60,trailpercent在程式中設定為0.03,計算得到新的觸發價格stop price為12.98。需要同步更新限制價格limitprice = stopprice - limitoffset = 12.70。

(8) 5月9日,最低價12.05,最高價12.58,未達到觸發價格stopprice值12.98。收盤價12.16,低于買單建立後最低收盤價12.60,是以需要更新觸發價格。觸發價格按照stopprice = price * (1 + trailpercent)計算,這裡price值為當日收盤價12.16,trailpercent在程式中設定為0.03,計算得到新的觸發價格stop price為12.52。需要同步更新限制價格limitprice = stopprice - limitoffset = 12.24。

(9) 5月10日,最低價12.10,最高價12.75,達到觸發價格stopprice值12.52,按照StopLimit訂單規則,訂單被觸發,訂單将以限制價格limitprice12.24按照Limit訂單規則被執行。(無法判斷5月10日的K線價格在達到觸發價格後,能否回撤到限制價格,是以将在下一日K線上判斷訂單是否成交。)

(10) 5月13日(11日和12日為周末,未開市),開盤價為12.33,高于限制價格limitprice值12.24,不滿足Limit訂單價格比對的規則1;但是最低價為12.23,低于限制價格limitprice值12.24,滿足Limit訂單價格比對的規則2,是以将以limitprice值成交。

(11) 5月13日,買單以limitprice值12.24成交。

通過這個例子可以發現,使用StopTrailLimit訂單進行買入,避免了假突破(突破15日均線)買入的情況發生。

不指定參數price值

1. 訂單邏輯

當不指定參數price值時,訂單的執行邏輯與指定參數price值相似。二者的差別在于:

  • 若不指定price值,StopTrailLimit訂單将以指定的plimit值初始化price值。
  • 若不指定price值,在觸發價格stopprice和限制價格limitprice的更新過程中,二者始終保持相等。

2. 初始化

  • 在初始化階段,将指定的plimit值初始化為price值,即price = plimit
  • 使用price值計算觸發價格stopprice = price * (1 + trailpercent)
  • 設定限制價格與觸發價格相等,即limitprice = stopprice (在bt源代碼中,實際上與指定price值的情況相同,也使用了偏內插補點limitoffset來計算limitprice = stopprice - limitoffset,不過在不指定price值的情況下,limitoffset = 0,是以觸發價格stopprice與限制價格limitprice始終相等。)

3. 價格更新與訂單執行

  • 按照Stop訂單規則判斷訂單已被觸發,則按照StopLimit訂單規則執行訂單
  • 按照Stop訂單規則判斷訂單未被觸發
    • 如果收盤價高于(或等于)StopTrailLimit買單建立後的最低收盤價及初始指定的price值,則不更新觸發價格stopprice及限制價格limitprcie
    • 如果收盤價低于StopTrailLimit買單建立後的最低收盤價或初始指定的price值,則更新觸發價格stopprice及限制價格limitprcie。觸發價格stopprice = price * (1 + trailpercent),其中price為所在K線的收盤價,trailpercent為給定的跟蹤比例。限制價格limitprice = stopprice (limitoffset = 0)。

4. 示例

政策:

  • 買入條件:收盤價高于15日均線後,建立StopTrailLimit買單,當買單建立後,價格較最低收盤價(及指定價格中的較小者)向上突破3%時買入
  • 賣出條件:收盤價低于15日均線賣出
...
            elif 'StopTrailLimit' == self.p.exectype:
                    price = self.data.close[0]
                    plimit = self.data.close[0] * (1.0 - self.p.traillimit)
                    st_order = self.buy(exectype=bt.Order.StopTrailLimit,
                                   trailamount=self.p.trailamount,
                                   trailpercent=self.p.trailpercent,
                                   #price = price,
                                   plimit = plimit)
                    if self.p.trailamount:
                        check = self.data.close + self.p.trailamount
                    else:
                        # 指定price值
                        #check = self.data.close * (1.0 + self.p.trailpercent)
                        # 不指定price值
                        check = plimit * (1.0 + self.p.trailpercent)
                    txt = 'BUY CREATE, exectype StopTrailLimit, close %.2f, stop price %.2f, check price %.2f, limit price %.2f'
                    self.log(txt % (self.data.close[0], st_order.created.price, check, st_order.created.pricelimit))
...
           

實作代碼與指定參數price值的差別是,在調用buy方法時,注釋掉了對price參數的指派。

為了便于對比說明,示例依然以4月29日建立的買單為例。将圖表顯示時間範圍調整為2019年4、5月,輸出圖形如下:

Python量化交易學習筆記(32)——backtrader的StopTrailLimit訂單

對應的部分輸出結果為:

2019-04-29, BUY CREATE, exectype StopTrailLimit, close 14.10, stop price 14.23, check price 14.23, limit price 14.23
2019-04-30, ORDER SUBMITTED
2019-04-30, ORDER ACCEPTED
2019-04-30, Open: 13.99, High: 14.05, Low: 13.59, Close: 13.85, Stop Price: 14.23, Check Price: 14.27, Limit Price: 14.23
2019-05-06, Open: 13.10, High: 13.35, Low: 12.71, Close: 12.87, Stop Price: 13.26, Check Price: 13.26, Limit Price: 13.26
2019-05-07, Open: 13.03, High: 13.09, Low: 12.72, Close: 12.95, Stop Price: 13.26, Check Price: 13.34, Limit Price: 13.26
2019-05-08, Open: 12.72, High: 12.91, Low: 12.50, Close: 12.60, Stop Price: 12.98, Check Price: 12.98, Limit Price: 12.98
2019-05-09, Open: 12.52, High: 12.58, Low: 12.05, Close: 12.16, Stop Price: 12.52, Check Price: 12.52, Limit Price: 12.52
2019-05-10, Open: 12.34, High: 12.75, Low: 12.10, Close: 12.68
2019-05-10, BUY EXECUTED, Price: 12.52, Cost: 1252.48.
           

下面結合輸出列印結果及圖形進行分析。

(1) 4月29日,收盤價高于15日均線,達到了買入條件,建立StopTrailLimit買單。當日收盤價close為14.10,不指定price的值,将以指定的plimit值初始化為price值,即price = plimit,使用price值計算觸發價格stopprice = price * (1 + trailpercent) = 14.23,設定限制價格與觸發價格相等,即limitprice = stopprice = 14.23。(在bt源代碼中,實際上與指定price值的情況相同,也使用了偏內插補點limitoffset來計算limitprice = stopprice - limitoffset,不過在不指定price值的情況下,limitoffset = 0,是以觸發價格stopprice與限制價格limitprice始終相等。)

(2) 4月30日,收到買單送出通知。(29日建立的訂單,在30日收到訂單狀态通知,是因為notify_order方法會在Strategy的next方法前被調用,即在29日的next方法中建立了買單,在30日的notify_order方法中通知訂單被送出、接受。)

(3) 4月30日,收到買單接受通知。

(4) 4月30日,最低價13.59,最高價14.05,未達到觸發價格stopprice值14.23。收盤價13.85,未低于買單建立後初始化price值13.82,是以不更新觸發價格14.23和限制價格14.23。

(5) 5月6日(5月1日至5日為假期,未開市),最低價12.71,最高價13.35,未達到觸發價格stopprice值14.23。收盤價12.87,低于買單建立後初始化price值13.82,是以需要更新觸發價格。觸發價格按照stopprice = price * (1 + trailpercent)計算,這裡price值為當日收盤價12.87,trailpercent在程式中設定為0.03,計算得到新的觸發價格stopprice為13.26。需要同步更新限制價格limitprice = stopprice - limitoffset = 13.26(limitoffset = 0)。

(6) 5月7日,最低價12.72,最高價13.09,未達到觸發價格stop price值13.26。收盤價12.95,未低于買單建立後最低收盤價12.87,是以不更新觸發價格13.26和限制價格13.26。

(7) 5月8日,最低價12.50,最高價12.91,未達到觸發價格stopprice值13.26。收盤價12.60,低于買單建立後最低收盤價12.87,是以需要更新觸發價格。觸發價格按照stopprice = price * (1 + trailpercent)計算,這裡price值為當日收盤價12.60,trailpercent在程式中設定為0.03,計算得到新的觸發價格stop price為12.98。需要同步更新限制價格limitprice = stopprice - limitoffset = 12.98。

(8) 5月9日,最低價12.05,最高價12.58,未達到觸發價格stopprice值12.98。收盤價12.16,低于買單建立後最低收盤價12.60,是以需要更新觸發價格。觸發價格按照stopprice = price * (1 + trailpercent)計算,這裡price值為當日收盤價12.16,trailpercent在程式中設定為0.03,計算得到新的觸發價格stop price為12.52。需要同步更新限制價格limitprice = stopprice - limitoffset = 12.52。

(9) 5月10日,最低價12.10,最高價12.75,達到觸發價格stopprice值12.52,訂單被觸發,訂單以限制價格limitprice12.52,按照觸發後的StopLimit訂單規則被執行。對于該日open(12.34) < stopprice(12.52) & high(12.75) > stopprice(12.52) & open(12.34) < (12.68) & limitprice(12.52) >= stopprice(12.52) 的情況,将以stopprice值12.52成交(具體邏輯參考源碼)。

(10) 5月13日,買單以stopprice值12.52成交。

通過這個例子可以看到StopTrailLimit訂單在指定與不指定參數price值的情況下存在着一定的差異表現。在不指定參數price值的情況下,由于stopprice與limitprice一直保持相等,也就是說訂單一旦被觸發,限制價格limitprice也立即可以達到,訂單立即成交,訂單的limit(回撤建倉)作用就無法發揮出來。

總結

  • 在backtrader源代碼中,StopTrailLimit訂單與StopLimit訂單實作邏輯基本一緻,差別在于StopLimit訂單的觸發價格為固定值,而StopTrailLimit訂單會根據收盤價的變化更新觸發價格。
  • StopTrialLimit訂單進行建倉時,可以實作突破回踩建倉的效果,并在一定程度上避免假突破造成高位建倉的情況。
  • StopTrailLimit訂單在建立時指定與不指定參數price值,訂單執行時表現存在一定差異(個人感覺這又是個bug)。

為了便于互相交流學習,建立了微信群,感興趣的讀者請加微信。

Python量化交易學習筆記(32)——backtrader的StopTrailLimit訂單