BackTrader 中文文档(二十九)

原文:www.backtrader.com/

开发一个指标

原文:www.backtrader.com/blog/posts/2015-07-18-developing-an-indicator/developing-an-indicator/

在对 backtrader 进行了大量的微调之后(因为它已经运行了一段时间),我决定不仅通过 GitHub 分享它,还要告诉世界它的存在,并在"Reddit"上发布了关于其存在的帖子。

在关于为什么交易/算法交易平台会突然出现的评论和关于平台是否支持多个同时交易的实时交易的私人问题之后,我得出了结论,我的孩子应该有自己的博客。

这就是我们所在的地方。但让我们专注于业务。

backtrader旨在让我快速尝试想法,并检查我的眼睛可能告诉我的是否可能存在机会。

backtrader(目前)完全是关于回测的,尚未连接到任何实时交易平台,甚至可能不会(尽管我确实相信技术实现是允许的)

当我使用表达式"尝试想法"时,我的意思是两件事:

  1. 能够快速起草一个指标,并能够直观评估其行为

  2. 无论如何,都要参与开发围绕该指标或与其他指标组合的潜在策略

我个人的交易是 100%判断性的,没有一个决定是由自动化系统做出的。但我会看看指标所说的内容。无论"指标"是否真的是一个信号,都留给了我有缺陷的人类思维。

但让我们来尝试一下。在第一次发布到 Reddit 后,我添加了一个著名的指标:

  • Trix

Stockcharts 在这里有关于 Trix 的很好的讨论:ChartSchool - Trix

让我们尝试如何做到,用尽可能少的行:

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import backtrader as bt
import backtrader.indicators as btind

class MyTrix(bt.Indicator):

    lines = ('trix',)
    params = (('period', 15),)

    def __init__(self):
        ema1 = btind.EMA(self.data, period=self.p.period)
        ema2 = btind.EMA(ema1, period=self.p.period)
        ema3 = btind.EMA(ema2, period=self.p.period)

        self.lines.trix = 100.0 * (ema3 - ema3(-1)) / ema3(-1)

Trix 指标已经投入使用。看着这一切,作为该平台的作者,我真的相信我的目标是能够快速地轻松尝试新的想法……已经实现了。

开发的详细过程:

  • lines = (‘trix’,)

    这个元组定义了指标的输出线(在本例中只有一个)。类声明开始时的这个语句在类创建和对象实例化过程中产生了大量的背景操作。

    可以说,该对象具有一个名为"lines"的属性,其中包含"trix"。

    作为额外的奖励,如果指标本身没有使用名为"trix"的名称,则可以通过"self.trix"来获得"line"。但为了清晰起见,我更喜欢"self.lines.trix"

    附加访问方法:

    • self.l.trix

    • self.lines[0] … 作为索引对应于元组中的位置

  • params = ((‘period’, 15),)

    这个元组的元组(也可以是 dict 或 OrderedDict)定义了指标接受的参数并声明了默认值。

    解析 kwargs 的负担从用户的肩上卸下来了。

    参数可以通过“self.params.xxxxx”表示法或通过速记“self.p.xxxxx”访问。

  • 计算(其中 EMA 代表指数移动平均)

    • ema1 = btind.EMA(self.data, period=self.p.period)

      新的奖金显示出来了…“self.data”。这似乎突然冒出来,但这又是指标背后进行的预处理。

      传递给指标进行计算的任何“数据”都会被拦截并放置在一个self.datas数组中,通常可以使用self.datas[0]来访问第一个数据。

      缩写确实存在,看起来像:self.data 和 self.data0 用于数组中的第一个数据。从那时起,self.data1,self.data2。

      Trix 只需要一个数据

    • ema2 = btind.EMA(ema1, period=self.p.period)

      没有太多可说的。EMA 使用 ema1 作为输入数据。

    • ema3 = btind.EMA(ema2, period=self.p.period)

      更少的话可说

    • self.lines.trix = 100.0 * (ema3 - ema3(-1)) / ema3(-1)

      首先,首先进行了简单的 1 周期百分比差值计算。

      魔术 ema3(-1)是一种表示:ema 的上一个值的记法。

      计算的结果被分配给了在类创建过程中定义的输出“line”“trix”。

轻而易举。但是如果我没有得到 Trix 正在做什么的视觉反馈(即使 Stockcharts 上有一篇很好的文章),我就不会进行“实验”。

注意

实际的 Trix 实现具有一些额外的花哨功能,主要用于美化绘图,对于本帖子没有相关性。

假设我们已经将MyTrix指标放在了一个 mytrix.py 文件中。

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import backtrader as bt
import backtrader.feeds as btfeeds

from mytrix import MyTrix

class NoStrategy(bt.Strategy):
    params = (('trixperiod', 15),)

    def __init__(self):
        MyTrix(self.data, period=self.p.trixperiod)

if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro()

    # Add a strategy
    cerebro.addstrategy(NoStrategy, trixperiod=15)

    # Create a Data Feed
    datapath = ('../datas/2006-day-001.txt')
    data = bt.feeds.BacktraderCSVData(dataname=datapath)

    # Add the Data Feed to Cerebro
    cerebro.adddata(data)

    # Run over everything
    cerebro.run()

    # Plot the result
    cerebro.plot()

并且视觉输出如下(在新窗口/标签中打开图表以获取全尺寸图像),希望显示出指标可以多快地创建和通过backtrader进行视觉评估的简单性。

image

食谱/资源

介绍

原文:www.backtrader.com/recipes/intro/

本节包含可直接应用于backtrader的配方和资源,例如指标或第三方商店、经纪人或数据源。

指标

简介

原文:www.backtrader.com/recipes/indicators/intro/

本节收集了一些指标实现,它们不是backtrader核心的一部分。

使用它们:

  • 将代码复制到你选择的文件中或直接放在你的策略上方

  • 如果复制到文件中,请从文件中导入它

    from myfile import TheIndicator` 
    
  • 并将其纳入你的策略中

    class MyStrategy(bt.Strategy):
        def __init__(self):
            self.myind = TheIndicator(self.data, param1=value1, ...)` 
    

绝对强度直方图

原文:www.backtrader.com/recipes/indicators/ash/ash/

这是一个看起来起源于外汇世界的指标,可能在 fxcodebase.com,但似乎很难追踪实际起源是什么。

潜在地首先在 LUA 中实现:

同样还提到,更新和修订的 LUA 版本可在以下位置找到:

同一站点托管了一个 MQL4 版本:

另一个 MQL5 版本可以在这里找到:

所有版本都似乎很复杂,由于语言和平台实现的原因,MQL5 版本有 227 行代码(当然包括一些注释),并且有轻微的差异,因此有必要确定一个合适的定义。在查看不同版本后的伪代码定义:

 # p0 is the current price and p1 is the previous price
  if mode is RSI:
    bulls = 0.5 * abs(p0 - p1) + p0 - p1
    bears = 0.5 * abs(p0 - p1) - p0 + p1
  elif mode is STOCH:
    bulls = p0 - lowest(period)
    bears = highest(period - p1

  avgbulls = moving_average(bulls, period)
  avgbears = moving_average(bears, period)

  smoothedbulls = moving_average(bulls, smoothing_period) / pointsize
  smoothedbears = moving_average(bears, smoothing_period) / pointsize

  ash = smoothedbulls - smoothedbears

RSI / STOCH 命名是原始实现者选择的,pointsize 也被称为 pipsize,反映了其 外汇 起源。目前尚不清楚 平滑平均值 是否必须与以前的移动平均值相同,一些版本似乎使用已经平均化的价格而不是标准价格。移动平均线是使用 整数 选择的。

一些决定

  • 该指标将使用数据提供的原始价格进行计算。如果用户希望在平均价格上运行指标,可以传递它,而不是传递原始价格。

  • RSI 模式中的 0.5 倍乘数将作为参数

  • 移动平均线不会被任何类型的 整数 选择。在 backtrader 中,可以将实际所需的移动平均线作为参数传递。

  • 平滑移动平均线,除非作为参数指定,否则将与指标中已使用的移动平均线相同

  • 除非用户将值指定为参数,否则将不使用 pointsize

这里是实现

class ASH(bt.Indicator):
    alias = ('AbsoluteStrengthOscilator',)

    lines = ('ash', 'bulls', 'bears',)  # output lines

    # customize the plotting of the *ash* line
    plotlines = dict(ash=dict(_method='bar', alpha=0.33, width=0.66))

    RSI, STOCH = range(0, 2)  # enum values for the parameter mode

    params = dict(
        period=9,
        smoothing=2,
        mode=RSI,
        rsifactor=0.5,
        movav=bt.ind.WMA,  # WeightedMovingAverage
        smoothav=None,  # use movav if not specified
        pointsize=None,  # use only if specified
    )

    def __init__(self):
        # Start calcs according to selected mode
        if self.p.mode == self.RSI:
            p0p1 = self.data - self.data(-1)  # used twice below
            half_abs_p0p1 = self.p.rsifactor * abs(p0p1)  # used twice below

            bulls = half_abs_p0p1 + p0p1
            bears = half_abs_p0p1 - p0p1
        else:
            bulls = self.data - bt.ind.Lowest(self.data, period=self.p.period)
            bears = bt.ind.Highest(self.data, period=self.p.period) - self.data

        avbulls = self.p.movav(bulls, period=self.p.period)
        avbears = self.p.movav(bears, period=self.p.period)

        # choose smoothing average and smooth the already averaged values
        smoothav = self.p.smoothav or self.p.movav  # choose smoothav
        smoothbulls = smoothav(avbulls, period=self.p.smoothing)
        smoothbears = smoothav(avbears, period=self.p.smoothing)

        if self.p.pointsize:  # apply only if it makes sense
            smoothbulls /= self.p.pointsize
            smoothbears /= self.p.pointsize

        # Assign the final values to the output lines
        self.l.bulls = smoothbulls
        self.l.bears = smoothbears
        self.l.ash = smoothbulls - smoothbears

这里展示了指标的工作方式

ASH View

康纳斯 RSI

原文:www.backtrader.com/recipes/indicators/crsi/crsi/

Google 提供的此指标参考文献:

两个来源对公式都表示同意,尽管术语不同(见下文)。 应按以下方式计算 Connors RSI

 CRSI(3, 2, 100) = [RSI(3) + RSI(Streak, 2) + PercentRank(100)] / 3

注意

TradingView 表示 ROC"变动率")必须用于 PctRank"百分比排名")的位置,但从 TradingView 自身提供的定义来看,显然是错误的。

Streak 是一个非标准术语,需要定义,让我们从源(在 TradingView 行话中称为 "UpDown") 中参考它。

  • 每日价格收盘比前一日高/低的连续天数

  • 如果某日收盘价与前一日相同,则连续数重置为0

  • 向上连续产生正值,向下连续产生负值

手头有公式,理解需要使用 PercentRank 以及对 Streak(或 UpDown)有清晰定义,创建 ConnorsRSI 指标应该轻而易举。

class Streak(bt.ind.PeriodN):
  '''
 Keeps a counter of the current upwards/downwards/neutral streak
 '''
    lines = ('streak',)
    params = dict(period=2)  # need prev/cur days (2) for comparisons

    curstreak = 0

    def next(self):
        d0, d1 = self.data[0], self.data[-1]

        if d0 > d1:
            self.l.streak[0] = self.curstreak = max(1, self.curstreak + 1)
        elif d0 < d1:
            self.l.streak[0] = self.curstreak = min(-1, self.curstreak - 1)
        else:
            self.l.streak[0] = self.curstreak = 0

class ConnorsRSI(bt.Indicator):
  '''
 Calculates the ConnorsRSI as:
 - (RSI(per_rsi) + RSI(Streak, per_streak) + PctRank(per_rank)) / 3
 '''
    lines = ('crsi',)
    params = dict(prsi=3, pstreak=2, prank=100)

    def __init__(self):
        # Calculate the components
        rsi = bt.ind.RSI(self.data, period=self.p.prsi)

        streak = Streak(self.data)
        rsi_streak = bt.ind.RSI(streak, period=self.p.pstreak)

        prank = bt.ind.PercentRank(self.data, period=self.p.prank)

        # Apply the formula
        self.l.crsi = (rsi + rsi_streak + prank) / 3.0

这里展示了指标的工作原理,还包括Streak辅助指标,以便视觉上验证实际连续数的输出。

CRSI 视图

Donchian 通道

原文:www.backtrader.com/recipes/indicators/donchian/donchian/

class DonchianChannels(bt.Indicator):
  '''
 Params Note:
 - `lookback` (default: -1)
 If `-1`, the bars to consider will start 1 bar in the past and the
 current high/low may break through the channel.
 If `0`, the current prices will be considered for the Donchian
 Channel. This means that the price will **NEVER** break through the
 upper/lower channel bands.
 '''

    alias = ('DCH', 'DonchianChannel',)

    lines = ('dcm', 'dch', 'dcl',)  # dc middle, dc high, dc low
    params = dict(
        period=20,
        lookback=-1,  # consider current bar or not
    )

    plotinfo = dict(subplot=False)  # plot along with data
    plotlines = dict(
        dcm=dict(ls='--'),  # dashed line
        dch=dict(_samecolor=True),  # use same color as prev line (dcm)
        dcl=dict(_samecolor=True),  # use same color as prev line (dch)
    )

    def __init__(self):
        hi, lo = self.data.high, self.data.low
        if self.p.lookback:  # move backwards as needed
            hi, lo = hi(self.p.lookback), lo(self.p.lookback)

        self.l.dch = bt.ind.Highest(hi, period=self.p.period)
        self.l.dcl = bt.ind.Lowest(lo, period=self.p.period)
        self.l.dcm = (self.l.dch + self.l.dcl) / 2.0  # avg of the above

资金流动指标

原文:www.backtrader.com/recipes/indicators/mfi/mfi/

参考

class MFI(bt.Indicator):
    lines = ('mfi',)
    params = dict(period=14)

    alias = ('MoneyFlowIndicator',)

    def __init__(self):
        tprice = (self.data.close + self.data.low + self.data.high) / 3.0
        mfraw = tprice * self.data.volume

        flowpos = bt.ind.SumN(mfraw * (tprice > tprice(-1)), period=self.p.period)
        flowneg = bt.ind.SumN(mfraw * (tprice < tprice(-1)), period=self.p.period)

        mfiratio = bt.ind.DivByZero(flowpos, flowneg, zero=100.0)
        self.l.mfi = 100.0 - 100.0 / (1.0 + mfiratio)

这是指标运作的一个视角

MFI 视图

随机指标(通用)

原文:www.backtrader.com/recipes/indicators/stochastic/stochastic/

backtrader已经包含了一个Stochastic指标(包括一种显示三条计算线而不仅仅是通常的两条%k%d线的变体)

但这种指标假设用于计算的数据源具有highlowclose组件。这是因为原始定义使用了这些组件。

如果想要使用不同的组件,一个首要的方法可能是创建一个数据源,将不同的组件存储在数据源的highlowclose行中。

但一个更直接的方法是拥有一个通用Stochastic指标,它接受三(3)个数据组件,并将它们用作如果它们是highlowclose组件。

下面的代码执行此操作,并通过允许自定义第二次平滑的移动平均值添加了一个不错的功能。

class Stochastic_Generic(bt.Indicator):
  '''
 This generic indicator doesn't assume the data feed has the components
 ``high``, ``low`` and ``close``. It needs three data sources passed to it,
 which whill considered in that order. (following the OHLC standard naming)
 '''
    lines = ('k', 'd', 'dslow',)
    params = dict(
        pk=14,
        pd=3,
        pdslow=3,
        movav=bt.ind.SMA,
        slowav=None,
    )

    def __init__(self):
        # Get highest from period k from 1st data
        highest = bt.ind.Highest(self.data0, period=self.p.pk)
        # Get lowest from period k from 2nd data
        lowest = bt.ind.Lowest(self.data1, period=self.p.pk)

        # Apply the formula to get raw K
        kraw = 100.0 * (self.data2 - lowest) / (highest - lowest)

        # The standard k in the indicator is a smoothed versin of K
        self.l.k = k = self.p.movav(kraw, period=self.p.pd)

        # Smooth k => d
        slowav = self.p.slowav or self.p.movav  # chose slowav
        self.l.d = slowav(k, period=self.p.pdslow)

当然,需要验证当给定相同的输入集时,该指标是否确实产生与标准指标相同的结果。下面的图表是使用这组指令创建的。

 # Generate 3 data feeds
        d0 = bt.ind.EMA(self.data.high, period=14)
        d1 = bt.ind.EMA(self.data.low, period=14)
        d2 = bt.ind.EMA(self.data.close, period=14)

        Stochastic_Generic(d0, d1, d2)  # customized stochastic

        # These two have generate the same results
        Stochastic_Generic(self.data.high, self.data.low, self.data.close)
        bt.ind.Stochastic(self.data)

这里是指标如何工作的视图

通用随机指标视图

绝对强度柱

原文:www.backtrader.com/recipes/indicators/stochrsi/stochrsi/

StockchartsInvestopedia 关于此指标有文献。

公式如下:

  • StochRSI = (RSI - min(RSI, period)) / (max(RSI, period) - min(RSI, period))

理论上,用于计算RSI的周期与随后用于查找RSI的最小值和最大值的周期相同。这意味着如果选择的周期是14(事实上的标准)用于RSI,则指标的总回溯期将为28

注意

实际回溯期将稍长,因为 14RSI 的有效回溯期为 15,因为需要比较前两个周期的收盘价格来启动计算

在任何情况下,backtrader 会自动计算所有必需的回溯期和预热期。

考虑到以下是 backtrader 中的内置指标:

  • RSI

  • Lowest(又称MaxN

  • Highest(又称MinN

根据上述公式开发StochRSI是直接的。

class StochRSI(bt.Indicator):
    lines = ('stochrsi',)
    params = dict(
        period=14,  # to apply to RSI
        pperiod=None,  # if passed apply to HighestN/LowestN, else "period"
    )

    def __init__(self):
        rsi = bt.ind.RSI(self.data, period=self.p.period)

        pperiod = self.p.pperiod or self.p.period
        maxrsi = bt.ind.Highest(rsi, period=pperiod)
        minrsi = bt.ind.Lowest(rsi, period=pperiod)

        self.l.stochrsi = (rsi - minrsi) / (maxrsi - minrsi)

这里是指标工作原理的一瞥

STOCHRSI 视图

商店/经纪人/数据源

介绍

原文:www.backtrader.com/recipes/storesbrokersdata/intro/

这一部分托管了一系列第三方存储(或单独的经纪人 / 数据源)的实现,它们不是backtrader核心的一部分。

bt-ccxt-store

原文:www.backtrader.com/recipes/storesbrokersdata/bt-ccxt-store/bt-ccxt-store/

bt-ccxt-store

作者 Dave Vallance

仓库链接:

Metaquotes MQL 5 - API

原文:www.backtrader.com/recipes/storesbrokersdata/mql5/mql5/

首次由用户 Nikolai 在这里宣布。

仓库链接:

NorgateData

原文:www.backtrader.com/recipes/storesbrokersdata/norgatedata/norgatedata/

包的链接:

Oanda v20

原文:www.backtrader.com/recipes/storesbrokersdata/oandav20/oandav20/

由用户 ftomassetti

仓库链接:

TradingView

原文:www.backtrader.com/recipes/storesbrokersdata/tradingview/tradingview/

用户为 Dave-Vallance

仓库链接:

posted @ 2024-04-15 11:22  绝不原创的飞龙  阅读(64)  评论(0编辑  收藏  举报