lesson01 Backtrader是什么

devtools/2024/10/20 8:43:40/

[Backtrader]专题连载

Backtrader是什么?

Backtrader 是 2015 年开源的 Python 量化回测框架(支持实盘交易)。专注于为量化交易策略提供回测和实盘交易功能。它允许用户集中精力编写可复用的交易策略、指标和分析工具,而无需花费时间构建基础设施。

Backtrader 主要特点

  • 丰富的功能:支持股票、期货、期权、外汇、数字货币等多种交易品种,以及从Ticks级到年度的不同时间周期
  • 高效的性能:利用pandas的矢量运算和多策略并行运算,提供快速的回测能力
  • 灵活的组件:内置了Ta-lib技术指标库、PyFlio分析模块、plot绘图模块和参数优化等工具
  • 策略开发:用户可以创建策略类,编写策略逻辑,并通过Cerebro引擎运行回测
  • 数据源:支持多种数据源,允许用户添加数据源到Cerebro引擎,并进行交易
  • 指标和交易:Backtrader提供了大量的内置指标,用户可以在策略中使用这些指标,或自定义新的指标
  • 可视化:Backtrader可以绘制策略的交易图表,帮助用户进行视觉检查

Backtrader 核心组件

在此,先了解 Backtrader 几个核心组件:

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

Backtrader 回测代码流程

Backtrader 以 cerebro 为统一的调度中心,数据、策略、回测条件等信息都会导入 cerebro 中,并由 cerebro 启动和完成回测,最后返回回测结果。

Backtrader 回测代码流程

Backtrader 第一个Demo

  • 股票池:中证500成分股

  • 回测区间:20190101-2021-01-28

  • 持仓周期:月度调仓,每月第一个交易日,以开票价买入或卖出

  • 持仓权重:流通市值占比

  • 总资产:100万元

  • 佣金:0.0003 双边

  • 滑点:0.0001 双边

  • 策略逻辑:假设已经在每个月最后一个交易日基于选股规则选出了中证500成分股中表现优异的前20%的股票作为下一个月的持仓成分股,然后在下个月的第一个交易日,卖出已持仓,买入新的持仓。

python">import backtrader as bt
import pandas as pd
import numpy as np
import datetime
from copy import deepcopy

准备数据

1、日度行情数据集

数据集 daily_price.csv,日度行情数据(后复权),共有 8 个字段,除 sec_code 字段外,其余 7 个字段是 Data Feeds 导入 DataFrame 数据时默认必须包含的字段。

导入多只股票的历史行情数据:

  • 导入的 DataFrame 有默认的格式要求:
    • 1)以交易日 ‘datetime’ 为 index;
    • 2)列为 ‘open’、‘high’、‘low’、‘close’、‘volume’、‘openinterest’ 字段
  • 采用的是循环导入的方式,每次循环导入一只股票的数据并将数据名称命名为股票名。

2、月末调仓成分股数据集

数据集 trade_info.csv,包含 3 个字段:trade_date 调仓期(每月最后一个交易日)、sec_code 持仓成分股代码、weight 持仓权重

python">daily_price = pd.read_csv('./data/daily_price.csv', parse_dates=['datetime'])
daily_price = daily_price.set_index(['datetime'])trade_info = pd.read_csv('./data/trade_info.csv',parse_dates=['trade_date'])

编写交易逻辑

选股策略

所有的交易策略都是写在自定义的策略类里,如下面的 TestStrategy 类,自定义的策略类名称可以任意取,但必须继承 Backtrader 内置的 Strategy 类,即 bt.Strategy 。

在TestStrategy 里至少需要定义 init() 和 next() 方法。其中, init() 用于初始化各类属性,next() 用于下单交易。

具体到选股策略:

    1. trade_info.csv 里的调仓日和持仓列表就可以定义在 init() 里,方便 next() 函数调用;
    1. 在 next() 里,判断每个交易日是否为调仓日,如果是调仓日就按调仓权重卖出旧股,买入新股。

打印回测日志

在 TestStrategy 里还可以定义许多打印日志的函数,常用的有 notify_order() 订单日志、notify_trade() 交易日志、notify_cashvalue() 资金信息、notify_store() 交易事件说明等等。

提取回测结果,首先要确保已经启动并完成回测,然后再从返回的 result 中提取事先配置好的回测结果。

python">import backtrader.indicators as btind # 导入策略分析模块
import backtrader.feeds as btfeeds # 导入数据模块class TestStrategy(bt.Strategy):# 可选,设置回测的可变参数:如移动均线的周期params = (('buy_stocks',None), # 传入各个调仓日的股票列表和相应的权重)def log(self, txt, dt=None):'''可选,构建策略打印日志的函数:可用于打印订单记录或交易记录等'''dt = dt or self.datas[0].datetime.date(0)print('{},{}'.format(dt.isoformat(),txt))def __init__(self):'''必选,初始化属性、计算指标等'''# 读取调仓日期,即每月的最后一个交易日,回测时,会在这一天下单,然后在下一个交易日,以开盘价买入self.trade_dates = pd.to_datetime(self.p.buy_stocks['trade_date'].unique()).to_list()# 保留调仓信息self.buy_stock = self.p.buy_stocks # 记录以往订单,在调仓日要全部取消未成交的订单self.order_list = []# 记录上一期持仓self.buy_stocks_pre = []# 订单日志def notify_order(self, order):# 未被处理的订单if order.status in [order.Submitted, order.Accepted]:return# 已被处理的订单if order.status in [order.Completed,order.Canceled,order.Margin]:if order.isbuy():self.log('Buy Executed, ref:%.0f, Price: %.2f, Cost: %.2f, Comm %.2f, Size: %.2f, Stock: %s' %(order.ref,order.executed.price,order.executed.value,order.executed.comm,order.executed.size,order.data._name))else:self.log('Sell Executed, ref:%.0f, Price: %.2f, Cost: %.2f, Comm %.2f, Size: %.2f, Stock: %s' %(order.ref,order.executed.price,order.executed.value,order.executed.comm,order.executed.size,order.data._name))def next(self):'''必选,编写交易策略逻辑'''# 获取当前的回测时间点dt = self.datas[0].datetime.date(0)# 打印当前时刻的总资产self.log('当前总资产 %.2f' %(self.broker.getvalue()))# 如果是调仓日,则进行调仓操作if dt in self.trade_dates:print('--{} 为调仓日---'.format(dt))# 1.取消之前所下的没成交也未到期的订单if len(self.order_list)>0:print('---撤销未完成的订单---')for order in self.order_list:self.cancel(order)self.order_list=[]# 2.提取当前调仓日的持仓列表buy_stocks_data = self.buy_stock.query(f"trade_date=='{dt}'")long_list = buy_stocks_data['sec_code'].tolist()print('long_list',long_list)# 3.对现有持仓中,调仓后不再继续持有的股票进行卖出平仓sell_stock = [i for i in self.buy_stocks_pre if i not in long_list]print('sell_stock',sell_stock)if len(sell_stock)>0:print('---对不再持有的股票进行平仓---')for stock in sell_stock:data = self.getdatabyname(stock)if self.getposition(data).size > 0:order = self.close(data=data)self.order_list.append(order)# 4.买入此次调仓的股票:多退少补原则print('---买入此次调仓的股票---')for stock in long_list:weight = buy_stocks_data.query(f"sec_code=='{stock}'")['weight'].iloc[0]data = self.getdatabyname(stock)order = self.order_target_percent(data=data,target=weight * 0.95)self.order_list.append(order)self.buy_stocks_pre = long_list

导入数据

python"># 实例化 cerebro
cerebro = bt.Cerebro()for stock in daily_price['sec_code'].unique():# 日期对齐data = pd.DataFrame(index=daily_price.index.unique())df = daily_price.query(f"sec_code=='{stock}'")[['open','high','low','close','volume','openinterest']]data_ = pd.merge(data, df, left_index=True, right_index=True, how='left')data_.loc[:,['volume','openinterest']] = data_.loc[:,['volume','openinterest']].fillna(0)data_.loc[:,['open','high','low','close']] = data_.loc[:,['open','high','low','close']].fillna(method='pad')data_.loc[:,['open','high','low','close']] = data_.loc[:,['open','high','low','close']].fillna(0)datafeed = bt.feeds.PandasData(dataname=data_, fromdate=datetime.datetime(2019,1,2),todate=datetime.datetime(2021,1,28))cerebro.adddata(datafeed,name=stock)

配置回测条件

python"># 通过经纪商设置初始资金
cerebro.broker.setcash(100000000.0)
# 佣金,双边各 0.0003
cerebro.broker.setcommission(commission=0.0003) 
# 滑点:双边各 0.0001
cerebro.broker.set_slippage_perc(perc=0.0001) # 添加策略
cerebro.addstrategy(TestStrategy, buy_stocks=trade_info)# 添加策略分析指标
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='pnl')
cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn')
cerebro.addanalyzer(bt.analyzers.SharpeRatio, riskfreerate=0.003, annualize=True, _name='_SharpeRatio')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown')# 添加观测器
cerebro.addobserver(bt.observers.Value)

运行回测

python"># 启动回测
result = cerebro.run()

提取回测结果

python">stats = result[0]
print('--AnnualReturn---')
print(stats.analyzers._AnnualReturn.get_analysis())
print('---SharpRatio---')
print(stats.analyzers._SharpeRatio.get_analysis())
print('--DrawDown---')
print(stats.analyzers._DrawDown.get_analysis())
--AnnualReturn---
OrderedDict([(2019, 0.2421668400755459), (2020, 0.2154227563253983), (2021, 0.017567210073598405)])
---SharpRatio---
OrderedDict([('sharperatio', 1.5512121051534205)])
--DrawDown---
AutoOrderedDict([('len', 136), ('drawdown', 6.655064560819013), ('moneydown', 10952970.349310696), ('max', AutoOrderedDict([('len', 206), ('drawdown', 20.374812759676267), ('moneydown', 27705182.493407518)]))])
python"># https://blog.csdn.net/weixin_42829932/article/details/128515915
# 可视化回测结果
%matplotlib inline
cerebro.plot(iplot=True)[0][0]

Q&A

在导入多只股票数据时需注意?

  • 各股交易日不统一:上市日期不一致、退市日期不一致、回测区间内出现停牌等,都会使得不同股票各自的交易日数量不统一,所以要以回测区间内所有交易日为基础,对每只股票缺失的交易日进行补齐;
  • 行情数据缺失:在补齐交易日过程中,会使得补充的交易日缺失行情数据,需对缺失数据进行填充。比如将缺失的 volume 填充为 0,表示股票无法交易的状态;将缺失的高开低收做前向填充;将上市前缺失的高开低收填充为 0 等;
  • 股票与行情数据的匹配:通过设置 adddata() 方法中 name 参数,来实现数据集与股票的一 一对应关系。

http://www.ppmy.cn/devtools/127238.html

相关文章

Go语言基础学习(Go安装配置、基础语法)

一、简介及安装教程 1、为什么学习Go? 简单好记的关键词和语法;更高的效率;生态强大;语法检查严格,安全性高;严格的依赖管理, go mod 命令;强大的编译检查、严格的编码规范和完整的…

计算机组成原理之高速缓冲存储器

定义: 高速缓冲存储器是一种存取速度比一般随机存取记忆体(RAM)更快的RAM,通常使用SRAM技术。 位置: 它是存在于主存与CPU之间的一级存储器,由静态存储芯片(SRAM)组成。 特点&am…

Android 取消充电动画logo,直接显示图片即可

删除充电动画逻辑,直接设置显示图片logo:hvga_low_battery.bmp vendor/mediatek/proprietary/external/libshowlogo/charging_animation.cpp void show_battery_capacity(unsigned int capacity) { anim_show_logo(LOW_BATTERY_INDEX);//*/rm animat…

C++卓越:全面提升专业技能的深度课程(第一章第一课C++17与C++20概述)

第一章:C的现代化 第一课:C17与C20概述 引言 C是一种强大的编程语言,具有丰富的特性和广泛的应用。随着C17和C20的发布,这些版本引入了大量新特性,进一步增强了语言的灵活性和效率。本课将全面探讨C17与C20的新特性…

Atlas800昇腾服务器(型号:3000)—CANN安装(二)

服务器配置如下: CPU/NPU:鲲鹏 CPU(ARM64)A300I pro推理卡 系统:Kylin V10 SP1【下载链接】【安装链接】 驱动与固件版本版本: Ascend-hdk-310p-npu-driver_23.0.1_linux-aarch64.run【下载链接】 Ascend-…

基于SpringBoot+Vue+uniapp微信小程序的澡堂预订的微信小程序的详细设计和实现

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念,提供了一套默认的配置,让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

Hadoop集群安装

集群规划 node01node02node03角色主节点从节点从节点NameNode√DataNode√√√ResourceManager√NodeManager√√√SecondaryNameNode√Historyserver√ 上传安装包到node01 解压到指定目录 tar -zxvf /bigdata/soft/hadoop-3.3.3.tar.gz -C /bigdata/server/ 创建软链接 cd…

安全光幕的工作原理及应用场景

安全光幕是一种利用光电传感技术来检测和响应危险情况的先进设备。其工作原理基于红外线传感器,通过发射红外光束并接收反射或透射光束来形成一道无形的屏障。以下是对安全光幕工作原理和应用场景的介绍: 工作原理 发射器与接收器:安全光幕通…