BackTrader 中文文档(十六)(2)

简介: BackTrader 中文文档(十六)

BackTrader 中文文档(十六)(1)https://developer.aliyun.com/article/1505374

基金跟踪示例

可选参数:

-h,–help 显示此帮助消息并退出

–data0 DATA0 要读取的数据(默认值:

../../datas/2005-2006-day-001.txt)

–fromdate FROMDATE 日期[时间]的格式为 YYYY-MM-DD[THH:MM:SS](默认值:

–todate TODATE 日期[时间]的格式为 YYYY-MM-DD[THH:MM:SS](默认值:

–cerebro kwargs 以键=值格式的 kwargs(默认值:)

–broker kwargs 以键=值格式的 kwargs(默认值:)

–sizer kwargs 以键=值格式的 kwargs(默认值:)

–strat kwargs 以键=值格式的 kwargs(默认值:)

–plot [kwargs] 以键=值格式的 kwargs(默认值:)`

## Sample Code

`from future import (absolute_import, division, print_function,

unicode_literals)

导入 argparse

导入 datetime

导入 backtrader as bt

类 St(bt.SignalStrategy):

参数 = 字典(
    cash2add=None,
    cashonday=15,
    pfast=10,
    pslow=30,
    trade=False,
)
def __init__(self):
    self.add_timer(when=bt.Timer.SESSION_END, monthdays=[self.p.cashonday])
    sma1 = bt.ind.SMA(period=self.p.pfast)
    sma2 = bt.ind.SMA(period=self.p.pslow)
    signal = bt.ind.CrossOver(sma1, sma2)
    if self.p.trade:
        self.signal_add(bt.SIGNAL_LONGSHORT, signal)
def notify_timer(self, timer, when, *args, **kwargs):
    # 无需检查计时器,只有一个
    if self.p.cash2add is not None:
        self.broker.add_cash(self.p.cash2add)
def next(self):
    pass

def runstrat(args=None):

args = parse_args(args)
cerebro = bt.Cerebro()
# 数据源关键字参数
kwargs = dict()
# 从/到日期解析
dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
    if a:
        strpfmt = dtfmt + tmfmt * ('T' in a)
        kwargs[d] = datetime.datetime.strptime(a, strpfmt)
data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
cerebro.adddata(data0)
# 经纪人
cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))
# 大小规模
cerebro.addsizer(bt.sizers.PercentSizer,
                **eval('dict(' + args.sizer + ')'))
# 策略
cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))
cerebro.addobserver(bt.observers.FundValue)
cerebro.addobserver(bt.observers.FundShares)
ankwargs = dict(timeframe=bt.TimeFrame.Years)
cerebro.addanalyzer(bt.analyzers.TimeReturn, **ankwargs)
cerebro.addanalyzer(bt.analyzers.TimeReturn, fund=True, **ankwargs)
cerebro.addanalyzer(bt.analyzers.TimeReturn, fund=False, **ankwargs)
# 执行
cerebro.run(**eval('dict(' + args.cerebro + ')'))
if args.plot:  # 如果要求绘图,则绘制
    cerebro.plot(**eval('dict(' + args.plot + ')'))

def parse_args(pargs=None):

parser = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    描述=(
        '基金跟踪示例'
    )
)
parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                    required=False, help='要读取的数据')
# 日期的默认值
parser.add_argument('--fromdate', required=False, default='',
                    help='以 YYYY-MM-DD[THH:MM:SS] 格式的日期[时间]')
parser.add_argument('--todate', required=False, default='',
                    help='以 YYYY-MM-DD[THH:MM:SS] 格式的日期[时间]')
parser.add_argument('--cerebro', required=False, default='',
                    metavar='kwargs', help='以键=值格式的关键字参数')
parser.add_argument('--broker', required=False, default='',
                    metavar='kwargs', help='以键=值格式的关键字参数')
parser.add_argument('--sizer', required=False, default='',
                    metavar='kwargs', help='以键=值格式的关键字参数')
parser.add_argument('--strat', required=False, default='',
                    metavar='kwargs', help='以键=值格式的关键字参数')
parser.add_argument('--plot', required=False, default='',
                    nargs='?', const='{}',
                    metavar='kwargs', help='以键=值格式的关键字参数')
return parser.parse_args(pargs)

if name == ‘main’:

runstrat()`
# 发布版本 1.9.51.121
> 原文:[`www.backtrader.com/blog/posts/2017-06-12-release-1.9.51.121/release-1.9.51.121/`](https://www.backtrader.com/blog/posts/2017-06-12-release-1.9.51.121/release-1.9.51.121/)
即使是一个次要版本,也有一些有趣的东西,可能值得为它们撰写专门的博客文章。
## `linealias`
[Pull-Request #320](https://github.com/mementum/backtrader/pull/320) 包括指标`相对动量指数`(或`RMI`),根据文献,这是`RSI`的演变,它:
+   考虑*up*和*down*周期,回溯期大于`1`
因此,与其让一个指标重复`RSI`的大部分内容,做两件事似乎更有用:
1.  扩展`RSI`(以及子指标如`UpDay`和`DownDay`)以支持大于*1*的回溯期。`RMI`可以作为一个简单具有一些不同默认值的子类来实现。
1.  `RMI`指标的逻辑名称是`rmi`,但`RSI`已经决定了名称为`rsi`。通过添加一个名为`linealias`的新功能来解决这个问题
`RMI`的实现看起来是这样的:
```py
class RelativeMomentumIndex(RSI):
    alias = ('RMI', )
    linealias = (('rsi', 'rmi',),)  # add an alias for this class rmi -> rsi
    plotlines = dict(rsi=dict(_name='rmi'))  # change line plotting name

为基类添加了rsi线的别名,名称为rmi。如果有人想要创建一个子类并使用名称rmi,现在是可能的。

此外,rsi线的绘图名称也更改为rmi。还有一种替代实现方式:

class RelativeMomentumIndex(RSI):
    alias = ('RMI', )
    linesoverrride = True  # allow redefinition of the lines hierarcy
    lines = ('rmi',)  # define the line
    linealias = (('rmi', 'rsi',),)  # add an alias for base class rsi -> rmi

在这里,不再考虑RSI的现有层次结构,而是使用lines来定义唯一的名为rmi的线。不需要定义绘图名称,因为唯一的线现在具有预期的名称。

但基类将无法填充值,因为它期望有一个名为rsi的线。因此添加了一个反向别名,让它找到该线。

交互式经纪人优化

使用交互式经纪人作为优化数据源的实时连接并未被预见。然而,有用户尝试了这种方法,导致了出现了速度违规。原因在于交互式经纪人数据源标记为live数据源,允许系统绕过某些事情,比如数据预加载。

没有预加载,每次优化实例都会尝试重新从交互式经纪人下载相同的历史数据。考虑到这一点,很明显数据源可以查看用户是否只请求历史下载,在这种情况下不报告自己为live,允许平台预加载数据并在优化实例之间共享。

查看社区帖子。使用 IBStore 进行优化会导致冗余连接/下载

平均趋势蜡烛图

这个其他社区帖子试图开发平滑蜡烛图作为指标:Develop Heikinashi Indicators,面临着一些问题,因为需要一个种子值,这可以在指标的prenext阶段完成。

作为传统蜡烛图的一个有趣的显示替代方案,这已经被实现为一个过滤器,允许修改数据源以真正提供平滑蜡烛图。就像这样:

data0 = MyDataFeed(dataname='xxx', timeframe=bt.TimeFrame.Days, compression=1)
data0.addfilter(bt.filters.HeikinAshi)
cerebro.adddata(data0)

任何人都可以通过这段代码快速比较蜡烛图:

data0 = MyDataFeed(dataname='xxx', timeframe=bt.TimeFrame.Days, compression=1)
cerebro.adddata(data0)
data1 = data0.clone()
data1.addfilter(bt.filters.HeikinAshi)
cerebro.adddata(data1)

要绘制蜡烛图,请记住执行:

cerebro.plot(style='candle')

使用样本每日数据来自 2005 年和 2006 年的来源。

并稍微放大以更好地欣赏差异

允许辅助演员重新调整 y 轴的比例

数据源的轴始终将主数据源用作比例所有者,因为数据始终是视图中最重要的部分。例如,考虑一下布林带,可能会导致顶部带远离数据的最大值,并且允许此带重新调整图表,将减少数据在图表中占用的空间,这是不希望的。

现在可以使用plotylimited来控制行为,例如:

...
data0 = MyDataFeed(dataname='xxx', timeframe=bt.TimeFrame.Days, compression=1)
data0.plotinfo.plotlog = False  # allow other actors to resize the axis
...

在下图中,底部的数据源使用了plotylimited=False进行绘制。布林带不会超出图表,因为它们会影响缩放,并且一切都适合图表中。

这在社区中也有所评论。How max - min plot boundaries are set?

半对数图(也称为对数图)

现在可以使用半对数刻度(y 轴刻度)绘制单个轴。例如:

...
data0 = MyDataFeed(dataname='xxx', timeframe=bt.TimeFrame.Days, compression=1)
data0.plotinfo.plotlog = True
data0.plotinfo.plotylimited = True
cerebro.adddata(data0)
...

这意味着由此数据源控制的轴将使用对数刻度,但其他轴不会,因此

  • 在数据上绘制移动平均线也将使用该刻度进行绘制
  • 随机指标(位于不同轴上且具有不同刻度)仍将以线性方式绘制

注意

请注意,使用了plotylimited=True。这是为了让matplotlib正确地计算对数图表的限制(因为刻度是 10 的幂)以适应图表中的内容。

简单地比较Yahoo数据的长期期间的示例。

允许plotmaster指向自身

在同一轴上绘制多个数据源已经是可能的,但是一个小麻烦不允许设置plotinfo.plotmaster值的干净循环。之前必须执行以下操作:

mydatas = []
data = MyDataFeed(dataname=mytickers[0], timeframe=..., compression=...)
mydatafeeds.append(data)
for ticker in mytickers[1:]
    data = MyDataFeed(dataname=ticker, timeframe=..., compression=...)
    mydatafeeds.append(data)
    data.plotinfo.plotmaster = mydatas[0]

现在可以使用更清晰的循环:

mydatas = []
for ticker in mytickers:
    data = MyDataFeed(dataname=ticker, timeframe=..., compression=...)
    mydatafeeds.append(data)
    data.plotinfo.plotmaster = mydatas[0]

并且dnames已经被记录了

引用数据源的名称已经可用,但它被忽略了,没有出现在文档中,因此它是一个隐藏的宝藏。策略中的 dnames 属性支持点表示法和*[]*表示法(实际上是一个 dict 子类)。如果我们首先添加一些数据源:

mytickers = ['YHOO', 'IBM', 'AAPL']
for t in mytickers:
  d = bt.feeds.YahooFinanceData(dataname=t, fromdate=..., name=t.lower())

在策略中稍后可以执行以下操作:

def __init__(self):
  yhoosma = bt.ind.SMA(self.dnames.yhoo, period=20)
  aaplsma = bt.ind.SMA(self.dnames['aapl'], period=30)
  # or even go over the keys/items/values like in a regular dict
  # for example with a dictionary comprehension
  stocs = {name: bt.ind.Stochastic(data) for name, data in self.dnames.items()}

结论

一个包含小改动的小版本发布,增加了一些巧妙的功能。

策略选择再访

原文:www.backtrader.com/blog/posts/2017-05-16-stsel-revisited/stsel-revisited/

最初的策略选择方法使用了两种策略,它们是手动注册的,以及一个简单的 [0, 1] 列表来决定哪个是策略的目标。

由于 Python 提供了许多元类的反射可能性,可以实际上自动化这种方法。让我们使用 装饰器 方法来做这件事,在这种情况下可能是最不侵入性的方法(不需要为策略定义 元类

重新设计工厂

工厂现在:

  • 在策略之前声明
  • 具有一个空的 _STRATS 类属性(之前它包含要返回的策略)
  • 具有一个 register 类方法,将用作装饰器,并接受一个参数,该参数将添加到 _STRATS
  • 具有一个 COUNT 类方法,将返回一个迭代器(实际上是一个 range),其中包含要优化的可用策略的计数
  • 对实际工厂方法没有任何更改:__new__,它仍然使用 idx 参数来返回 _STRATS 类属性中给定索引处的任何内容
class StFetcher(object):
    _STRATS = []
    @classmethod
    def register(cls, target):
        cls._STRATS.append(target)
    @classmethod
    def COUNT(cls):
        return range(len(cls._STRATS))
    def __new__(cls, *args, **kwargs):
        idx = kwargs.pop('idx')
        obj = cls._STRATSidx
        return obj

如下:

  • StFetcher 策略工厂不再包含任何硬编码的策略

装饰待优化的策略

示例中的策略不需要重新设计。使用 StFetcherregister 方法进行装饰就足以将它们添加到选择池中。

@StFetcher.register
class St0(bt.SignalStrategy):

@StFetcher.register
class St1(bt.SignalStrategy):

利用 COUNT

在将策略工厂添加到系统中并使用 optstrategy 时,过去的手动 [0, 1] 列表可以完全替换为对 StFetcher.COUNT() 的透明调用。硬编码已经结束。

cerebro.optstrategy(StFetcher, idx=StFetcher.COUNT())

一个样本运行

$ ./stselection-revisited.py --optreturn
Strat 0 Name OptReturn:
  - analyzer: OrderedDict([(u'rtot', 0.04847392369449283), (u'ravg', 9.467563221580632e-05), (u'rnorm', 0.02414514457151587), (u'rnorm100', 2.414514457151587)])
Strat 1 Name OptReturn:
  - analyzer: OrderedDict([(u'rtot', 0.05124714332260593), (u'ravg', 0.00010009207680196471), (u'rnorm', 0.025543999840699633), (u'rnorm100', 2.5543999840699634)])

我们的两种策略已经运行并且(如预期)产生了不同的结果。

注意

该示例很简单,但已经在所有可用的 CPU 上运行。使用 --maxpcpus=1 执行将更快。对于更复杂的情况,使用所有 CPU 将很有用。

结论

选择已经完全自动化。与之前一样,可以想象类似于查询数据库以获取可用策略数量,然后逐个获取策略的方法。


BackTrader 中文文档(十六)(3)https://developer.aliyun.com/article/1505377

相关文章
|
Unix 索引 Python
BackTrader 中文文档(一)(2)
BackTrader 中文文档(一)
981 0
|
索引
BackTrader 中文文档(六)(2)
BackTrader 中文文档(六)
409 0
|
9月前
|
数据采集 机器学习/深度学习 数据可视化
Python量化交易:结合爬虫与TA-Lib技术指标分析
Python量化交易:结合爬虫与TA-Lib技术指标分析
|
机器学习/深度学习 算法 机器人
BackTrader 中文文档(一)(1)
BackTrader 中文文档(一)
2123 0
|
存储 数据库连接 数据库
BackTrader 中文文档(三)(2)
BackTrader 中文文档(三)
435 0
|
存储 编解码 API
BackTrader 中文文档(四)(1)
BackTrader 中文文档(四)
366 1
|
存储 缓存 Shell
BackTrader 中文文档(二)(3)
BackTrader 中文文档(二)
764 0
BackTrader 中文文档(一)(4)
BackTrader 中文文档(一)
330 0
|
Python
BackTrader 中文文档(一)(3)
BackTrader 中文文档(一)
347 0
|
编解码 C++ 索引
BackTrader 中文文档(九)(3)
BackTrader 中文文档(九)
624 0

热门文章

最新文章