Backtrader解决多股回测时跳过日期的问题

2023-10-27

股票的上市日期各不相同,有些也退市了。在回测时,Backtrader会遍历所有的数据,选择有效期的交集开始执行next()

这时我们的选股策略就会因为数据的问题出现一段时间的空窗期,所以我们不要用next()来执行,而是用prenext()来执行,Backtrader会循环所有的数据,选择最小的那个日期作为开始日,执行prenext(),但是此时衍生出两个问题:

  1. 这只股票的开始日,未必是另一支股票的开始日,这回导致另一只股票没数据
  2. 一直股票可能在某一天退市了就没有数据了,但是有持仓,这就很尴尬了

为了解决以上问题,我们首先学习需要用到的方法:

  • 策略中使用self.datetime.date(),可以知道当前模拟的日期
  • 使用self.getdatabyname(股票name).datetime.date(0),可知给定name的股票数据的起始日期
  • 使用len(this_bond_data) >= this_bond_data.buflen(),得到Backtrader是否已经遍历全部数据点,即终止条件

策略中的函数调用顺序与生命周期可以参考官方文档:https://www.backtrader.com/docu/strategy/

策略中使用next()

如上所述,Backtrader会遍历所有的数据,选择有效期的交集开始执行next()

from datetime import datetime
import backtrader as bt
import pandas as pd
import efinance


def get_k_data(stock_code, begin: datetime, end: datetime) -> pd.DataFrame:
    """
    根据efinance工具包获取股票数据
    :param stock_code:股票代码
    :param begin: 开始日期
    :param end: 结束日期
    :return:
    """
    # stock_code = '600519'  # 股票代码,茅台
    k_dataframe: pd.DataFrame = efinance.stock.get_quote_history(
        stock_code, beg=begin.strftime("%Y%m%d"), end=end.strftime("%Y%m%d"))
    k_dataframe = k_dataframe.iloc[:, :9]
    k_dataframe.columns = ['name', 'code', 'date', 'open', 'close', 'high', 'low', 'volume', 'turnover']
    k_dataframe.index = pd.to_datetime(k_dataframe.date)
    k_dataframe.drop(['name', 'code', 'date'], axis=1, inplace=True)
    return k_dataframe


class MyStrategy1(bt.Strategy):  # 策略
    def __init__(self):
        # 初始化交易指令、买卖价格和手续费
        pass

    def next(self):  # 固定的函数,框架执行过程中会不断循环next(),过一个K线,执行一次next()
        print("今天是:", self.datetime.date())


if __name__ == '__main__':
    # 获取数据
    start_time = datetime(2020, 1, 1)
    end_time = datetime(2020, 3, 1)
    maotai_df = get_k_data('600519', begin=start_time, end=end_time)  # 茅台,从1月1日开始
    ningde_df = get_k_data("300750", begin=datetime(2020, 2, 1), end=end_time)  # 宁德时代,从5月1日开始
    # =============== 为系统注入数据 =================
    # 加载数据
    maotai_data = bt.feeds.PandasData(dataname=maotai_df, fromdate=start_time, todate=end_time)
    ningde_data = bt.feeds.PandasData(dataname=ningde_df, fromdate=start_time, todate=end_time)
    # 初始化cerebro回测系统
    cerebral_system = bt.Cerebro()  # Cerebro引擎在后台创建了broker(经纪人)实例,系统默认每个broker的初始资金量为10000
    # 将数据传入回测系统
    cerebral_system.adddata(maotai_data, name="茅台")  # 导入数据,在策略中使用 self.datas 来获取数据源
    cerebral_system.adddata(ningde_data, name="宁德")
    # 将交易策略加载到回测系统中
    cerebral_system.addstrategy(MyStrategy1)
    # =============== 系统设置 ==================
    # 设置启动资金为 100000
    start_cash = 1000000
    cerebral_system.broker.setcash(start_cash)
    # 设置手续费 万2.5
    cerebral_system.broker.setcommission(commission=0.00025)
    # 运行回测系统
    cerebral_system.run()

我们可以看到:

今天是: 2020-02-03
...
...
今天是: 2020-02-27
今天是: 2020-02-28

是从2月3日开始,修改如下:

使用prenext从第一个模拟交易日开始指定策略

使用prenext()可以从所有数据集的最小的日期开始制定策略,如下代码:

from datetime import datetime
import backtrader as bt
import pandas as pd
import efinance


def get_k_data(stock_code, begin: datetime, end: datetime) -> pd.DataFrame:
    """
    根据efinance工具包获取股票数据
    :param stock_code:股票代码
    :param begin: 开始日期
    :param end: 结束日期
    :return:
    """
    # stock_code = '600519'  # 股票代码,茅台
    k_dataframe: pd.DataFrame = efinance.stock.get_quote_history(
        stock_code, beg=begin.strftime("%Y%m%d"), end=end.strftime("%Y%m%d"))
    k_dataframe = k_dataframe.iloc[:, :9]
    k_dataframe.columns = ['name', 'code', 'date', 'open', 'close', 'high', 'low', 'volume', 'turnover']
    k_dataframe.index = pd.to_datetime(k_dataframe.date)
    k_dataframe.drop(['name', 'code', 'date'], axis=1, inplace=True)
    return k_dataframe


class MyStrategy1(bt.Strategy):  # 策略
    def __init__(self):
        # 初始化交易指令、买卖价格和手续费
        pass

    def prenext(self):
        self.next()

    def next(self):  # 固定的函数,框架执行过程中会不断循环next(),过一个K线,执行一次next()
        print("今天是:", self.datetime.date())


if __name__ == '__main__':
    # 获取数据
    start_time = datetime(2020, 1, 1)
    end_time = datetime(2020, 3, 1)
    maotai_df = get_k_data('600519', begin=start_time, end=end_time)  # 茅台,从1月1日开始
    ningde_df = get_k_data("300750", begin=datetime(2020, 2, 1), end=end_time)  # 宁德时代,从5月1日开始
    # =============== 为系统注入数据 =================
    # 加载数据
    maotai_data = bt.feeds.PandasData(dataname=maotai_df, fromdate=start_time, todate=end_time)
    ningde_data = bt.feeds.PandasData(dataname=ningde_df, fromdate=start_time, todate=end_time)
    # 初始化cerebro回测系统
    cerebral_system = bt.Cerebro()  # Cerebro引擎在后台创建了broker(经纪人)实例,系统默认每个broker的初始资金量为10000
    # 将数据传入回测系统
    cerebral_system.adddata(maotai_data, name="茅台")  # 导入数据,在策略中使用 self.datas 来获取数据源
    cerebral_system.adddata(ningde_data, name="宁德")
    # 将交易策略加载到回测系统中
    cerebral_system.addstrategy(MyStrategy1)
    # =============== 系统设置 ==================
    # 设置启动资金为 100000
    start_cash = 1000000
    cerebral_system.broker.setcash(start_cash)
    # 设置手续费 万2.5
    cerebral_system.broker.setcommission(commission=0.00025)
    # 运行回测系统
    cerebral_system.run()

只需要在策略中添加一个:

    def prenext(self):
        self.next()

就可以从第一天开始执行了

注意:在切换prenext()next()时,会触发一次nextstart(),此时可以执行一些操作

巧妙运用prenext与next

如果我们修改策略为:

class MyStrategy1(bt.Strategy):  # 策略
    def __init__(self):
        # 初始化交易指令、买卖价格和手续费
        pass

    def prenext(self):
        print('PRENEXT 今天是', self.datetime.date())
        self.next()

    def next(self):  # 固定的函数,框架执行过程中会不断循环next(),过一个K线,执行一次next()
        print("今天是:", self.datetime.date())

就可以看到:

PRENEXT 今天是 2020-01-02
今天是: 2020-01-02
PRENEXT 今天是 2020-01-03
今天是: 2020-01-03
....
....
PRENEXT 今天是 2020-01-22
今天是: 2020-01-22
PRENEXT 今天是 2020-01-23
今天是: 2020-01-23
今天是: 2020-02-03
....
....
今天是: 2020-02-26
今天是: 2020-02-27
今天是: 2020-02-28

prenext()会从第一个交易日开始执行,知道所有的数据都存在交集的时候,切换到next(),当然可以在prenext()中主动调用next()

灵活的使用这两种代码可以组合完成非常复杂的逻辑

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Backtrader解决多股回测时跳过日期的问题 的相关文章

随机推荐

  • ++ 符号

    关于 符号 i i 刚好最近看到一个关于i i 的测试题就查询资料测试了一下 int i 10 int j 30 i i Console WriteLine i Console WriteLine i i Console WriteLine
  • ajax保存文件对话框_VBA学习笔记34-2:Excel对话框(FileDialog)

    学习资源 Excel VBA从入门到进阶 第34集 by兰色幻想 这篇笔记写对话框的FileDialog对象 开始前先把上篇漏写的改变窗口默认路径补一下 chdrive 盘符 可以改变默认驱动器 chdir 路径 可以改变默认路径 这两个默
  • C#调用C回调函数后,程序奔溃问题

    原始代理声明 delegate void DlgVideoStreamCallBack IntPtr pData int size int height int width IntPtr pUserData C函数导入 DllImport
  • gitlab-ce-10.0.2版本升级到15.4.2

    先看官方升级文档说明 官方文档链接 升级 如果你的gitlab版本是10 0 2 那个要升级到15 4 2 首先得过度如下几个版本 10 1 0 10 2 3 10 8 7 11 3 4 11 11 8 12 0 12 12 1 17 12
  • C语言小游戏-俄罗斯方块

    C语言小游戏 俄罗斯方块 全部代码如下所示 include
  • 第十一章 数据可视化 - 地图可视化

    目录 疫情地图的使用 疫情地图 国内疫情地图 疫情地图 省级疫情地图 疫情地图的使用 第一阶段 第十一章 01 数据可视化案例 地图 基础地图使用 哔哩哔哩 bilibili 演示地图可视化的基本使用 from pyecharts char
  • layui 树型tree组件 回显BUG

    父级回选时 不管子级有没有设置回显 都会导致子级全部勾选 解决办法是 在后端代码添加逻辑 有子树的都不选中 因为选中子树时 父级会跟着选中 所有这是没问题的
  • 图片服务器

    文章目录 一 项目简介 二 功能及场景 三 业务设计 四 数据库设计 准备图片表 准备实体类 五 API设计 常用功能封装 文件上传 文件上传 获取图片列表接口 获取图片内容 删除图片接口 六 项目优化 七 测试 自动化测试 测试用例 一
  • Qt做的俄罗斯方块游戏

    最近一直在用Qt折腾一个简单的俄罗斯方块游戏 期间断断续续经过将近一个月的折腾 终于完成啦 挂在这里 供大家评阅 第一次做游戏 肯定会有很多的不足之处 希望大家指正 其实做这个游戏主要是想学学多层控件的效果 因为以前做过一些简单的应用型软件
  • 华为OD机试 - 最大矩阵和(Java)

    题目描述 给定一个二维整数矩阵 要在这个矩阵中选出一个子矩阵 使得这个子矩阵内所有的数字和尽量大 我们把这个子矩阵称为和最大子矩阵 子矩阵的选取原则是原矩阵中一块相互连续的矩形区域 输入描述 输入的第一行包含2个整数n m 1 lt n m
  • 解决中文乱码用spring经典编码过滤器org.springframework.web.filter.CharacterEncodingFilter

    使用spring的前提下在web xml中配置
  • Dynamics CRM 错误 问题排查

    很多时候在网上或者群里面看到别人的问题 第一反应就是 有没有搞错 这样的问题怎么让人回答呢 发现很多开发 维护根本没有一点点文档钻研精神 对于这些业务系统 第一是看日志记录 第二是看sdk 别的一切都只是浮云 说多了 回头说这个错误问题排查
  • 什么是开尔文测试?什么时候需要采用开尔文接法?

    开尔文 Kelvin 测试就是通常所说的四线测试方式 四线开尔文测试的目的是扣除导线电阻带来的压降 一段30cm长导线的等效电阻大概是十毫欧姆到百毫欧姆 如果通过导线的电流足够大 比如是安培级的话 那么导线两端的压降就达到几十到上百mV 如
  • chisel快速入门(二)

    上一篇见此 chisel快速入门 一 沧海一升的博客 CSDN博客简单介绍了chisel 使硬件开发者能快速上手chisel https blog csdn net qq 21842097 article details 121415341
  • use JAXB/CXF to process soap message XML

    我参考下列资料 http stackoverflow com questions 11465653 how to unmarshall soap response using jaxb if namespace declaration is
  • Java批量转换编码

    起因 因下载了一些java源代码 存在大约五百个java项目源文件 使用IDEA加载java文件时 其中中文内容全部乱码 立马便想起我的IDEA是utf 8编码 可能这些源码是由GBK进行的字符编码 由于博主很懒 想要通过代码的方式进行转换
  • 详解-自定义树莓派的显示分辨率

    背景 树莓派一般可以自动检测目标设备的分辨率 但对于部分设备 尤其是HDMI VGA转换器 分辨率可能会明显不对 过大或过小 这时就需要给树莓派指定一个分辨率 方法1 设置方法是在终端输入 sudo raspberry config 命令
  • 鼎信通讯提前批主管面凉经(已收到感谢信)

    1 介绍自己 2我提到3D打印 问怎么编程 3 怎么证明自己可以适应工作 4 贪心算法
  • Python-异常处理try(一)

    平时在做自动化测试的时候 脚本里面会经常用到try和except 但是感觉好像对try的用法理解的不是很透彻 今天在这里重新学习下 也分享给大家 大家看一下 try 和 except 的工作原理 try 可能发生异常的代码 lt 语句 gt
  • Backtrader解决多股回测时跳过日期的问题

    股票的上市日期各不相同 有些也退市了 在回测时 Backtrader会遍历所有的数据 选择有效期的交集开始执行next 这时我们的选股策略就会因为数据的问题出现一段时间的空窗期 所以我们不要用next 来执行 而是用prenext 来执行