筆記
示例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
基類負責參數、初始化、打開文件、讀取行、將行拆分為標記以及其他一些事情,例如跳過不適合最終用戶可能已定義的日期範圍( fromdate
、 todate
)的行.
開發非 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
的情況下,此參數意味著文件的路徑或已經是類似文件的對象。fromdate
和todate
定義將傳遞給策略的日期範圍。 Feed 提供的超出此範圍的任何值都將被忽略name
是用於繪圖目的的化妝品timeframe
表示時間工作參考潛在值:
Ticks
、Seconds
、Minutes
、Days
、Weeks
、Months
和Years
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、 open 、 high 、 low 、 close 、 volume 、 openinterest 。在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、 open 、 high 、 low 、 close 、 volume 、 openinterest
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