Backtrader 量化回测实践(1)—— 架构理解和MACD/KDJ混合指标

news2025/1/9 17:03:10

Backtrader 量化回测实践(1)—— 架构理解和MACD/KDJ混合指标

按Backtrader的架构组织,整理了一个代码,包括了Backtrader所有的功能点,原来总是使用SMA最简单的指标,现在稍微增加了复杂性,用MACD和KDJ两个指标综合作为操作指标,因此买入卖出操作就比较少,还有就是买入的时候,采用了限价单,整个的交易频率不高,所以图示交易点比较少,也符合多看少动的交易理念。
通过代码结合架构图,可以充分去理解整个Backtrader的功能设计思路,前面一个功能一个功能学习理解,现在把所有的功能综合在一起进行展示,小有成就感。

回测的操作过程 :

  • #1.实例初始化
  • #2.加载数据 Data feeds
  • #3.加载策略 Strategy
  • #4.加载分析器 Analyzers
  • #5.加载观察者 Observers
  • #6.设置仓位管理 Sizers
  • #7.设置佣金管理 Commission
  • #8.设置初始资金
  • #9.启动回测
  • #10.回测结果

1. Backtrader的架构

在这里插入图片描述

2. 代码

import pandas as pd
import numpy as np

import common # get data
import datetime
import backtrader as bt

# 定义Observer
class OrderObserver(bt.observer.Observer):
    lines = ('created', 'expired',)
    # 做图参数设置
    plotinfo = dict(plot=True, subplot=True, plotlinelabels=True)
    # 创建工单 * 标识,过期工单 方块 标识
    plotlines = dict(
        created=dict(marker='*', markersize=8.0, color='lime', fillstyle='full'),
        expired=dict(marker='s', markersize=8.0, color='red', fillstyle='full')
    )

    # 处理 Lines
    def next(self):
        for order in self._owner._orderspending:
            if order.data is not self.data:
                continue

            if not order.isbuy():
                continue

            # Only interested in "buy" orders, because the sell orders
            # in the strategy are Market orders and will be immediately
            # executed

            if order.status in [bt.Order.Accepted, bt.Order.Submitted]:
                self.lines.created[0] = order.created.price

            elif order.status in [bt.Order.Expired]:
                self.lines.expired[0] = order.created.price

# 定义策略
class MACD_KDJStrategy(bt.Strategy):
    # 策略参数
    params = (
        ('highperiod', 9),
        ('lowperiod', 9),
        ('kperiod', 3),
        ('dperiod', 3),
        ('me1period', 12),
        ('me2period', 26),
        ('signalperiod', 9),
        ('limitperc', 1.0), # 限价比例 ,下跌1个百分点才买入,目的可以展示Observer的过期单
        ('valid', 7), # 限价周期
        ('print', False),
        ('counter', 0),  # 计数器
    )

    def log(self, txt, dt=None):
        """ Logging function fot this strategy"""
        dt = dt or self.datas[0].datetime.date(0)
        if self.params.print:
            print("%s, %s" % (dt.isoformat(), txt))

    def __init__(self):
        # 初始化全局变量,备用
        self.dataclose = self.datas[0].close
        self.dataopen = self.datas[0].open
        self.datahigh = self.datas[0].high
        self.datalow = self.datas[0].low
        self.volume = self.datas[0].volume

        self.order = None
        self.buyprice = None
        self.buycomm = None

        # N个交易日内最高价
        self.highest = bt.indicators.Highest(self.data.high, period=self.p.highperiod)
        # N个交易日内最低价
        self.lowest = bt.indicators.Lowest(self.data.low, period=self.p.lowperiod)

        # 计算rsv值 RSV=(CLOSE- LOW) / (HIGH-LOW) * 100
        # 如果被除数0 ,为None
        self.rsv = 100 * bt.DivByZero(
            self.data_close - self.lowest, self.highest - self.lowest, zero=None
        )

        # 计算rsv的N个周期加权平均值,即K值
        self.K = bt.indicators.EMA(self.rsv, period=self.p.kperiod, plot=False)
        # D值=K值 的N个周期加权平均值
        self.D = bt.indicators.EMA(self.K, period=self.p.dperiod, plot=False)
        # J=3*K-2*D
        self.J = 3 * self.K - 2 * self.D

        # MACD策略参数
        me1 = bt.indicators.EMA(self.data, period=self.p.me1period, plot=True)
        me2 = bt.indicators.EMA(self.data, period=self.p.me2period, plot=True)

        self.macd = me1 - me2
        self.signal = bt.indicators.EMA(self.macd, period=self.p.signalperiod)
        bt.indicators.MACDHisto(self.data)

    # 订单通知处理
    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    "BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f"
                    % (order.executed.price, order.executed.value, order.executed.comm)
                )

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
                self.bar_executed_close = self.dataclose[0]
            else:
                self.log(
                    "SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f"
                    % (order.executed.price, order.executed.value, order.executed.comm)
                )
            self.bar_executed = len(self)

        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log("Order Canceled/Margin/Rejected")

        self.order = None

    # 交易通知处理
    def notify_trade(self, trade):
        if not trade.isclosed:
            return

        self.log("OPERATION PROFIT, GROSS %.2f, NET %.2f" % (trade.pnl, trade.pnlcomm))

    # 策略执行
    def next(self):
        self.log("Close, %.2f" % self.dataclose[0])
        if self.order:
            return

        # 空仓中,开仓买入
        if not self.position:
            # 买入基于MACD策略
            condition1 = self.macd[-1] - self.signal[-1] # 昨天低于signal
            condition2 = self.macd[0] - self.signal[0] # 今天高于signal
            # 买入基于KDJ策略 K值大于D值,K线向上突破D线时,为买进信号。下跌趋势中,K值小于D值,K线向下跌破D线时,为卖出信号。
            condition3 = self.K[-1] - self.D[-1] # 昨天J低于D
            condition4 = self.K[0] - self.D[0]   # 今天J高于D

            if condition1 < 0 and condition2 > 0 and condition3 < 0 and condition4 > 0 :
                self.log('BUY CREATE, %.2f' % self.dataclose[0])

                plimit = self.data.close[0] * (1.0 - self.p.limitperc / 100.0)
                valid = self.data.datetime.date(0) + datetime.timedelta(days=self.p.valid)
                self.log('BUY CREATE, %.2f' % plimit)
                # 限价购买
                self.buy(exectype=bt.Order.Limit, price=plimit, valid=valid)


        else:
            # 卖出基于MACD策略
            condition1 = self.macd[-1] - self.signal[-1]
            condition2 = self.macd[0] - self.signal[0]
            # 卖出基于KDJ策略
            condition3 = self.K[-1] - self.D[-1]
            condition4 = self.D[0] - self.D[0]

            if condition1 > 0 and condition2 < 0 and (condition3 > 0 or condition4 < 0):
                self.log("SELL CREATE, %.2f" % self.dataclose[0])
                self.order = self.sell()

    def start(self):
        # 从0 开始
        # self.params.counter += 1
        self.log('Strategy start %s' % self.params.counter)

    def nextstart(self):
        self.params.counter += 1
        self.log('Strategy nextstart %s' % self.params.counter)

    def prenext(self):
        self.params.counter += 1
        self.log('Strategy prenext  %s' % self.params.counter)

    def stop(self):
        self.params.counter += 1
        self.log('Strategy stop  %s' % self.params.counter)
        self.log('Ending Value %.2f' % ( self.broker.getvalue()))


if __name__ == "__main__":
    tframes = dict(
        days=bt.TimeFrame.Days,
        weeks=bt.TimeFrame.Weeks,
        months=bt.TimeFrame.Months,
        years=bt.TimeFrame.Years)

    #1.实例初始化
    cerebro = bt.Cerebro()

    # 2.加载数据 Data feeds
    # 加载数据到模型中,由dataframe 到 Lines 数据类型,查询10年数据到dataframe
    stock_df = common.get_data('000858.SZ','2010-01-01','2021-01-01')
    # 加载5年数据进行分析
    start_date = datetime.datetime(2016, 1, 1)  # 回测开始时间
    end_date = datetime.datetime(2020, 12, 31)  # 回测结束时间
    # bt数据转换
    data = bt.feeds.PandasData(dataname=stock_df, fromdate=start_date, todate=end_date)
    # bt加载数据
    cerebro.adddata(data)

    #3.加载策略 Strategy
    cerebro.addstrategy(MACD_KDJStrategy)

    #4.加载分析器 Analyzers
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='mysharpe')
    cerebro.addanalyzer(bt.analyzers.DrawDown,_name = 'mydrawdown')
    cerebro.addanalyzer(bt.analyzers.AnnualReturn,_name = 'myannualreturn')

    #5.加载观察者 Observers
    cerebro.addobserver(OrderObserver)

    #6.设置仓位管理 Sizers
    cerebro.addsizer(bt.sizers.FixedSize, stake=100)

    #7.设置佣金管理 Commission
    cerebro.broker.setcommission(commission=0.002)

    #8.设置初始资金
    cerebro.broker.setcash(100000)
    print("Starting Portfolio Value: %.2f" % cerebro.broker.getvalue())

    #9.启动回测
    checkstrats = cerebro.run()
    #数据源0 返回值处理
    checkstrat = checkstrats[0]

    #10.回测结果
    print("Final Portfolio Value: %.2f" % cerebro.broker.getvalue())

    print('夏普率:')
    for k, v in checkstrat.analyzers.mysharpe.get_analysis().items():
        print(k, ':', v)

    print('最大回测:')
    for k, v in checkstrat.analyzers.mydrawdown.get_analysis()['max'].items():
        print('max ', k, ':', v)

    print('年化收益率:')
    for year, ann_ret in checkstrat.analyzers.myannualreturn.get_analysis().items():
        print(year, ':', ann_ret)

    #11.回测图示
    cerebro.plot()

3.输出

Starting Portfolio Value: 100000.00
Final Portfolio Value: 109320.46
夏普率:
sharperatio : 0.24167200140493122
最大回测:
max  len : 323
max  drawdown : 4.220391363516371
max  moneydown : 4426.0
年化收益率:
2016 : 0.0
2017 : 0.03684790760000012
2018 : -0.027969386625977366
2019 : 0.07656254422728326
2020 : 0.007551367384477592

4.图示

在这里插入图片描述
做个有趣的猜测,如果对市场上所有的stock代码按程序的遍历一遍,不知道盈亏情况,比例如何?另外一个关心的就是消耗时间?

如果大家有兴趣知道结果,点赞收藏超过100 ,就做个Excel ,给大家看看效果。

仅供学习参考,不做交易操作依据。

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

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

相关文章

读写锁学习笔记

1、数据结构 读锁是共享模式&#xff0c;写锁是独占模式&#xff0c;两个锁也公用一个AQS 两者共用一个state来表示&#xff0c;state前16位表示读锁&#xff0c;后16位表示写锁 读锁操作 通过向右位移16位&#xff0c;然后进行操作 写锁操作 通过和0000 0000 0000 0000 111…

CheatEngine基础进阶篇

我们在上一篇文章里说了CE的安装,汉化以及最基础最基础的值扫描和修改方法,当然很多游戏不能通过这些简单的手段就能达到修改值的效果,因此我们还需要掌握以下几个重要的知识点: 指针寻址 上一步阐述了如何使用"代码查找"功能对付变化位置的数据地址,但这种方法…

【医学大模型 逻辑准确】PLPF:将医生诊断逻辑集成到大模型

PLPF&#xff1a;将医生诊断逻辑集成到大模型 提出背景规则建模偏好数据构建人类偏好对齐 实验结果不同模型对比论文复现 提出背景 论文&#xff1a;https://arxiv.org/pdf/2401.05695.pdf 起始问题: 如何提高大型语言模型&#xff08;LLMs&#xff09;在医疗对话生成中的逻辑…

docker (八)-dockerfile制作镜像

一 dockerfile dockerfile通常包含以下几个常用命令&#xff1a; FROM ubuntu:18.04 WORKDIR /app COPY . . RUN make . CMD python app.py EXPOSE 80 FROM 打包使用的基础镜像WORKDIR 相当于cd命令&#xff0c;进入工作目录COPY 将宿主机的文件复制到容器内RUN 打包时执…

CV论文--2024.2.21

source:CV论文--2024.2.21 1、Binary Opacity Grids: Capturing Fine Geometric Detail for Mesh-Based View Synthesis 中文标题&#xff1a;二元不透明度网格&#xff1a;捕获精细的几何细节以进行基于网格的视图合成 简介&#xff1a;尽管基于表面的视图合成算法由于其低计…

uni-app常用组件 App开发 小程序开发 这篇就够了

目录 一、uni-app官网网址 二、uni-app常用组件 1.视图容器 1.1 view 1.2 scroll-view 1.3 swiper 1.4 movable-area 1.5 movable-view 2.基础内容 2.1 icon 2.2 text 2.3 rich-text 3.表单组件 3.1 button 3.2 checkbox-group 3.3 form 3.4 input 3.5 label…

[经验] 什么是鄱阳湖旅游最主要的景点 #知识分享#知识分享

什么是鄱阳湖旅游最主要的景点 鄱阳湖是中国最大的淡水湖&#xff0c;位于江西省北部和湖南省南部。鄱阳湖旅游资源丰富&#xff0c;景色秀美&#xff0c;是游客游览江西最热门的旅游胜地之一。在所有的景点中&#xff0c;以下是鄱阳湖旅游中最主要的景点。 景点一&#xff1…

六西格玛培训对职场和财务的长期影响——张驰咨询

在当今竞争激烈的商业环境中&#xff0c;公司不断寻求提高质量和效率的方法&#xff0c;六西格玛&#xff08;Six Sigma&#xff09;作为一种旨在减少缺陷、提升产品和服务质量的管理策略&#xff0c;成为了很多企业的选择。然而&#xff0c;实施六西格玛需要专业的培训和认证&…

银河麒麟系列产品全新介绍——麒麟天御安全域管平台

麒麟天御安全域管平台是麒麟软件自主研发的新一代终端系统域管理平台,是专门针对银河麒麟操作系统环境下大规模的域用户和终端管理需求而设计。该平台聚焦用户身份验证、权限、访问控制、集中化管理、单点登录、策略等多个领域,提供组织管理、用户管理、终端管理、任务管理、软…

【python】windowslinux系统python的安装

一、python官网及下载路径 官网地址&#xff1a;Welcome to Python.org 下载路径&#xff1a;Download Python | Python.org ​​​​​​​ linux源码安装包下载&#xff1a; windows二进制安装包下载&#xff1a; 二、Linux如何安装python 2.1 单版本安装 以安装python…

防御保护---内容保护

文章目录 目录 文章目录 一.防火墙内容安全概述 二.深度识别技术&#xff08;DFI/DPI&#xff09; 深度包检测技术&#xff08;DPI&#xff09; 深度流检测技术&#xff08;DFI&#xff09; 两者区别 三.入侵防御IPS 一.防火墙内容安全概述 防火墙内容安全是防火墙的一个重…

即时设计和sketch对比

在设计领域&#xff0c;有很多易于使用的设计软件&#xff0c;每个软件都有自己的特点&#xff0c;但在使用中也会有一些限制。例如&#xff0c;传统的Sketch。Sketch是一款古老的UI设计软件。自2010年推出以来&#xff0c;已有10多年的历史&#xff0c;但它始终仅限于MAC。到目…

java中实体pojo对于布尔类型属性命名尽量别以is开头,否则 fastjson可能会导致属性读取不到

假如我们有一个场景&#xff0c;就是需要将一个对象以字符串的形式&#xff0c;也就是jsonString存到一个地方&#xff0c;比如mysql&#xff0c;或者redis的String结构。现在有一个实体&#xff0c;我们自己创建的&#xff0c;叫做CusPojo.java 有两个属性是布尔类型的&#x…

微调实操三:人类反馈对语言模型进行强化学习(RLHF)

1、前言 前面我们在《微调实操一: 增量预训练(Pretraining)》和《微调实操二: 有监督微调(Supervised Finetuning)》实操的两个章节&#xff0c;学习了PT(Continue PreTraining)增量预训练和SFT(Supervised Fine-tuning)有监督微调过程,&#xff0c;今天我们进入第三阶段的微调…

【微服务生态】Docker

文章目录 一、基础篇1. 简介2. 下载与安装3. 常用命令3.1 帮助启动类3.2 镜像命令3.3 容器命令 4. Docker 容器数据券5. Docker 镜像5.1 commit 生成镜像5.2 Docker Registry5.3 发布镜像 6. Docker 常规安装软件 二、高级篇1. Dockerfile1.1 概述1.2 基础知识1.3 Dockerfile常…

软件测试工程师经典面试题

软件测试工程师&#xff0c;和开发工程师相比起来&#xff0c;虽然前期可能不会太深&#xff0c;但是涉及的面还是比较广的。前期面试实习生或者一年左右的岗位&#xff0c;问的也主要是一些基础性的问题比较多。涉及的知识主要有MySQL数据库的使用、Linux操作系统的使用、软件…

豆粕贸易商的二次点价策略:如何在价格波动中获得收益补贴?

贸易商如何通过衍生品工具实现二次点价&#xff1f; 贸易商交易惯例&#xff1a;以豆粕贸易商和油厂签订的基差采购合同为例&#xff0c;同理可以类推至其他板块上下游企业。 按照交易惯例&#xff0c;贸易商通常会持有基差合同&#xff0c;但并不会先点价。当价格从高点到低…

饮用水、地下水深度去除全氟烷基物质的技术

摘要&#xff1a;全氟烷基物质&#xff08;PFAS&#xff09;是一组广泛存在于环境中的化学物质&#xff0c;对人体健康构成潜在威胁。本文将探讨PFAS的来源、危害以及饮用水处理中去除PFAS的方法&#xff0c;特别是离子交换树脂技术的应用&#xff0c;以期为公众提供环境保护和…

智慧水利数字孪生可视化,满屏黑科技!A3D引擎支持,免费即可搭建

2024年1月&#xff0c;在北京召开的全国水利工作会议&#xff0c;强调了要大力推进数字孪生水利建设&#xff0c;全面提升水利监测感知能力。 除此之外&#xff0c;2035年远景目标也提出&#xff1a;构建智慧水利体系&#xff0c;以流域为单元提升水情测报和智能调度能力。《数…

pom.xml常见依赖及其作用

1.org.mybatis.spring.boot下的mybatis-spring-boot-starter&#xff1a;这个依赖是mybatis和springboot的集成库&#xff0c;简化了springboot项目中使用mybatis进行持久化操作的配置和管理 2.org.projectlombok下的lombok&#xff1a;常用注解Data、NoArgsConstructor、AllA…