Backtrader 教程:數據饋送 - 開發 - 常規

  |  

筆記

示例goog.fd中使用的二進製文件屬於 VisualChart,不能與backtrader一起分發。

對直接使用二進製文件感興趣的人可以免費下載VisualChart

CSV數據饋送開發已經展示瞭如何添加新的基於 CSV 的數據饋送。現有的基類 CSVDataBase 提供的框架將大部分工作從子類中移除,在大多數情況下可以簡單地完成:

def _loadline(self, linetokens):

  # parse the linetokens here and put them in self.lines.close,
  # self.lines.high, etc

  return True # if data was parsed, else ... return False

基類負責參數、初始化、打開文件、讀取、將拆分為標記以及其他一些事情,例如跳過不適合最終用戶可能已定義的日期範圍( fromdatetodate )的.

開發非 CSV 數據饋送遵循相同的模式,而無需深入到已經拆分的標記。

要做的事情:

  • backtrader .feed.DataBase 派生

  • 添加您可能需要的任何參數

  • 如果需要初始化,覆蓋__init__(self)和/或start(self)

  • 如果需要任何清理代碼,請覆蓋stop(self)

  • 工作發生在必須始終被覆蓋的方法內部: _load(self)

讓我們看看backtrader .feed.DataBase已經提供的參數:

from backtrader.utils.py3 import with_metaclass

...
...

class DataBase(with_metaclass(MetaDataBase, dataseries.OHLCDateTime)):

    params = (('dataname', None),
        ('fromdate', datetime.datetime.min),
        ('todate', datetime.datetime.max),
        ('name', ''),
        ('compression', 1),
        ('timeframe', TimeFrame.Days),
        ('sessionend', None))

具有以下含義:

  • dataname允許數據饋送識別如何獲取數據。在CSVDataBase的情況下,此參數意味著文件的路徑或已經是類似文件的對象。

  • fromdatetodate定義將傳遞給策略的日期範圍。 Feed 提供的超出此範圍的任何值都將被忽略

  • name是用於繪圖目的的化妝品

  • timeframe表示時間工作參考

    潛在值: TicksSecondsMinutesDaysWeeksMonthsYears

  • compression (默認值:1)

    每個柱的實際柱數。內容豐富。僅對數據重採樣/重放有效。

  • compression

  • sessionend如果通過(一個datetime對象)將被添加到允許識別會話結束的數據饋送日期時間

示例二進制數據饋送

backtrader已經為VisualChart的導出定義了一個 CSV 數據饋送 ( VChartCSVData ),但也可以直接讀取二進制數據文件。

讓我們開始吧(完整的數據饋送代碼可以在底部找到)

初始化

二進制 VisualChart 數據文件可以包含每日(.fd 擴展名)或日內數據(.min 擴展名)。這裡將使用參數timeframe來區分正在讀取的文件類型。

__init__期間,設置了每種類型不同的常量。

    def __init__(self):
        super(VChartData, self).__init__()

        # Use the informative "timeframe" parameter to understand if the
        # code passed as "dataname" refers to an intraday or daily feed
        if self.p.timeframe >= TimeFrame.Days:
            self.barsize = 28
            self.dtsize = 1
            self.barfmt = 'IffffII'
        else:
            self.dtsize = 2
            self.barsize = 32
            self.barfmt = 'IIffffII'

開始

Datafeed 將在回測開始時啟動(實際上可以在優化期間啟動多次)

start方法中,除非傳遞了類似文件的對象,否則二進製文件是打開的。

    def start(self):
        # the feed must start ... get the file open (or see if it was open)
        self.f = None
        if hasattr(self.p.dataname, 'read'):
            # A file has been passed in (ex: from a GUI)
            self.f = self.p.dataname
        else:
            # Let an exception propagate
            self.f = open(self.p.dataname, 'rb')

停止

回測完成時調用。

如果一個文件是打開的,它將被關閉

    def stop(self):
        # Close the file if any
        if self.f is not None:
            self.f.close()
            self.f = None

實際加載

實際工作在_load中完成。調用以加載下一組數據,在本例中為下一組:datetime、 openhighlowclosevolumeopeninterest 。在backtrader中,“實際”時刻對應於索引 0。

將從打開的文件中讀取一些字節(由__init__期間設置的常量確定),用struct模塊解析,如果需要進一步處理(如日期和時間的 divmod 操作)並存儲在數據lines提要:日期時間、開盤價最高價、最低價收盤價、交易持倉量。

如果無法從文件中讀取數據,則假定已到達文件結尾 (EOF)

  • 返回False表示沒有更多數據可用

否則,如果數據已加載並解析:

  • 返回True表示數據集加載成功
    def _load(self):
        if self.f is None:
            # if no file ... no parsing
            return False

        # Read the needed amount of binary data
        bardata = self.f.read(self.barsize)
        if not bardata:
            # if no data was read ... game over say "False"
            return False

        # use struct to unpack the data
        bdata = struct.unpack(self.barfmt, bardata)

        # Years are stored as if they had 500 days
        y, md = divmod(bdata[0], 500)
        # Months are stored as if they had 32 days
        m, d = divmod(md, 32)
        # put y, m, d in a datetime
        dt = datetime.datetime(y, m, d)

        if self.dtsize > 1:  # Minute Bars
            # Daily Time is stored in seconds
            hhmm, ss = divmod(bdata[1], 60)
            hh, mm = divmod(hhmm, 60)
            # add the time to the existing atetime
            dt = dt.replace(hour=hh, minute=mm, second=ss)

        self.lines.datetime[0] = date2num(dt)

        # Get the rest of the unpacked data
        o, h, l, c, v, oi = bdata[self.dtsize:]
        self.lines.open[0] = o
        self.lines.high[0] = h
        self.lines.low[0] = l
        self.lines.close[0] = c
        self.lines.volume[0] = v
        self.lines.openinterest[0] = oi

        # Say success
        return True

其他二進制格式

相同的模型可以應用於任何其他二進制源:

  • 數據庫

  • 分層數據存儲

  • 在線資源

又是步驟:

  • __init__ -> 實例的任何初始化代碼,只有一次

  • start -> 開始回測(如果要運行優化,則進行一次或多次)

    例如,這將打開到數據庫的連接或到在線服務的套接字

  • stop -> 清理,例如關閉數據庫連接或打開套接字

  • _load -> 查詢數據庫或在線源以獲取下一組數據並將其加載到對象的lines中。標準字段為:datetime、 openhighlowclosevolumeopeninterest

VChartData 測試

VCharData從本地“.fd”文件為 Google 加載 2006 年的數據。

它只是關於加載數據,因此甚至不需要Strategy的子類。

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

import datetime

import backtrader as bt
from vchart import VChartData


if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro(stdstats=False)

    # Add a strategy
    cerebro.addstrategy(bt.Strategy)

    ###########################################################################
    # Note:
    # The goog.fd file belongs to VisualChart and cannot be distributed with
    # backtrader
    #
    # VisualChart can be downloaded from www.visualchart.com
    ###########################################################################
    # Create a Data Feed
    datapath = '../../datas/goog.fd'
    data = VChartData(
        dataname=datapath,
        fromdate=datetime.datetime(2006, 1, 1),
        todate=datetime.datetime(2006, 12, 31),
        timeframe=bt.TimeFrame.Days
    )

    # Add the Data Feed to Cerebro
    cerebro.adddata(data)

    # Run over everything
    cerebro.run()

    # Plot the result
    cerebro.plot(style='bar')

VChartData 完整代碼

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

import datetime
import struct

from backtrader.feed import DataBase
from backtrader import date2num
from backtrader import TimeFrame


class VChartData(DataBase):
    def __init__(self):
        super(VChartData, self).__init__()

        # Use the informative "timeframe" parameter to understand if the
        # code passed as "dataname" refers to an intraday or daily feed
        if self.p.timeframe >= TimeFrame.Days:
            self.barsize = 28
            self.dtsize = 1
            self.barfmt = 'IffffII'
        else:
            self.dtsize = 2
            self.barsize = 32
            self.barfmt = 'IIffffII'

    def start(self):
        # the feed must start ... get the file open (or see if it was open)
        self.f = None
        if hasattr(self.p.dataname, 'read'):
            # A file has been passed in (ex: from a GUI)
            self.f = self.p.dataname
        else:
            # Let an exception propagate
            self.f = open(self.p.dataname, 'rb')

    def stop(self):
        # Close the file if any
        if self.f is not None:
            self.f.close()
            self.f = None

    def _load(self):
        if self.f is None:
            # if no file ... no parsing
            return False

        # Read the needed amount of binary data
        bardata = self.f.read(self.barsize)
        if not bardata:
            # if no data was read ... game over say "False"
            return False

        # use struct to unpack the data
        bdata = struct.unpack(self.barfmt, bardata)

        # Years are stored as if they had 500 days
        y, md = divmod(bdata[0], 500)
        # Months are stored as if they had 32 days
        m, d = divmod(md, 32)
        # put y, m, d in a datetime
        dt = datetime.datetime(y, m, d)

        if self.dtsize > 1:  # Minute Bars
            # Daily Time is stored in seconds
            hhmm, ss = divmod(bdata[1], 60)
            hh, mm = divmod(hhmm, 60)
            # add the time to the existing atetime
            dt = dt.replace(hour=hh, minute=mm, second=ss)

        self.lines.datetime[0] = date2num(dt)

        # Get the rest of the unpacked data
        o, h, l, c, v, oi = bdata[self.dtsize:]
        self.lines.open[0] = o
        self.lines.high[0] = h
        self.lines.low[0] = l
        self.lines.close[0] = c
        self.lines.volume[0] = v
        self.lines.openinterest[0] = oi

        # Say success
        return True

推薦閱讀

相關文章

Backtrader向 OHLC 提供買入價/賣出價數據

最近,backtrader通過實現line覆蓋來執行從 ohlc-land 逃逸,這允許重新定義整個層次結構,例如,具有僅具有 bid,ask 和 datetime lines的data feeds。

Backtrader教程:觀察者 - 參考

基準 backtrader類 .observers.基準() 此 observer 存儲策略的回報和參考資產的回報,參考資產是傳遞到系統的數據之一。

Backtrader教程:分析儀

無論是回溯測試還是交易,能夠分析交易系統的性能是瞭解是否不僅獲得了利潤,而且是否在風險太大的情況下實現了利潤,或者與參考資產(或無風險資產)相比,是否真的值得付出努力的關鍵。 這就是物件家族的用武之Analyzer 地:提供對所發生事件甚至實際發生的事情的分析。

Backtrader股票篩選

在尋找其他一些東西時,我在StackOverlow家族網站之一上遇到了一個問題:Quantitative Finance aka Quant StackExchange。問題: 它被標記為Python,因此值得一看的是 backtrader 是否能夠勝任這項任務。 分析儀本身 該問題似乎適合用於簡單的分析器。

Backtrader教程:觀察者 - 基準測試

工單 #89 是關於針對資產添加基準測試的。明智的是,人們實際上可能有一個策略,即使積極,也低於簡單地跟蹤資產所能提供的策略。

Backtrader回溯

在一些關於改進的ShapeRatio的提示之後, backtrader 已將此分析儀添加到其武器庫中。 文獻位於: 從對數回報的好處開始,並遵循在SharpeRatio方程的分母中具有標準偏差的副作用,本文檔開發了該分析儀的公式和期望。

Backtrader數據多時間幀

有時投資決策是使用不同的時間框架做出的: 每周評估趨勢 每天執行條目 或者5分鐘對60分鐘。 這意味著需要將多個時間幀的數據組合在 backtrader 中以支援此類組合。 對它的本機支持已經內置。

Backtrader Python隐藏的细节

只有當遇到 backtrader 的真實使用者時,人們才能意識到平臺中使用的抽象和Python功能是否有意義。 在不撇開python的座右銘的情況下, backtrader 試圖為使用者提供盡可能多的控制權,同時通過將Python提供的隱藏功能付諸行動來簡化使用。 第一個示例是系列文章的第一篇。

Backtrader版本 1.2.1.88

將次要版本號從 1 更改為 2 需要一段時間,但舊的 DataResampler 和 DataReplayer 的棄用導致了這種情況。 readthedocs 的文件有 文件已更新為僅引用現代方法 resampling 和 replaying。

Backtrader教程:策略 - 信號

操作 backtrader 也是可能的,而無需編寫策略。雖然這是首選方式,但由於構成機器的對象層次結構,使用信號也是可能的。