把backtrader改造成金融强化学习回测引擎

news2025/1/16 3:31:48

原创文章第119篇,专注“个人成长与财富自由、世界运作的逻辑, AI量化投资”。

继续强化学习应用于金融投资。

我们的AI量化平台,针对传统规则量化策略,进行了“积木式”的拆分,这种拆分的好处,就是最大化复用代码逻辑,这样开发策略又快且不容易出错。

针对强化学习环境,我们也打算这么做。看到有些平台,股票一个环境,加密货币一个环境,期货又是另一个环境,甚至把数据源处理都耦合到环境中,这是不对的。维护起来特别麻烦且容易出错。

01 强化学习环境的构成

一个完整的金融强化学习环境,包括数据(通常是OHLC以及特征数据)、交易动作(做多,平仓或者权重),回测系统(回测系统能够对交易动作做出反馈),激励指标(收益率、夏普比等)。

当下很多开源包在实现这个环境,存在一些问题。它们大篇幅做数据特征处理,实现了简单的量化回测,激励指标比较单一。这些环境扩展性不好,更谈不上接入实盘。

前文我们对传统量化与机器学习量化实现了统一的框架,数据管理及自动化标注,接入到backtrader的回测系统中。

同样,我们也希望可以最大化复用之前的成果,把强化学习环境也整合到一起,我们就不必关心数据处理,回测系统等环节的处理。

然而,backtrader本身为是传统量化而生,它的run直接就遍历了整个时间序列,而强化学习是由环境来调用回测引擎,一步一步往前走。

对于backtrader要进行一些改造。

一、重写_run_next

Backtrader在执行run的时间,在_runnext函数里整个顺序进行遍历。我们重写这个函数,只做第一步初始化的运算。

def _runnext(self, runstrats):
    '''
    Actual implementation of run in full next mode. All objects have its
    ``next`` method invoke on each data arrival
    '''
    self.runstrats_container = runstrats
    self._init_run()

def _init_run(self):
    datas = sorted(self.datas,
                   key=lambda x: (x._timeframe, x._compression))
    datas1 = datas[1:]
    data0 = datas[0]
    d0ret = True
    rsonly = [i for i, x in enumerate(datas)
              if x.resampling and not x.replaying]
    onlyresample = len(datas) == len(rsonly)
    noresample = not rsonly

    clonecount = sum(d._clone for d in datas)
    ldatas = len(datas)
    ldatas_noclones = ldatas - clonecount
    dt0 = date2num(datetime.datetime.max) - 2  # default at max

    self.bt_state_container = {"datas": datas,
                               "datas1": datas1,
                               "data0": data0,
                               "d0ret": d0ret,
                               "rsonly": rsonly,
                               "onlyresample": onlyresample,
                               "noresample": noresample,
                               "ldatas_noclones": ldatas_noclones,
                               "dt0": dt0,
                               }

二、提供一个step单步执行函数

这个函数的代码大多可以从cerebro里查到,这里就是展开描述。

def _step(self, runstrats, datas, datas1, data0, d0ret, rsonly,
          onlyresample, noresample, ldatas_noclones, dt0):

    # if any has live data in the buffer, no data will wait anything
    newqcheck = not any(d.haslivedata() for d in datas)
    if not newqcheck:
        # If no data has reached the live status or all, wait for
        # the next incoming data
        livecount = sum(d._laststatus == d.LIVE for d in datas)
        newqcheck = not livecount or livecount == ldatas_noclones

    lastret = False
    # Notify anything from the store even before moving datas
    # because datas may not move due to an error reported by the store
    self._storenotify()
    if self._event_stop:  # stop if requested
        return True
    self._datanotify()
    if self._event_stop:  # stop if requested
        return True

    # record starting time and tell feeds to discount the elapsed time
    # from the qcheck value
    drets = []
    qstart = datetime.datetime.utcnow()
    for d in datas:
        qlapse = datetime.datetime.utcnow() - qstart
        d.do_qcheck(newqcheck, qlapse.total_seconds())
        drets.append(d.next(ticks=False))

    d0ret = any((dret for dret in drets))
    if not d0ret and any((dret is None for dret in drets)):
        d0ret = None

    if d0ret:
        dts = []
        for i, ret in enumerate(drets):
            dts.append(datas[i].datetime[0] if ret else None)

        # Get index to minimum datetime
        if onlyresample or noresample:
            dt0 = min((d for d in dts if d is not None))
        else:
            dt0 = min((d for i, d in enumerate(dts)
                       if d is not None and i not in rsonly))

        dmaster = datas[dts.index(dt0)]  # and timemaster
        self._dtmaster = dmaster.num2date(dt0)
        self._udtmaster = num2date(dt0)

        # slen = len(runstrats[0])
        # Try to get something for those that didn't return
        for i, ret in enumerate(drets):
            if ret:  # dts already contains a valid datetime for this i
                continue

            # try to get a data by checking with a master
            d = datas[i]
            d._check(forcedata=dmaster)  # check to force output
            if d.next(datamaster=dmaster, ticks=False):  # retry
                dts[i] = d.datetime[0]  # good -> store
                # self._plotfillers2[i].append(slen)  # mark as fill
            else:
                # self._plotfillers[i].append(slen)  # mark as empty
                pass

        # make sure only those at dmaster level end up delivering
        for i, dti in enumerate(dts):
            if dti is not None:
                di = datas[i]
                rpi = False and di.replaying  # to check behavior
                if dti > dt0:
                    if not rpi:  # must see all ticks ...
                        di.rewind()  # cannot deliver yet
                    # self._plotfillers[i].append(slen)
                elif not di.replaying:
                    # Replay forces tick fill, else force here
                    di._tick_fill(force=True)

                # self._plotfillers2[i].append(slen)  # mark as fill

    elif d0ret is None:
        # meant for things like live feeds which may not produce a bar
        # at the moment but need the loop to run for notifications and
        # getting resample and others to produce timely bars
        for data in datas:
            data._check()
    else:
        lastret = data0._last()
        for data in datas1:
            lastret += data._last(datamaster=data0)

        if not lastret:
            # Only go extra round if something was changed by "lasts"
            return True  # return somethin signaling the end

    # Datas may have generated a new notification after next
    self._datanotify()
    if self._event_stop:  # stop if requested
        return True

    if d0ret or lastret:  # if any bar, check timers before broker
        self._check_timers(runstrats, dt0, cheat=True)
        if self.p.cheat_on_open:
            for strat in runstrats:
                strat._next_open()
                if self._event_stop:  # stop if requested
                    return True

    self._brokernotify()
    if self._event_stop:  # stop if requested
        return True

    if d0ret or lastret:  # bars produced by data or filters
        self._check_timers(runstrats, dt0, cheat=False)
        for strat in runstrats:
            strat._next()
            if self._event_stop:  # stop if requested
                return True

            self._next_writers(runstrats)

    self.bt_state_container = {"datas": datas,
                               "datas1": datas1,
                               "data0": data0,
                               "d0ret": d0ret,
                               "rsonly": rsonly,
                               "onlyresample": onlyresample,
                               "noresample": noresample,
                               "ldatas_noclones": ldatas_noclones,
                               "dt0": dt0,
                               }

    return False

如此,我们的backtrader就可以实现单步循环,为我们的环境提供回测能力了。

一些思考:

疫情还在持续,不知要多久,不知以何种方式结束。

目前大家看到的,可预见到的,肯定不是大家所期待的。

我们很多时候决定不了什么,只能耐心等待,保护好自己,积蓄能力。

三年后,也许这些都是故事 ,也许一个超级疫苗就研发出来,从此人间皆安。

我们要做的事情是,如果再有类似的“黑天鹅”事件发生,我们是否不那么被动,有更多的选择的权利?!

小时候,受到不公正的待遇,私下默默努力。但现代毕竟不是武侠里的快意恩愁。我们能做的事情是升级自己的环境,有能力远离那些不喜欢的人,不喜欢的事。尽管不好的事情哪里都有,但越往上走,会越发文明,越发自由。

努力的意义是自由,财务自由就是你能离得开职场,进而不受地点的约束,而同样可以过上有品质的生活,你就是自由的。

创业九死一生,若成会带来大自由,不成则更加不自由。读书,写作也许是很好的一条路。读书,写作是在疫情封控这样的情境下都可以做的事情。

代码细节,请前往星球微信群交流。

强化学习框架stable-baseline3以及pandas datareader

ETF轮动+RSRS择时,加上卡曼滤波:年化48.41%,夏普比1.89

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44962.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【三维目标检测】Part-A2(二)

PartA2数据和源码配置调试过程请参考上一篇博文:【三维目标检测】Part-A2(一)_Coding的叶子的博客-CSDN博客。本文主要详细介绍PartA2网络结构及其运行中间状态。 1 PointRCNN模型总体过程 Part-A2的整体结构如下图所示,主要包括…

据2019年中国社交电商行业研究报告称,电商正处于更新换代的时期

引言: 据艾瑞咨询《2019年中国社交电商行业研究报告》示,传统主流电商平台用户与交易规模增速均呈现逐渐放缓的趋势,平台亟需找到更高效、低价、高粘性的流量来源,来跳出竞争日益激烈获客成本持续攀升的困境。移动互联网时代,微信、QQ、微博、快手、抖音等社交类AP…

Wordpress模板主题中functions.php常用功能代码与常用插件(持续收集整理)

用Wordpress建站的初学者一定会需要用到的Wordpress模板主题中functions.php常用功能代码与常用插件。慢慢持续收集整理....... 目录 一、Wordpress模板主题中functions文件常用的代码 二、Wordpress自定义字段的设定与调用代码(系统常规自定义字段) …

【面试宝典】Java八股文之Dubbo 面试题

Dubbo 面试题1、为什么要用 Dubbo?2、Dubbo 的整体架构设计有哪些分层?3、默认使用的是什么通信框架,还有别的选择吗?4、服务调用是阻塞的吗?5、一般使用什么注册中心?还有别的选择吗?6、默认使用什么序列化框架,你知道的还有哪些?7、服务提供者能…

下沉市场投资热度提升 7天酒店打造酒店投资“极致性价比”

近日,7天酒店 “总裁面对面”酒店投资云沙龙活动举办,通过微信、抖音双平台联合直播,多维度探讨酒店行业的“新蓝海”机遇以及下沉市场的投资模式,助力更多投资人把握新的市场红利。 经济型酒店拥抱“新蓝海” 下沉市场投资热度提…

【学习笔记60】JavaScript原型链的理解

一、万物皆对象 JS中, 万物都可以都可以称为对象 1、对象概念 含义1: 一种数据格式 {key: value, key2: value2}含义2: 某一类事务的实例(某一类内容中的真实个体) 2、说明 arr1就是Array这一类内容中的某一个真实个体数组也可以算作一个对象(Array 这一类事务中的一个个体) …

天宇优配|酒企没借壳!标准股份股价上演A杀,两跌停

11月28日晚间,接连三日大跌的规范股份(600302.SZ)发布股价异动公告,再次否定了借壳和重组风闻。当日龙虎榜该股获净卖出774.89万元,闻名游资“赵老哥”常用席位中国银河绍兴现身卖一席位。另外,也有多家本地…

五笔会消亡吗

今天第一次看到“五笔会消亡”的说法。一看好像也没有什么消不消亡的说法,但是深入想一想好像的确是有一个现象90 后 00后使用五笔的应该会少很多,可能用的非常少。 从五笔与拼音在百度的搜索比例也可以看出,的确在2015~2016年间有了转折&am…

【Hack The Box】linux练习-- Paper

HTB 学习笔记 【Hack The Box】linux练习-- Paper 🔥系列专栏:Hack The Box 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 📆首发时间:🌴2022年11月27日🌴 &#x1f36…

第一期 微信云开发小程序介绍-生活智打卡

目录 1.项目介绍 1.1 开发背景 1.2 项目简介 1.2.1 雏形 1.2.2 现状 1.2.3 展望 1.3 市场分析 1.3.1 目标用户 1.3.2 市场需求分析 1.4 系统需求 1.5 竞品分析 2.产品设计 2.1产品功能 2.1.1 智打卡 2.1.2 发现 2.1.2 我的 2.2交互设计 2.2.1 智打卡流程 2.2…

BP神经网络的梯度公式推导(三层结构)

本站原创文章,转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com目录 一. 推导目标 1.1 梯度公式目标 1.2 本文梯度公式目标 二. 网络表达式梳理 2.1 梳理三层BP神经网络的网络表达式 三. 三层BP神经网络梯度推导过程 3.1 简化推导目标 3.2 输出层权重…

模拟电路(详细版)--放大电路的频率效应(RC电路)

一、高通电路 1.1传输特性 AuA_uAu​ RR1jωC\frac{R}{R \frac {1} { j \omega C}}RjωC1​R​   (补充知识:j是复数域中的一个旋转因子) 详细求解思路:   求解AuA_uAu​就是要求输入与输出的关系。 所以AuA_uAu​ U˙oU˙…

门店数字化转型| 美容院管理系统

随着互联网信息的高速发展,行业数字化进程加快,传统美容院面临几个难题。 1、竞争激烈、拓客困难。 美业巨头迅速扩张积压中小型门店生存空间。大多数中小美容院仍旧停留在发传单、口口相传的传统渠道,辐射范围非常有限。 2、投资周期长、资…

Linux网络配置管理

目录 一、实验目的 二、实验软硬件要求 三、实验预习 1、利用ifconfig 命令实现ip地址、MAC地址的配置,并测试网络连通性 1-1查看网卡 1-2临时改写eth0网卡地址 1-3测试连通性 2、通过修改interfaces配置文件,分别实现ip地址的动态配置和静态配置…

【信号处理】时序数据中的稀疏辅助信号去噪和模式识别(Matlab代码实现)

目录 一、概述 二、算例及仿真 📢算例一: 📢算例二: 📢算例三: 📢算例四: 📢算例五: 📢算例六: 三、Matlab代码实现 一、概述…

【笔试强训】Day 4

🌈欢迎来到笔试强训专栏 (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一起打天下嘞!送给自己的一句鸡汤&#x…

初识数据结构

目录 1. 集合的框架 集合框架的重要性 数据结构的介绍 算法的介绍 容器背后对应的数据结构 2. 时间复杂度和空间复杂度 算法效率 时间复杂度 时间复杂度的概念 大O的渐进表示法 常见的时间复杂度的计算 空间复杂度 空间复杂度的概念 从本章开始又要开始新的篇章&a…

流媒体传输 - RTMP 协议报文分析

握手之后,连接开始对一个或多个 chunk stream 进行合并。创建的每个块都有一个唯一 id 对其进行关联,这个 id 叫做 chunk stream id。这些块通过网络进行传输。传递时,每个块必须被完全发送才可以发送下一块。在接收端,这些块被根…

【毕业设计】深度学习车辆颜色识别检测系统 - python opencv YOLOv5

文章目录1 前言2 实现效果3 CNN卷积神经网络4 Yolov55 数据集处理及模型训练6 最后1 前言 🔥 Hi,大家好,这里是丹成学长的毕设系列文章! 🔥 对毕设有任何疑问都可以问学长哦! 这两年开始,各个学校对毕设…

pytorch初学笔记(十三):神经网络基本结构之Sequential层的使用以及搭建完整的小型神经网络实战

目录 一、Container下Sequential层的介绍 1.1 作用 1.2 Example 二、实战神经网络搭建以及sequential的使用 2.1 前期准备 2.1.1 神经网络模型 2.1.2 求卷积过程中的padding参数 2.2 网络搭建 2.3 sequential的使用 2.4 检验网络正确性 三、完整代码 3.1 控制台输出 …