当前位置: 首页 > news >正文

Backtrader 数据篇 02

Backtrader 数据篇

本系列是使用Backtrader在量化领域的学习与实践,着重介绍Backtrader的使用。Backtrader 中几个核心组件:

  • Cerebro:BackTrader的基石,所有的操作都是基于Cerebro的。
  • Feed:将运行策略所需的基础数据加载到Cerebro中,一般为K线数据。
  • Indicator:BackTader自带的指标,并集成了talib中的指标。我们也可以选择继承一个Indicator实现自己的指标。
  • Strategy:交易策略。这里是整个过程中最复杂的部分,需要我们计算买入/卖出信号。
  • Analyzer:分析器,以图形和风险收益等指标对交易策略的回测结果进行分析评价。
  • Order:订单,记录了与当前订单相关的所有数据。
  • Trader:交易,记录了与当前交易相关的所有数据。
  • Position:持仓,记录了与当前持仓相关的所有数据。
  • Broker:可以理解成经纪人,整个策略的初始资金、交易费率、滑点等参数需要通过Broker进行设置。
  • Observer:观察者,对数据进行监控观察,比如资金曲线等等。
  • Plotting:可视化组件

本次介绍Backtrader中DataFeeds模块,DataFeeds模块是Backtrader核心模块之一,其提供多数据加载途径,同时提供数据的前期处理。

在使用Backtrader时,结合量化策略编写过程通常会考虑:

  1. backtrader如何加载数据?可以加载哪些数据?
  2. 编写策略时,如何访问标的数据(行、列、单元)?
  3. 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(f"sec_code=='600466.SH'").set_index('datetime').drop(columns=['sec_code'])
data2 = daily_price.query(f"sec_code=='603228.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(dataname=data1,fromdate=st_date,todate = ed_date)
cerebro.adddata(data=datafeed1,name='600466.SH')datafeed2 = bt.feeds.PandasData(dataname=data2,fromdate=st_date,todate=ed_date)
cerebro.adddata(data=datafeed2,name='603228.SH')cerebro.addstrategy(TestStrategy)
result = cerebro.run()

1 如何添加数据源

Backtrader 支持导入各式各样的数据:

  • 第三方网站加载数据(Yahoo、VisualChart、Sierra Chart、Interactive Brokers 盈透、OANDA、Quandl)
  • CSV 文件
  • Pandas DataFrame
  • InfluxDB、MT4CSV 等

最基础或最常见的就是导入 CSV 和导入 DataFrame了。

默认的导入方式: Backtrader 中的数据表格默认情况下包含7列,这7列的位置也是固定的,依次为 (‘close’, ‘low’, ‘high’, ‘open’, ‘volume’, ‘openinterest’, ‘datetime’)

导入的数据表格必须包含这 7 个指标吗?指标的排列顺序也必须一致吗?

  • 当然不是!可以通过 GenericCSVData、PandasData 、PandasDirectData 这 7 个指标在数据源中位于第几列,如果没有这个指标,那就将位置设置为 -1。
data1
openhighlowclosevolumeopeninterest
datetime
2019-01-0233.06489133.49670931.95450332.386321106293520
2019-01-0332.26294432.94151531.39930931.83112786026460
2019-01-0431.39930933.55839731.33762133.496709127681160
2019-01-0733.49670934.36034433.37333233.620085105843210
2019-01-0833.31164434.11359132.69476233.743462100129020
.....................
2021-01-2230.24543030.31294229.50279629.772845171840550
2021-01-2529.57030929.57030928.82767528.962699236461740
2021-01-2628.96269929.23274828.69265128.76016399634420
2021-01-2728.76016329.23274828.69265128.895187129293310
2021-01-2828.82767528.82767528.55762728.760163128260070

506 rows × 6 columns

2 如何访问和获取数据

获取 line 数据

Backtrader中,列是“lines”:一列 → 一个指标 → 该指标的时间序列 → 一条线 line

Backtrader中每一个Data Feed对象都含有lines属性。将 lines 属性看作是 line 的集合,所以想要调用具体的某一条线,就通过 lines 属性来调用:

    1. 通过 lines 属性访问。例如xxx.lines, 可简写为xxx.l
    1. 通过 lines 属性中具体“线”访问。 例如:xxx.lines.close, 或写成 xxx.lines_close
    1. “先调用某张数据表格,再调用这张表格中具体的某根 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(dataname=data1,fromdate=st_date,todate = ed_date)
cerebro.adddata(data=datafeed1,name='600466.SH')datafeed2 = bt.feeds.PandasData(dataname=data2,fromdate=st_date,todate=ed_date)
cerebro.adddata(data=datafeed2,name='603228.SH')cerebro.addstrategy(TestStrategy)
result = cerebro.run()

获取 line 上的数据点

Backtrader 提供了 索引规则 和 切片方法 get() 获取数据节点数据:

  • 1、索引规则:索引位置编号结合了时间信息,0 号位置指向当前时间点的数据,-1 号位置指向前一个时间点的数据,然后依次回退 (backwards)-2、-3、-4、-5、…;1 号位置指向下一天的数据,然后依次向前(forwards)2、3、4、…;

line 上的数据点

  • 2、切片方法:get(ago=0, size=1) 函数,其中 ago 对应数据点的索引位置,即从 ago 时间点开始往前取 size 个数据点。默认情况下是取当前最新时点(ago=0)的那一个数据(size=1);

  • 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(ago=0, size=3))print("从-1开始往前取3天的收盘价:", self.data1.lines.close.get(ago=-1, size=3))print("从-2开始往前取3天的收盘价:", self.data1.lines.close.get(ago=-2, size=3))print("line的总长度:", self.data1.buflen())def next(self):print(f"------------- next 的第{self.count+1}次循环 --------------")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(ago=0, size=3))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(dataname=data1,fromdate=st_date,todate = ed_date)
cerebro.adddata(data=datafeed1,name='600466.SH')datafeed2 = bt.feeds.PandasData(dataname=data2,fromdate=st_date,todate=ed_date)
cerebro.adddata(data=datafeed2,name='603228.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_codeopenhighlowclosevolumeopeninterest
02019-01-02600466.SH33.06489133.49670931.95450332.386321106293520
12019-01-02603228.SH50.66023051.45851350.39413651.1207784261470
22019-01-02600315.SH148.258423150.480132148.258423149.55893521385560
32019-01-02000750.SZ49.51257953.15488348.71582551.5613752275576120
42019-01-02002588.SZ36.60867236.60867235.66998835.76385728415170
52019-01-02002926.SZ8.4056568.4759548.3353588.43578455340550
62019-01-02603816.SH46.48051846.48051844.11031945.01325218644190
72019-01-02002517.SZ23.62670123.62670123.25266323.31500397671710
82019-01-02600366.SH56.67503258.75697256.67503257.60033855044620
92019-01-02001914.SZ60.60462161.41484360.11848760.28053214840880

示例说明:

  1. 定义读取数据时间范围:20190102 - 20210128
  2. 缺失值设置为0.0
  3. 日期格式定义为YYYY-MM-DD
  4. 定义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, name='600466.SH')
cerebro.addstrategy(TestStrategy)
rasult = cerebro.run()

4 新增指标

往 Backtrader 的数据表格里添加指标,就是给数据表格新增列,也就是给数据表格新增 line。

在继承原始的数据读取类 GenericCSVData 或 bt.feeds.PandasData 的基础上,设置 lines 属性和 params 属性,新的 line 会按其在 lines 属性中的顺序依次添加进数据表格中。

示例说明:

  1. 本次增加PE、PB信息;
  2. 继承bt.feeds.PandasData类;
  3. 在现有的数据上追加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(dataname=data1,fromdate=st_date,todate = ed_date)
cerebro.adddata(data=datafeed1,name='600466.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 属性中的顺序依次添加进数据表格中。
示例说明:

  1. 本次增加PE、PB信息;
  2. 继承GenericCSVData类;
  3. 在现有的数据上追加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(data=datafeed1,name='600466.SH')cerebro.addstrategy(TestStrategy)
result = cerebro.run()
http://www.lryc.cn/news/472957.html

相关文章:

  • 视频转场素材资源网站分享
  • 二十二、MySQL 8.0 主从复制原理分析与实战
  • 基于OSS搭建在线教育视频课程分享网站
  • CentOS 7 下升级 OpenSSL
  • 线上 Dump
  • AcWing 1303:斐波那契前 n 项和 ← 矩阵快速幂加速递推
  • 2024 Rust现代实用教程:1.2编译器与包管理工具以及开发环境搭建
  • 人工智能原理实验一:知识的表示与推理实验
  • 自学C语言——VS实用调试技巧总结
  • VC2012创建弹出式菜单
  • Google 第三季度季报出炉
  • 入职总结(更新中)
  • @DeleteMapping和@PostMapping和@GetMapping和Content-Type使用记录
  • unity 中使用zeroMq和Mqtt 进行通讯
  • 四、k8s快速入门之Kubernetes资源清单
  • 掌握ElasticSearch(六):分析过程
  • 【设计模式】使用python 实践框架设计
  • Apache paimon-CDC
  • 如何分析算法的执行效率和资源消耗
  • 提示工程(Prompt Engineering)指南(进阶篇)
  • 音视频入门基础:FLV专题(19)——FFmpeg源码中,解码Audio Tag的AudioTagHeader,并提取AUDIODATA的实现
  • 前端零基础入门到上班:【Day3】从零开始构建网页骨架HTML
  • 字符脱敏工具类
  • 【jvm】jvm对象都分配在堆上吗
  • @AutoWired和 @Resource原理深度分析!
  • C++设计模式创建型模式———原型模式
  • 重学SpringBoot3-Spring WebFlux之SSE服务器发送事件
  • YOLO即插即用模块---AgentAttention
  • 探索开源语音识别的未来:高效利用先进的自动语音识别技术20241030
  • 学习路之TP6--workman安装