网站开发主要都做些什么,电商数据,软件开发要学什么,图片外链网站Backtrader 数据篇
本系列是使用Backtrader在量化领域的学习与实践#xff0c;着重介绍Backtrader的使用。Backtrader 中几个核心组件#xff1a;
Cerebro#xff1a;BackTrader的基石#xff0c;所有的操作都是基于Cerebro的。Feed#xff1a;将运行策略所需的基础数据…Backtrader 数据篇
本系列是使用Backtrader在量化领域的学习与实践着重介绍Backtrader的使用。Backtrader 中几个核心组件
CerebroBackTrader的基石所有的操作都是基于Cerebro的。Feed将运行策略所需的基础数据加载到Cerebro中一般为K线数据。IndicatorBackTader自带的指标并集成了talib中的指标。我们也可以选择继承一个Indicator实现自己的指标。Strategy交易策略。这里是整个过程中最复杂的部分需要我们计算买入/卖出信号。Analyzer分析器以图形和风险收益等指标对交易策略的回测结果进行分析评价。Order订单记录了与当前订单相关的所有数据。Trader交易记录了与当前交易相关的所有数据。Position持仓记录了与当前持仓相关的所有数据。Broker可以理解成经纪人整个策略的初始资金、交易费率、滑点等参数需要通过Broker进行设置。Observer观察者对数据进行监控观察比如资金曲线等等。Plotting可视化组件
本次介绍Backtrader中DataFeeds模块DataFeeds模块是Backtrader核心模块之一其提供多数据加载途径同时提供数据的前期处理。
在使用Backtrader时结合量化策略编写过程通常会考虑
backtrader如何加载数据可以加载哪些数据编写策略时如何访问标的数据行、列、单元backtrader是否可以自定义数据内容
…
import backtrader as bt
import pandas as pd
import numpy as np
import datetimedaily_price pd.read_csv(./data/daily_price.csv, parse_dates[datetime])# 筛选 600466.SH 和 603228.SH 2只股票的数据集
data1 daily_price.query(fsec_code600466.SH).set_index(datetime).drop(columns[sec_code])
data2 daily_price.query(fsec_code603228.SH).set_index(datetime).drop(columns[sec_code])0 Backtrader中的数据结构
在Backtrader可将单个或多个标的数据导入到“数据表”中数据可通过self.datas插入顺序、self.dataX方式进行获取。
self.data 和 self.data0 指向第一个元素self.dataX 指向数组中索引为 X 的元素
class TestStrategy(bt.Strategy):def __init__(self):print(--- self.datas ---)print(self.datas)print(--- self.data ---)print(self.data._name,self.data)print(--- self.data0 ---)print(self.data0._name, self.data0)print(--- self.datas[0] ---)print(self.datas[0]._name, self.datas[0])print(--- self.datas[1] ---)print(self.datas[1]._name, self.datas[1])print(--- self.datas[-1] ---)print(self.datas[-1]._name, self.datas[-1])def next(self):pass#print(---next: 索引为6的线 ---)#print(bt.num2date(self.datas[0].lines[6][0]))#print(bt.num2date(self.datas[0].lines[6][0]))cerebro bt.Cerebro()
st_date datetime.datetime(2019,1,2)
ed_date datetime.datetime(2021,1,28)
datafeed1 bt.feeds.PandasData(datanamedata1,fromdatest_date,todate ed_date)
cerebro.adddata(datadatafeed1,name600466.SH)datafeed2 bt.feeds.PandasData(datanamedata2,fromdatest_date,todateed_date)
cerebro.adddata(datadatafeed2,name603228.SH)cerebro.addstrategy(TestStrategy)
result cerebro.run()1 如何添加数据源
Backtrader 支持导入各式各样的数据
第三方网站加载数据Yahoo、VisualChart、Sierra Chart、Interactive Brokers 盈透、OANDA、QuandlCSV 文件Pandas DataFrameInfluxDB、MT4CSV 等 最基础或最常见的就是导入 CSV 和导入 DataFrame了。 默认的导入方式: Backtrader 中的数据表格默认情况下包含7列这7列的位置也是固定的依次为 (‘close’, ‘low’, ‘high’, ‘open’, ‘volume’, ‘openinterest’, ‘datetime’) 导入的数据表格必须包含这 7 个指标吗指标的排列顺序也必须一致吗 当然不是可以通过 GenericCSVData、PandasData 、PandasDirectData 这 7 个指标在数据源中位于第几列如果没有这个指标那就将位置设置为 -1。 data1openhighlowclosevolumeopeninterestdatetime2019-01-0233.06489133.49670931.95450332.3863211062935202019-01-0332.26294432.94151531.39930931.831127860264602019-01-0431.39930933.55839731.33762133.4967091276811602019-01-0733.49670934.36034433.37333233.6200851058432102019-01-0833.31164434.11359132.69476233.743462100129020.....................2021-01-2230.24543030.31294229.50279629.7728451718405502021-01-2529.57030929.57030928.82767528.9626992364617402021-01-2628.96269929.23274828.69265128.760163996344202021-01-2728.76016329.23274828.69265128.8951871292933102021-01-2828.82767528.82767528.55762728.760163128260070
506 rows × 6 columns
2 如何访问和获取数据
获取 line 数据
Backtrader中列是“lines”一列 → 一个指标 → 该指标的时间序列 → 一条线 line
Backtrader中每一个Data Feed对象都含有lines属性。将 lines 属性看作是 line 的集合所以想要调用具体的某一条线就通过 lines 属性来调用 通过 lines 属性访问。例如xxx.lines, 可简写为xxx.l 通过 lines 属性中具体“线”访问。 例如xxx.lines.close, 或写成 xxx.lines_close “先调用某张数据表格再调用这张表格中具体的某根 line”的逻辑依次编写代码。 例如xxx.datas[0].lines[6][0]
在Backtrader的lines中对于 datetime 线 1、datetime 线中的时间点存的是数字形式的时间可以通过 bt.num2date() 方法将其转为“xxxx-xx-xx xx:xx:xx”这种形式 2、对 datetime 线进行索引时xxx.date(X) 可以直接以“xxxx-xx-xx xx:xx:xx”的形式返回X 就是索引位置可以看做是传统 [X] 索引方式的改进版 。 class TestStrategy(bt.Strategy):def __init__(self):print(---打印 self 策略本身的lines ---)print(self.lines.getlinealiases())print(--- 打印 self.datas 第一个数据表格的 lines ---)print(self.datas[0]._name,self.datas[0].lines.getlinealiases())print(self.datas[0]._name,self.datas[0].lines[5][0])print(--- 打印 self.datas 第二个数据表格的 lines ---)print(self.datas[1]._name, self.datas[1].lines.getlinealiases())def next(self):print(---next: 索引为6的线 ---)print(self.datas[0]._name,bt.num2date(self.datas[0].lines[6][0]))print(self.datas[1]._name,bt.num2date(self.datas[1].lines[6][0]))cerebro bt.Cerebro()
st_date datetime.datetime(2019,1,2)
ed_date datetime.datetime(2021,1,28)
datafeed1 bt.feeds.PandasData(datanamedata1,fromdatest_date,todate ed_date)
cerebro.adddata(datadatafeed1,name600466.SH)datafeed2 bt.feeds.PandasData(datanamedata2,fromdatest_date,todateed_date)
cerebro.adddata(datadatafeed2,name603228.SH)cerebro.addstrategy(TestStrategy)
result cerebro.run()获取 line 上的数据点
Backtrader 提供了 索引规则 和 切片方法 get() 获取数据节点数据
1、索引规则索引位置编号结合了时间信息0 号位置指向当前时间点的数据-1 号位置指向前一个时间点的数据然后依次回退 backwards-2、-3、-4、-5、…1 号位置指向下一天的数据然后依次向前forwards2、3、4、… 2、切片方法get(ago0, size1) 函数其中 ago 对应数据点的索引位置即从 ago 时间点开始往前取 size 个数据点。默认情况下是取当前最新时点ago0的那一个数据size1 3、在编写策略时上面提到的对数据点的索引切片操作一般在 next() 函数中涉及较多而 init() 中涉及较少因为__init__() 中一般是对 一整条 line 进行操作运算。
class TestStrategy(bt.Strategy):def __init__(self):self.count 0 # 用于计算 next 的循环次数# 打印数据集和数据集对应的名称print(------------- init 中的索引位置-------------)print(0 索引,datetime,self.data1.lines.datetime.date(0), close,self.data1.lines.close[0])print(-1 索引,datetime,self.data1.lines.datetime.date(-1),close, self.data1.lines.close[-1])print(-2 索引,datetime, self.data1.lines.datetime.date(-2),close, self.data1.lines.close[-2])print(1 索引,datetime,self.data1.lines.datetime.date(1),close, self.data1.lines.close[1])print(2 索引,datetime, self.data1.lines.datetime.date(2),close, self.data1.lines.close[2])print(从 0 开始往前取3天的收盘价, self.data1.lines.close.get(ago0, size3))print(从-1开始往前取3天的收盘价, self.data1.lines.close.get(ago-1, size3))print(从-2开始往前取3天的收盘价, self.data1.lines.close.get(ago-2, size3))print(line的总长度, self.data1.buflen())def next(self):print(f------------- next 的第{self.count1}次循环 --------------)print(当前时点今日,datetime,self.data1.lines.datetime.date(0),close, self.data1.lines.close[0])print(往前推1天昨日,datetime,self.data1.lines.datetime.date(-1),close, self.data1.lines.close[-1])print(往前推2天前日, datetime,self.data1.lines.datetime.date(-2),close, self.data1.lines.close[-2])print(前日、昨日、今日的收盘价, self.data1.lines.close.get(ago0, size3))print(往后推1天明日,datetime,self.data1.lines.datetime.date(1),close, self.data1.lines.close[1])print(往后推2天明后日, datetime,self.data1.lines.datetime.date(2),close, self.data1.lines.close[2])print(已处理的数据点, len(self.data1))print(line的总长度, self.data1.buflen())self.count 1cerebro bt.Cerebro()
st_date datetime.datetime(2019,1,2)
ed_date datetime.datetime(2021,1,28)
datafeed1 bt.feeds.PandasData(datanamedata1,fromdatest_date,todate ed_date)
cerebro.adddata(datadatafeed1,name600466.SH)datafeed2 bt.feeds.PandasData(datanamedata2,fromdatest_date,todateed_date)
cerebro.adddata(datadatafeed2,name603228.SH)cerebro.addstrategy(TestStrategy)
result cerebro.run()3 自定义读取函数
重新自定义数据读取函数自定义的方式就是继承数据加载类 GenericCSVData、PandasData 再构建一个新的类然后在新的类里统一设置参数。
在Backtrader中默认列对应的索引值 open (default: 1) , high (default: 2), low (default: 3), close (default: 4), volume (default: 5), openinterest (default: 6)自定义数据读取函数需要注意
如果传递负值例如-1则表示 CSV 数据中不存在该字段自定义的函数不会修改 Backtrader 底层的数据表格内 lines 的排列规则。
daily_price.head(10)datetimesec_codeopenhighlowclosevolumeopeninterest02019-01-02600466.SH33.06489133.49670931.95450332.38632110629352012019-01-02603228.SH50.66023051.45851350.39413651.120778426147022019-01-02600315.SH148.258423150.480132148.258423149.5589352138556032019-01-02000750.SZ49.51257953.15488348.71582551.561375227557612042019-01-02002588.SZ36.60867236.60867235.66998835.7638572841517052019-01-02002926.SZ8.4056568.4759548.3353588.4357845534055062019-01-02603816.SH46.48051846.48051844.11031945.0132521864419072019-01-02002517.SZ23.62670123.62670123.25266323.3150039767171082019-01-02600366.SH56.67503258.75697256.67503257.6003385504462092019-01-02001914.SZ60.60462161.41484360.11848760.28053214840880
示例说明
定义读取数据时间范围20190102 - 20210128缺失值设置为0.0日期格式定义为YYYY-MM-DD定义openinterest 为不存在列
class My_CSVData(bt.feeds.GenericCSVData):params ((fromdate, datetime.datetime(2019,1,2)),(todate, datetime.datetime(2021,1,28)),(nullvalue, 0.0),(dtformat, (%Y-%m-%d)),(datetime, 0),(time, -1),(high, 3),(low, 4),(open, 2),(close, 5),(volume, 6),(openinterest, -1)
)class TestStrategy(bt.Strategy):def __init__(self):self.count 0 # 用于计算 next 的循环次数# 打印数据集和数据集对应的名称print(------------- init 中的索引位置-------------)print(--- 打印 self.datas 第一个数据表格的 lines ---)print(self.datas[0]._name,self.datas[0].lines.getlinealiases())cerebro bt.Cerebro()
data My_CSVData(dataname./data/daily_price.csv)
cerebro.adddata(data, name600466.SH)
cerebro.addstrategy(TestStrategy)
rasult cerebro.run()4 新增指标
往 Backtrader 的数据表格里添加指标就是给数据表格新增列也就是给数据表格新增 line。
在继承原始的数据读取类 GenericCSVData 或 bt.feeds.PandasData 的基础上设置 lines 属性和 params 属性新的 line 会按其在 lines 属性中的顺序依次添加进数据表格中。
示例说明
本次增加PE、PB信息继承bt.feeds.PandasData类在现有的数据上追加PB、PE列数据
class PandasData_more(bt.feeds.PandasData):# 要添加的线lines (pe,pb,)# -1表示自动按列明匹配数据也可以设置为线在数据源中列的位置索引 ((pe,6),(pb,7),)params((pe,-1),(pb,-1),)class TestStrategy(bt.Strategy):def __init__(self):print(---第一个数据表格 lines---)print(self.data0.lines.getlinealiases())print(pe line:, self.data0.lines.pe)print(pb line:, self.data0.lines.pb)data1[pe] 2
data1[pb] 3cerebro bt.Cerebro()
st_date datetime.datetime(2019,1,2)
ed_date datetime.datetime(2021,1,28)# 调用PandasData_more导入Data1数据
datafeed1 PandasData_more(datanamedata1,fromdatest_date,todate ed_date)
cerebro.adddata(datadatafeed1,name600466.SH)cerebro.addstrategy(TestStrategy)
result cerebro.run()---第一个数据表格 lines---
(close, low, high, open, volume, openinterest, datetime, pe, pb)
pe line: backtrader.linebuffer.LineBuffer object at 0x133edfc40
pb line: backtrader.linebuffer.LineBuffer object at 0x133edf9d0在继承原始的数据读取类 GenericCSVData 的基础上设置 lines 属性和 params 属性新的 line 会按其在 lines 属性中的顺序依次添加进数据表格中。 示例说明
本次增加PE、PB信息继承GenericCSVData类在现有的数据上追加PB、PE列数据
from backtrader.feeds import GenericCSVDataclass GenericCSV_PE(GenericCSVData):# 在从基类继承的行中添加一个pe行lines (pe,)# openinterest in GenericCSVData has index 7 ... add 1# add the parameter to the parameters inherited from the base classparams ((pe, 7),(fromdate, datetime.datetime(2019,1,2)),(todate, datetime.datetime(2021,1,28)),(nullvalue, 0.0),(dtformat, (%Y-%m-%d)),(datetime, 0),(time, -1),(high, 3),(low, 4),(open, 2),(close, 5),(volume, 6),(openinterest, -1))class TestStrategy(bt.Strategy):def __init__(self):print(---第一个数据表格 lines---)print(self.data0.lines.getlinealiases())print(pe line:, self.data0.lines.pe)#print(pb line:, self.data0.lines.pb)data1[pe] 2cerebro bt.Cerebro()
# 调用PandasData_more导入Data1数据
datafeed1 GenericCSV_PE(dataname./data/daily_price.csv)
print(type(datafeed1))
cerebro.adddata(datadatafeed1,name600466.SH)cerebro.addstrategy(TestStrategy)
result cerebro.run()