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

这篇具有很好参考价值的文章主要介绍了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的架构

Backtrader 量化回测实践(1)—— 架构理解和MACD/KDJ混合指标,数据分析,BackTrader,量化,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.图示

Backtrader 量化回测实践(1)—— 架构理解和MACD/KDJ混合指标,数据分析,BackTrader,量化,BackTrader
做个有趣的猜测,如果对市场上所有的stock代码按程序的遍历一遍,不知道盈亏情况,比例如何?另外一个关心的就是消耗时间?

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

仅供学习参考,不做交易操作依据。文章来源地址https://www.toymoban.com/news/detail-832638.html

到了这里,关于Backtrader 量化回测实践(1)—— 架构理解和MACD/KDJ混合指标的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 形态驱动选股支持回测评估啦!!股票量化分析工具QTYX-V2.5.4

    前言 在股票交易中,不少交易者容易走入胜率的误区,总觉得策略的胜率要很高才能盈利,其实设置好止损点后,让利润奔跑,利用赔率也是可以长期盈利的。 股票量化分析工具QTYX的形态选股功能,就是利用了的赔率原理去获利。 比如出现双底形态、箱体形态的个股不一定

    2024年02月11日
    浏览(24)
  • 深入理解Kafka:架构、设计原则及最佳实践

    Kafka是一款由Apache开发的分布式流处理平台,它最初是由LinkedIn公司在2010年开发的。从最初的消息队列到如今的分布式流处理平台Kafka经历了一个逐步演化的过程。 Kafka最开始的设计目的是解决LinkedIn内部存在的海量数据传输问题,在其不断的发展中Kafka逐渐发展成为一种可持

    2024年02月07日
    浏览(46)
  • mysql 27day 深入理解MySQL:架构、性能优化与最佳实践

    MySQL是一个流行的开源关系型数据库管理系统,深受开发者喜爱,广泛应用于各种网站和应用程序中。在本文中,我们将探索MySQL的内部架构,讨论常见的性能瓶颈,并提供一些针对性的优化建议。无论你是一名初学者还是有经验的数据库管理员,本文都将为你提供宝贵的My

    2024年02月03日
    浏览(34)
  • 相似性搜索:第 3 部分--混合倒排文件索引和产品量化

    接续前文:相似性搜索:第 2 部分:产品量化  S Imilarity 搜索 是一个问题,给定一个查询的目标是在所有数据库文档中找到与其最相似的文档。         在数据科学中,相似性搜索经常出现在NLP领域,搜索引擎或推荐系统中,其中需要检索最相关的文档或项目以进行查询

    2024年02月07日
    浏览(32)
  • 混合高斯模型的应用与理解

    混合高斯模型数学表达 混合高斯模型是一种概率模型,用于对包含多个高斯分布的数据进行建模。在混合高斯模型中,假设观测数据是由多个高斯分布组合而成的,每个高斯分布称为一个分量。每个观测数据点都由这些分量中的某一个生成,但具体是哪一个分量生成的则是不

    2024年01月24日
    浏览(26)
  • 如何理解采样量化编码

    采样:取x值--数字化时间轴 量化:取y值--数字化幅度轴 编码:按格式存储取的xy值 所谓的 采样 就是 只在时间轴上对信号进行数字化。 量化是指 在幅度轴上对信号进行数字化 。 每一个量化都是一个采样,将这么多采样进行存储就叫做 编码 。 所谓 编码 ,就是按照一定的

    2024年02月16日
    浏览(24)
  • 深度学习之神经网络量化理解

    刚开始接触神经网络,对量化是2年前那会,用NCNN和TVM在树莓派上部署一个简单的SSD网络。那个时候使用的量化脚本是参考于TensorRT和NCNN的PTQ量化(训练后量化)模式,使用交叉熵的方式对模型进行量化,最终在树莓派3B+上部署一个简单的分类模型(识别剪刀石头布静态手势

    2024年02月02日
    浏览(22)
  • Python Opencv实践 - 图像混合

               

    2024年02月13日
    浏览(31)
  • 训练后量化(PTQ) 工作流理解

    目前神经网络在许多前沿领域的应用取得了较大进展,但经常会带来很高的计算成本,对内存带宽和算力要求高。另外降低神经网络的功率和时延在现代网络集成到边缘设备时也极其关键,在这些场景中模型推理具有严格的功率和计算要求。神经网络量化是解决上述问题有效

    2024年02月08日
    浏览(20)
  • 云计算技术 — 混合云 — 技术架构

    Hybrid Cloud 需要解决 3 个核心技术问题: 多云网络:提供 Private Cloud 和 Public Cloud 之间的互联网络。 多云管理:提供一致性的操作界面。 应用(数据)迁移:提供应用迁移、扩展、弹性伸缩的能力。 其中,多云网络是 Hybrid Cloud 建设的首要基础。 Private Cloud 和 Public Cloud 都有

    2024年02月01日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包