Backtrader教程:數據饋送 - 展期交割

  |  

並非每個供應商都為可以交易的工具提供連續的未來。有時提供的數據是仍然有效的到期日期的數據,即:仍在交易的日期

這在回溯測試方面並不是很有幫助,因為數據分散在幾個不同的儀器上,這些儀器另外...時間重疊。

能夠正確地將這些儀器的數據從過去連接到連續的流中,可以減輕疼痛。問題:

  • 沒有關於如何最好地將不同的到期日期加入連續未來的法律。

一些文獻,由SierraChart提供,

展期 Data Feed

backtrader 增加了1.8.10.99 將不同到期日的期貨數據聯接到連續未來的可能性:

import backtrader as bt

cerebro = bt.Cerebro()
data0 = bt.feeds.MyFeed(dataname='Expiry0')
data1 = bt.feeds.MyFeed(dataname='Expiry1')
...
dataN = bt.feeds.MyFeed(dataname='ExpiryN')

drollover = cerebro.rolloverdata(data0, data1, ..., dataN, name='MyRoll', **kwargs)

cerebro.run()

注意

下面解釋可能**kwargs

也可以通過直接存取RollOver 源來完成此操作(如果子類化完成,這將很有説明):

import backtrader as bt

cerebro = bt.Cerebro()
data0 = bt.feeds.MyFeed(dataname='Expiry0')
data1 = bt.feeds.MyFeed(dataname='Expiry1')
...
dataN = bt.feeds.MyFeed(dataname='ExpiryN')

drollover = bt.feeds.RollOver(data0, data1, ..., dataN, dataname='MyRoll', **kwargs)
cerebro.adddata(drollover)

cerebro.run()

注意

下面解釋可能**kwargs

注意

使用RollOver 時,使用 dataname指定名稱。這是用於傳遞名稱/股票代碼的所有 data feeds 的標準參數。在這種情況下,它將被重用,為完整的展期期貨集分配一個通用名稱。

在這種情況下cerebro.rolloverdata,使用 將名稱分配給源,該源 name已經是該方法的一個命名參數

結論:

  • Data Feeds 像往常一樣創建,但不會添加到cerebro

  • 這些 data feeds 作為輸入提供給bt.feeds.RollOver

    還給出了Adataname ,主要用於識別目的。

  • 然後將此翻轉 data feed 添加到cerebro

展期選項

提供兩個參數來控制展期過程

  • checkdate (預設值: None

    這必須是具有以下簽名的可調用項:

    checkdate(dt, d):
    

    哪裡:

    • dt 是一個 datetime.datetime 物件

    • d是活躍未來的當前data feed

    預期返回值:

    • True:只要可調用返回此值,切換即可在下一個將來發生

      如果商品在3月的第3個星期 五到期, checkdate 則可以在到期發生的整個星期內返回 True

    • False:無法進行過期

  • checkcondition (預設值: None

    注意

    僅當已返回時checkdate 才會調用此函數 True

    如果None 這將在 True 內部評估(執行滾動更新)

    否則,這必須是具有以下簽名的可調用項:

    checkcondition(d0, d1)
    

    哪裡:

    • d0是活躍未來的當前data feed

    • d1是下一個到期日的data feed

    預期返回值:

    • True:展期到下一個未來

      按照from的示例checkdate,這可以說,僅當 volume from d0 已經小於from的 volume 時,才會發生翻轉。 d1

    • False:無法進行過期

子類RollOver

如果指定可調用對象還不夠,則始終有機會進行子類化RollOver。要子類化的方法:

  • def _checkdate(self, dt, d):

    這與上面同名參數的簽名相匹配。預期的返回值也是 saame。

  • def _checkcondition(self, d0, d1)

    這與上面同名參數的簽名相匹配。預期的返回值也是 saame。

讓我們滾吧

注意

範例中的預設行為是使用cerebro.rolloverdata。這可以通過傳遞 -no-cerebro 標誌來更改。在這種情況下,示例使用 RollOvercerebro.adddata

該實現包括一個示例,該示例在 backtrader 源中提供。

期貨串聯

讓我們首先通過運行沒有參數的示例來查看純串聯。

$ ./rollover.py

Len, Name, RollName, Datetime, WeekDay, Open, High, Low, Close, Volume, OpenInterest
0001, FESX, 199FESXM4, 2013-09-26, Thu, 2829.0, 2843.0, 2829.0, 2843.0, 3.0, 1000.0
0002, FESX, 199FESXM4, 2013-09-27, Fri, 2842.0, 2842.0, 2832.0, 2841.0, 16.0, 1101.0
...
0176, FESX, 199FESXM4, 2014-06-20, Fri, 3315.0, 3324.0, 3307.0, 3322.0, 134777.0, 520978.0
0177, FESX, 199FESXU4, 2014-06-23, Mon, 3301.0, 3305.0, 3265.0, 3285.0, 730211.0, 3003692.0
...
0241, FESX, 199FESXU4, 2014-09-19, Fri, 3287.0, 3308.0, 3286.0, 3294.0, 144692.0, 566249.0
0242, FESX, 199FESXZ4, 2014-09-22, Mon, 3248.0, 3263.0, 3231.0, 3240.0, 582077.0, 2976624.0
...
0306, FESX, 199FESXZ4, 2014-12-19, Fri, 3196.0, 3202.0, 3131.0, 3132.0, 226415.0, 677924.0
0307, FESX, 199FESXH5, 2014-12-22, Mon, 3151.0, 3177.0, 3139.0, 3168.0, 547095.0, 2952769.0
...
0366, FESX, 199FESXH5, 2015-03-20, Fri, 3680.0, 3698.0, 3672.0, 3695.0, 147632.0, 887205.0
0367, FESX, 199FESXM5, 2015-03-23, Mon, 3654.0, 3655.0, 3608.0, 3618.0, 802344.0, 3521988.0
...
0426, FESX, 199FESXM5, 2015-06-18, Thu, 3398.0, 3540.0, 3373.0, 3465.0, 1173246.0, 811805.0
0427, FESX, 199FESXM5, 2015-06-19, Fri, 3443.0, 3499.0, 3440.0, 3488.0, 104096.0, 516792.0

這個用途cerebro.chaindata 和結果應該是明確的:

  • 每當一個 data feed 結束時,下一個就會接管

  • 這總是發生在週五和周一之間:樣本中的期貨總是在週五到期

期貨展期,無需支票

讓我們執行--rollover

$ ./rollover.py --rollover --plot

Len, Name, RollName, Datetime, WeekDay, Open, High, Low, Close, Volume, OpenInterest
0001, FESX, 199FESXM4, 2013-09-26, Thu, 2829.0, 2843.0, 2829.0, 2843.0, 3.0, 1000.0
0002, FESX, 199FESXM4, 2013-09-27, Fri, 2842.0, 2842.0, 2832.0, 2841.0, 16.0, 1101.0
...
0176, FESX, 199FESXM4, 2014-06-20, Fri, 3315.0, 3324.0, 3307.0, 3322.0, 134777.0, 520978.0
0177, FESX, 199FESXU4, 2014-06-23, Mon, 3301.0, 3305.0, 3265.0, 3285.0, 730211.0, 3003692.0
...
0241, FESX, 199FESXU4, 2014-09-19, Fri, 3287.0, 3308.0, 3286.0, 3294.0, 144692.0, 566249.0
0242, FESX, 199FESXZ4, 2014-09-22, Mon, 3248.0, 3263.0, 3231.0, 3240.0, 582077.0, 2976624.0
...
0306, FESX, 199FESXZ4, 2014-12-19, Fri, 3196.0, 3202.0, 3131.0, 3132.0, 226415.0, 677924.0
0307, FESX, 199FESXH5, 2014-12-22, Mon, 3151.0, 3177.0, 3139.0, 3168.0, 547095.0, 2952769.0
...
0366, FESX, 199FESXH5, 2015-03-20, Fri, 3680.0, 3698.0, 3672.0, 3695.0, 147632.0, 887205.0
0367, FESX, 199FESXM5, 2015-03-23, Mon, 3654.0, 3655.0, 3608.0, 3618.0, 802344.0, 3521988.0
...
0426, FESX, 199FESXM5, 2015-06-18, Thu, 3398.0, 3540.0, 3373.0, 3465.0, 1173246.0, 811805.0
0427, FESX, 199FESXM5, 2015-06-19, Fri, 3443.0, 3499.0, 3440.0, 3488.0, 104096.0, 516792.0

相同的行為。可以清楚地看到,合同變更是在3月,6月,9月,12月的第三 個星期五進行的。

但這大多是錯誤的。 backtrader 不知道,但作者知道EuroStoxx 50期貨在12:00 CET停止交易。因此,即使到期月份的第 3個星期五有每日柱線,這種變化也為時已晚。

一周內的變化

checkdate示例中實現了可調用,該示例計算當前活動合約的到期日期。

checkdate 一旦達到當月 3個星期五的那一周,將允許展期(例如,如果星期一是銀行假日,則可能是星期二)

$ ./rollover.py --rollover --checkdate --plot

Len, Name, RollName, Datetime, WeekDay, Open, High, Low, Close, Volume, OpenInterest
0001, FESX, 199FESXM4, 2013-09-26, Thu, 2829.0, 2843.0, 2829.0, 2843.0, 3.0, 1000.0
0002, FESX, 199FESXM4, 2013-09-27, Fri, 2842.0, 2842.0, 2832.0, 2841.0, 16.0, 1101.0
...
0171, FESX, 199FESXM4, 2014-06-13, Fri, 3283.0, 3292.0, 3253.0, 3276.0, 734907.0, 2715357.0
0172, FESX, 199FESXU4, 2014-06-16, Mon, 3261.0, 3275.0, 3252.0, 3262.0, 180608.0, 844486.0
...
0236, FESX, 199FESXU4, 2014-09-12, Fri, 3245.0, 3247.0, 3220.0, 3232.0, 650314.0, 2726874.0
0237, FESX, 199FESXZ4, 2014-09-15, Mon, 3209.0, 3224.0, 3203.0, 3221.0, 153448.0, 983793.0
...
0301, FESX, 199FESXZ4, 2014-12-12, Fri, 3127.0, 3143.0, 3038.0, 3042.0, 1409834.0, 2934179.0
0302, FESX, 199FESXH5, 2014-12-15, Mon, 3041.0, 3089.0, 2963.0, 2980.0, 329896.0, 904053.0
...
0361, FESX, 199FESXH5, 2015-03-13, Fri, 3657.0, 3680.0, 3627.0, 3670.0, 867678.0, 3499116.0
0362, FESX, 199FESXM5, 2015-03-16, Mon, 3594.0, 3641.0, 3588.0, 3629.0, 250445.0, 1056099.0
...
0426, FESX, 199FESXM5, 2015-06-18, Thu, 3398.0, 3540.0, 3373.0, 3465.0, 1173246.0, 811805.0
0427, FESX, 199FESXM5, 2015-06-19, Fri, 3443.0, 3499.0, 3440.0, 3488.0, 104096.0, 516792.0

好多了。現在,翻滾發生在5天前。對Len指數的快速目視檢查顯示出來。例如:

  • 199FESXM4 發生在 199FESXU4 len 171-172.沒有 checkdate 它發生在 176-177

展期在到期月份的第 三個星期五之前的星期一進行。

添加 volume 條件

即使有所改善,情況也可以進一步改善,因為不僅日期,而且 volume 都將得到考慮。當新合約的交易量比當前活躍的合約 volume 時,請切換。

讓我們將 acheckcondition 新增到組合中並運行。

$ ./rollover.py --rollover --checkdate --checkcondition --plot

Len, Name, RollName, Datetime, WeekDay, Open, High, Low, Close, Volume, OpenInterest
0001, FESX, 199FESXM4, 2013-09-26, Thu, 2829.0, 2843.0, 2829.0, 2843.0, 3.0, 1000.0
0002, FESX, 199FESXM4, 2013-09-27, Fri, 2842.0, 2842.0, 2832.0, 2841.0, 16.0, 1101.0
...
0175, FESX, 199FESXM4, 2014-06-19, Thu, 3307.0, 3330.0, 3300.0, 3321.0, 717979.0, 759122.0
0176, FESX, 199FESXU4, 2014-06-20, Fri, 3309.0, 3318.0, 3290.0, 3298.0, 711627.0, 2957641.0
...
0240, FESX, 199FESXU4, 2014-09-18, Thu, 3249.0, 3275.0, 3243.0, 3270.0, 846600.0, 803202.0
0241, FESX, 199FESXZ4, 2014-09-19, Fri, 3273.0, 3293.0, 3250.0, 3252.0, 1042294.0, 3021305.0
...
0305, FESX, 199FESXZ4, 2014-12-18, Thu, 3095.0, 3175.0, 3085.0, 3172.0, 1309574.0, 889112.0
0306, FESX, 199FESXH5, 2014-12-19, Fri, 3195.0, 3200.0, 3106.0, 3147.0, 1329040.0, 2964538.0
...
0365, FESX, 199FESXH5, 2015-03-19, Thu, 3661.0, 3691.0, 3646.0, 3668.0, 1271122.0, 1054639.0
0366, FESX, 199FESXM5, 2015-03-20, Fri, 3607.0, 3664.0, 3595.0, 3646.0, 1182235.0, 3407004.0
...
0426, FESX, 199FESXM5, 2015-06-18, Thu, 3398.0, 3540.0, 3373.0, 3465.0, 1173246.0, 811805.0
0427, FESX, 199FESXM5, 2015-06-19, Fri, 3443.0, 3499.0, 3440.0, 3488.0, 104096.0, 516792.0

甚至更好。我們已將切換日期移至到期月份眾所周知的第 3 個星期五之前的星期四

這應該不足為奇,因為到期的未來交易在週五的小時數要少得多,而且 volume 必須很小。

注意

展期日期也可以由可調用者設置為該checkdate 星期四。但這不是樣本的重點。

總結

backtrader 現在包括一個靈活的機制,允許展期期貨以創建連續流。

示例用法

$ ./rollover.py --help
usage: rollover.py [-h] [--no-cerebro] [--rollover] [--checkdate]
                   [--checkcondition] [--plot [kwargs]]

Sample for Roll Over of Futures

optional arguments:
  -h, --help            show this help message and exit
  --no-cerebro          Use RollOver Directly (default: False)
  --rollover
  --checkdate           Change during expiration week (default: False)
  --checkcondition      Change when a given condition is met (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
import bisect
import calendar
import datetime

import backtrader as bt


class TheStrategy(bt.Strategy):
    def start(self):
        header = ['Len', 'Name', 'RollName', 'Datetime', 'WeekDay', 'Open',
                  'High', 'Low', 'Close', 'Volume', 'OpenInterest']
        print(', '.join(header))

    def next(self):
        txt = list()
        txt.append('%04d' % len(self.data0))
        txt.append('{}'.format(self.data0._dataname))
        # Internal knowledge ... current expiration in use is in _d
        txt.append('{}'.format(self.data0._d._dataname))
        txt.append('{}'.format(self.data.datetime.date()))
        txt.append('{}'.format(self.data.datetime.date().strftime('%a')))
        txt.append('{}'.format(self.data.open[0]))
        txt.append('{}'.format(self.data.high[0]))
        txt.append('{}'.format(self.data.low[0]))
        txt.append('{}'.format(self.data.close[0]))
        txt.append('{}'.format(self.data.volume[0]))
        txt.append('{}'.format(self.data.openinterest[0]))
        print(', '.join(txt))


def checkdate(dt, d):
    # Check if the date is in the week where the 3rd friday of Mar/Jun/Sep/Dec

    # EuroStoxx50 expiry codes: MY
    # M -> H, M, U, Z (Mar, Jun, Sep, Dec)
    # Y -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 -> year code. 5 -> 2015
    MONTHS = dict(H=3, M=6, U=9, Z=12)

    M = MONTHS[d._dataname[-2]]

    centuria, year = divmod(dt.year, 10)
    decade = centuria * 10

    YCode = int(d._dataname[-1])
    Y = decade + YCode
    if Y < dt.year:  # Example: year 2019 ... YCode is 0 for 2020
        Y += 10

    exp_day = 21 - (calendar.weekday(Y, M, 1) + 2) % 7
    exp_dt = datetime.datetime(Y, M, exp_day)

    # Get the year, week numbers
    exp_year, exp_week, _ = exp_dt.isocalendar()
    dt_year, dt_week, _ = dt.isocalendar()

    # print('dt {} vs {} exp_dt'.format(dt, exp_dt))
    # print('dt_week {} vs {} exp_week'.format(dt_week, exp_week))

    # can switch if in same week
    return (dt_year, dt_week) == (exp_year, exp_week)


def checkvolume(d0, d1):
    return d0.volume[0] < d1.volume[0]  # Switch if volume from d0 < d1


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

    cerebro = bt.Cerebro()

    fcodes = ['199FESXM4', '199FESXU4', '199FESXZ4', '199FESXH5', '199FESXM5']
    store = bt.stores.VChartFile()
    ffeeds = [store.getdata(dataname=x) for x in fcodes]

    rollkwargs = dict()
    if args.checkdate:
        rollkwargs['checkdate'] = checkdate

        if args.checkcondition:
            rollkwargs['checkcondition'] = checkvolume

    if not args.no_cerebro:
        if args.rollover:
            cerebro.rolloverdata(name='FESX', *ffeeds, **rollkwargs)
        else:
            cerebro.chaindata(name='FESX', *ffeeds)
    else:
        drollover = bt.feeds.RollOver(*ffeeds, dataname='FESX', **rollkwargs)
        cerebro.adddata(drollover)

    cerebro.addstrategy(TheStrategy)
    cerebro.run(stdstats=False)

    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 Roll Over of Futures')

    parser.add_argument('--no-cerebro', required=False, action='store_true',
                        help='Use RollOver Directly')

    parser.add_argument('--rollover', required=False, action='store_true')

    parser.add_argument('--checkdate', required=False, action='store_true',
                        help='Change during expiration week')

    parser.add_argument('--checkcondition', required=False,
                        action='store_true',
                        help='Change when a given condition is met')

    # 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按日線交易

似乎在世界某個地方有一種权益(Interest)可以總結如下: 使用每日柱線引入訂單,但使用開盤價 這來自工單#105订单执行逻辑与当前数据和#101动态投注计算中的對話 backtrader 嘗試盡可能保持現實,並且在處理每日柱線時適用以下前提: 當每日柱被評估時,柱線已經結束 這是有道理的,

Backtrader對逐筆報價數據重新取樣

backtrader 已經可以從分鐘數據中重新採樣。接受價格變動數據不是問題,只需將 4 個常用欄位(open、 high、 low、 close)設置為價格變動值。 但是傳遞要重新採樣的逐筆報價數據再次生成相同的數據。作為或版本 1.1.11.88,情況已不再如此。

Backtrader教程:指標 - 開發

如果必須開發任何東西(除了一個或多個獲勝策略之外),那麼這個東西就是一個自定義指標。 根據作者的說法,平臺內的這種開發很容易。 需要滿足以下條件: 從指標派生的類(直接或從現有的子類派生) 定義它將保持lines 指標必須至少具有 1 line。

Backtrader期貨補償與現貨補償

版本1.9.32.116 增加了對社區中呈現的有趣用例 的支援 以期貨開始交易,包括實物交割 讓一個指標告訴你一些事情 如果需要, close 現貨價格操作,有效地取消實物交割,無論是為了接收貨物還是為了必須交付貨物(並希望獲利)來頭寸。

Backtrader教程:操作平臺

Line 反覆運算器 為了參與操作,plaftorm使用 line 反覆運算器的概念。它們已經鬆散地模仿了Python的反覆運算器,但實際上與它們無關。 策略和指標是 line 反覆運算器。

Backtrader教程:日誌記錄 - 編寫器

將以下內容寫出到流中: csv 流,

Backtrader教程:數據饋送 - 擴展 (Extending DataFeed)

GitHub 中的問題實際上是在推動文檔部分的完成,或者説明我瞭解我是否backtrader 具有我從一開始就設想的易用性和靈活性以及在此過程中做出的決定。 在本例中為問題 #9。

Backtrader教程:數據饋送 - 展期交割

並非每個供應商都為可以交易的工具提供連續的未來。有時提供的數據是仍然有效的到期日期的數據,即:仍在交易的日期 這在回溯測試方面並不是很有幫助,因為數據分散在幾個不同的儀器上,這些儀器另外...時間重疊。 能夠正確地將這些儀器的數據從過去連接到連續的流中,可以減輕疼痛。

Backtrader蟒蛇隱藏的力量3

Last,但並非最不重要的一點是,在這個系列中,關於如何在 backtrader 中使用Python的隱藏功能是一些神奇變數是如何出現的。

Backtrader多數據範例

社區中的幾個主題似乎以如何跟蹤訂單為導向,特別是當幾個data feeds在起作用時,還包括當多個訂單一起工作時,