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

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 npimport 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'))# 处理 Linesdef next(self):for order in self._owner._orderspending:if order.data is not self.data:continueif not order.isbuy():continue# Only interested in "buy" orders, because the sell orders# in the strategy are Market orders and will be immediately# executedif order.status in [bt.Order.Accepted, bt.Order.Submitted]:self.lines.created[0] = order.created.priceelif 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].closeself.dataopen = self.datas[0].openself.datahigh = self.datas[0].highself.datalow = self.datas[0].lowself.volume = self.datas[0].volumeself.order = Noneself.buyprice = Noneself.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 ,为Noneself.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*Dself.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 - me2self.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]:returnif 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.priceself.buycomm = order.executed.commself.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:returnself.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] # 昨天低于signalcondition2 = self.macd[0] - self.signal[0] # 今天高于signal# 买入基于KDJ策略 K值大于D值,K线向上突破D线时,为买进信号。下跌趋势中,K值小于D值,K线向下跌破D线时,为卖出信号。condition3 = self.K[-1] - self.D[-1] # 昨天J低于Dcondition4 = self.K[0] - self.D[0]   # 今天J高于Dif 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 += 1self.log('Strategy start %s' % self.params.counter)def nextstart(self):self.params.counter += 1self.log('Strategy nextstart %s' % self.params.counter)def prenext(self):self.params.counter += 1self.log('Strategy prenext  %s' % self.params.counter)def stop(self):self.params.counter += 1self.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年数据到dataframestock_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.加载策略 Strategycerebro.addstrategy(MACD_KDJStrategy)#4.加载分析器 Analyzerscerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='mysharpe')cerebro.addanalyzer(bt.analyzers.DrawDown,_name = 'mydrawdown')cerebro.addanalyzer(bt.analyzers.AnnualReturn,_name = 'myannualreturn')#5.加载观察者 Observerscerebro.addobserver(OrderObserver)#6.设置仓位管理 Sizerscerebro.addsizer(bt.sizers.FixedSize, stake=100)#7.设置佣金管理 Commissioncerebro.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.mzph.cn/news/693018.shtml

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

相关文章

k8s除了可以直接运行docker镜像之外,还可以运行什么? springboot项目打包成的压缩包可以直接运行在docker容器中吗?

Kubernetes&#xff08;k8s&#xff09;主要设计用于自动部署、扩展和管理容器化应用程序。虽然它与Docker容器最为密切相关&#xff0c;Kubernetes实际上是与容器运行时技术无关的&#xff0c;这意味着它不仅仅能够管理Docker容器。Kubernetes支持多种容器运行时&#xff0c;包…

[office] EXCEL表格不能使用键盘箭头切换单元格该怎么解决- #媒体#经验分享#知识分享

EXCEL表格不能使用键盘箭头切换单元格该怎么解决? EXCEL表格不能使用键盘箭头切换单元格该怎么解决&#xff1f; 1、入下图所示的键盘。 图中红色标记“1”的地方是Scroll Lock指示灯。Scroll Lock就是“滚动锁定”的意思。当该指示灯亮起来的时候&#xff0c;在excel表格中操…

Android 面试问题 2024 版(其一)

Android 面试问题 2024 版&#xff08;其一&#xff09; 一、Java 和 Kotlin二、安卓组件三、用户界面 (UI) 开发四、安卓应用架构五、网络和数据持久性 一、Java 和 Kotlin Java 中的抽象类和接口有什么区别&#xff1f; 答&#xff1a;抽象类是不能实例化的类&#xff0c;它…

使用IntelliJ IDEA查看接口的全部实现方法

在大型Java项目中&#xff0c;经常会使用接口和抽象类进行代码设计。为了更好地了解代码结构和功能&#xff0c;我们需要快速查看一个接口的所有实现类。IntelliJ IDEA提供了一些方便的方法来实现这一目标。 1. 点击查看接口的实现子类 在IDEA中&#xff0c;你可以轻松地查看…

大话设计模式——2.简单工厂模式(Simple Factory Pattern)

定义&#xff1a;又称静态工厂方法&#xff0c;可以根据参数的不同返回不同类的实例&#xff0c;专门定义一个类&#xff08;工厂类&#xff09;来负责创建其他类的实例可通过类名直接调用&#xff0c;被创建的实例通常具有共同的父类。 UML图&#xff1a; 例子&#xff1a; 计…

计算机视觉的应用24-ResNet网络与DenseNet网络的对比学习,我们该如何选择。

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用24-ResNet网络与DenseNet网络的对比学习&#xff0c;我们该如何选择。在计算机视觉领域&#xff0c;ResNet&#xff08;残差网络&#xff09;和DenseNet&#xff08;密集网络&#xff09;都是深度学…

华清远见作业第三十九天——Qt(第一天)

思维导图&#xff1a; 登录界面&#xff1a; 代码&#xff1a; #include "mainwindow.h" #include<QToolBar> #include<QPushButton> MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {this->resize(600,400);this->setFixedSize…

Mysql 8.0新特性详解

建议使用8.0.17及之后的版本&#xff0c;更新的内容比较多。 1、新增降序索引 MySQL在语法上很早就已经支持降序索引&#xff0c;但实际上创建的仍然是升序索引&#xff0c;如下MySQL 5.7 所示&#xff0c;c2字段降序&#xff0c;但是从show create table看c2仍然是升序。8.0…

ubuntu 22.04.3 live server安装JDK21与远程编程环境和maven

ubuntu 22.04.3 live server安装JDK21与远程编程环境 一、安装jdk21 解压jdk压缩包&#xff0c;命令&#xff1a; tar -zxvf jdk-21_linux-x64_bin.tar.gz打开环境变量&#xff0c;命令&#xff1a; sudo vim /etc/profile配置环境变量 export JAVA_HOME/root/jdk-21.0.2 …

第3.3章:StarRocks数据导入--Stream Load

一、概述 Stream Load是StarRocks常见的数据导入方式&#xff0c;用户通过发送HTTP请求将本地文件或数据流导入至StarRocks中&#xff0c;该导入方式不依赖其他组件。 Stream Load作是一种同步导入方式&#xff0c;可以直接通过请求的返回值判断导入是否成功&#xff0c;无法手…

JAVA并发编程之原子性、可见性与有序性

并发编程-原子性、可见性与有序性 一、CPU的可见性 1.1 缓存一致性问题的出现 CPU处理器在处理速度上&#xff0c;远胜于内存&#xff0c;主内存执行一次内存的读写操作&#xff0c;所需要的时间足够处理器去处理上百条指令。 为了弥补处理器与主内存处理能力之间的差距&am…

(三)Spring 核心之面向切面编程(AOP)—— 代理的创建

目录 一. 前言 二. 代理的创建 2.1. 创建前准备 2.2. 获取所有的 Advisor 2.3. 创建代理的入口方法 2.4. 依据条件创建代理&#xff08;JDK 或 CGLIB&#xff09; 三. 动态代理要解决什么问题 3.1. 什么是代理 3.2. 什么是动态代理 四. 总结 一. 前言 前面两篇文章《…

MyBatis学习总结

MyBatis分页如何实现 分页分为 逻辑分页&#xff1a;查询出所有的数据缓存到内存里面&#xff0c;在从内存中筛选出需要的数据进行分页 物理分页&#xff1a;直接用数据库语法进行分页limit mybatis提供四种方法分页&#xff1a; 直接在sql语句中分页&#xff0c;传递分页参数…

网贷大数据查询多了对征信有影响吗?

网贷大数据在日常的金融借贷中起到很重要的风控作用&#xff0c;不少银行已经将大数据检测作为重要的风控环节。很多人在申贷之前都会提前了解自己的大数据信用情况&#xff0c;那网贷大数据查询多了对征信有影响吗?本文带你一起去看看。 首先要说结论&#xff1a;那就是查询网…

[极客大挑战2019]upload

该题考点&#xff1a;后缀黑名单文件内容过滤php木马的几种书写方法 phtml可以解析php代码&#xff1b;<script language"php">eval($_POST[cmd]);</script> 犯蠢的点儿&#xff1a;利用html、php空格和php.不解析<script language"php"&…

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

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

缓存驱动联邦学习架构赋能个性化边缘智能 | TMC 2024

缓存驱动联邦学习架构赋能个性化边缘智能 | TMC 2024 伴随着移动设备的普及与终端数据的爆炸式增长&#xff0c;边缘智能&#xff08;Edge Intelligence, EI&#xff09;逐渐成为研究领域的前沿。在这一浪潮中&#xff0c;联邦学习&#xff08;Federated Learning, FL&#xf…

leetcode hot100零钱兑换Ⅱ

本题可以看出也是背包问题&#xff0c;但区别于之前的01背包问题&#xff0c;这个是完全背包问题的变形形式。 下面介绍01背包和完全背包的区别与联系&#xff1a; 01背包是背包中的物品只能用一次&#xff0c;不可以重复使用&#xff0c;而完全背包则是可以重复使用。01/完全…

一个基于C#开发的、开源的特殊字符输入法

emoji表情在社交网络非常流行&#xff0c;我们在手机也非常方便输入&#xff0c;但是在PC电脑我们一般需要到归集好的网页拷贝&#xff0c;所以今天推荐一个Windows小工具&#xff0c;让你方便输入特殊字符和emoji表情。 01 项目简介 这是一个基于C#开发的开源项目&#xff0…

ansible及其模块

一、ansible是什么&#xff1f; Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可以实现。 Ansible能批量配置、部…