BackTrader 中文文档(二十一)(1)

本文涉及的产品
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介: BackTrader 中文文档(二十一)


原文:www.backtrader.com/

目标订单

原文:www.backtrader.com/blog/posts/2016-09-02-target-orders/target-orders/

直到版本 1.8.10.96,通过Strategy方法:买入和卖出,在backtrader上实现了智能的投注。一切都是关于向方程中添加一个 Sizer,它负责赌注的大小。

Sizer无法决定操作是买入还是卖出。这意味着需要引入一个新概念,在其中添加一个小的智能层来做出这样的决定。

这就是Strategy中的order_target_xxx方法家族发挥作用的地方。受到zipline中的方法的启发,这些方法提供了简单指定最终目标的机会,目标可以是:

  • size -> 特定资产组合中的股票、合约数量
  • value -> 投资组合中资产的货币单位价值
  • percent -> 当前投资组合中资产的百分比价值

注意

方法的参考可以在策略参考中找到。简而言之,这些方法使用与buysell相同的签名,除了参数size被参数target替换。

在这种情况下,重点是指定最终目标,方法决定操作是买入还是卖出。相同的逻辑适用于这 3 种方法。让我们从order_target_size开始

  • 如果目标大于仓位,则会发出买入指令,差额为目标 - 仓位大小示例:
  • 仓位:0目标7 -> 买入(size=7 - 0) -> 买入(size=7)
  • 仓位:3目标7 -> 买入(size=7 - 3) -> 买入(size=4)
  • 仓位:-3目标7 -> 买入(size=7 - -3) -> 买入(size=10)
  • 仓位:-3目标-2 -> 买入(size=-2 - -3) -> 买入(size=1)
  • 如果目标小于仓位,则会发出卖出指令,差额为仓位大小 - 目标示例:
  • 仓位:0目标-7 -> 卖出(size=0 - -7) -> 卖出(size=7)
  • 仓位:3目标-7 -> 卖出(size=3 - -7) -> 卖出(size=10)
  • 仓位:-3目标-7 -> 卖出(size=-3 - -7) -> 卖出(size=4)
  • 仓位:3目标2 -> 卖出(size=3 - 2) -> 卖出(size=1)

当使用order_target_value来设置目标值时,投资组合中资产的当前价值和仓位大小都会被考虑在内,以决定最终的基础操作。推理如下:

  • 如果仓位大小为负值(空头)且目标价值必须大于当前价值,则意味着:卖出更多

因此,逻辑如下:

  • 如果目标 > 值size >=0 -> 买入
  • 如果目标 > 值size < 0 -> 卖出
  • 如果目标 < 值size >= 0 -> 卖出
  • 如果目标 < 值size\* < 0 -> 买入

order_target_percent的逻辑与order_target_value相同。该方法简单地考虑了投资组合的当前总价值,以确定资产的目标价值

示例

backtrader尝试为每个新功能提供一个示例,这不例外。没有花里胡哨,只是为了测试结果是否符合预期。这个示例位于 samples 中的order_target目录下。

示例中的逻辑相当愚蠢,只是用于测试:

  • 奇数月(一月,三月,…)中,使用作为目标(对于order_target_value,将日乘以1000
    这模拟了一个递增的目标
  • 偶数月(二月,四月,…)中,使用31 - 日作为目标
    这模拟了一个递减的目标

order_target_size

让我们看看一月二月发生了什么。

$ ./order_target.py --target-size -- plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - Order Target Size: 03
0002 - 2005-01-04 - Position Size:     03 - Value 999994.39
0002 - 2005-01-04 - Order Target Size: 04
0003 - 2005-01-05 - Position Size:     04 - Value 999992.48
0003 - 2005-01-05 - Order Target Size: 05
0004 - 2005-01-06 - Position Size:     05 - Value 999988.79
...
0020 - 2005-01-31 - Position Size:     28 - Value 999968.70
0020 - 2005-01-31 - Order Target Size: 31
0021 - 2005-02-01 - Position Size:     31 - Value 999954.68
0021 - 2005-02-01 - Order Target Size: 30
0022 - 2005-02-02 - Position Size:     30 - Value 999979.65
0022 - 2005-02-02 - Order Target Size: 29
0023 - 2005-02-03 - Position Size:     29 - Value 999966.33
0023 - 2005-02-03 - Order Target Size: 28
...

一月目标从年初的第 1 个交易日开始为3,并逐渐增加。持仓大小最初从0增加到3,然后以1的增量移动。

结束一月时,最后的order_target31,当进入二月的第 1 天时报告了该持仓大小,当新的目标大小请求为30时,并随着持仓以1的递减变化。

order_target_value

预计目标值会有类似的行为。

$ ./order_target.py --target-value --plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - data value 0.00
0001 - 2005-01-03 - Order Target Value: 3000.00
0002 - 2005-01-04 - Position Size:     78 - Value 999854.14
0002 - 2005-01-04 - data value 2853.24
0002 - 2005-01-04 - Order Target Value: 4000.00
0003 - 2005-01-05 - Position Size:     109 - Value 999801.68
0003 - 2005-01-05 - data value 3938.17
0003 - 2005-01-05 - Order Target Value: 5000.00
0004 - 2005-01-06 - Position Size:     138 - Value 999699.57
...
0020 - 2005-01-31 - Position Size:     808 - Value 999206.37
0020 - 2005-01-31 - data value 28449.68
0020 - 2005-01-31 - Order Target Value: 31000.00
0021 - 2005-02-01 - Position Size:     880 - Value 998807.33
0021 - 2005-02-01 - data value 30580.00
0021 - 2005-02-01 - Order Target Value: 30000.00
0022 - 2005-02-02 - Position Size:     864 - Value 999510.21
0022 - 2005-02-02 - data value 30706.56
0022 - 2005-02-02 - Order Target Value: 29000.00
0023 - 2005-02-03 - Position Size:     816 - Value 999130.05
0023 - 2005-02-03 - data value 28633.44
0023 - 2005-02-03 - Order Target Value: 28000.00
...

还有一行额外的信息,告诉实际的数据值(在投资组合中)是多少。这有助于确定是否已达到目标值

初始目标为3000.0,报告的初始值为2853.24。这里的问题是这是否足够接近。答案是

  • 该示例在每日 K 线结束时使用Market订单和最后可用价格来计算目标大小,以满足目标价值
  • 执行然后使用下一天的open价格,这不太可能是前一天的close

以任何其他方式进行将意味着在欺骗自己。

下一个目标值最终值更接近:40003938.17

当转变为二月时,目标价值开始从31000减少到3000029000数据值也随之从30580.00减少到30706.56,然后到28633.44。等待:

  • 30580 -> 30706.56是一个正向变化。
    确实。在这种情况下,计算出的目标值大小遇到了将值提升到30706.56开盘价

如何避免这种影响:

  • 该示例使用Market类型执行订单,这种效果无法避免。
  • 方法order_target_xxx允许指定执行类型价格
    可以指定Limit作为执行订单,并让价格为close价格(如果没有提供其他价格,则由方法选择),甚至提供特定定价。

order_target_value

在这种情况下,它只是当前投资组合价值的一个百分比。

$ ./order_target.py --target-percent --plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - data percent 0.00
0001 - 2005-01-03 - Order Target Percent: 0.03
0002 - 2005-01-04 - Position Size:     785 - Value 998532.05
0002 - 2005-01-04 - data percent 0.03
0002 - 2005-01-04 - Order Target Percent: 0.04
0003 - 2005-01-05 - Position Size:     1091 - Value 998007.44
0003 - 2005-01-05 - data percent 0.04
0003 - 2005-01-05 - Order Target Percent: 0.05
0004 - 2005-01-06 - Position Size:     1381 - Value 996985.64
...
0020 - 2005-01-31 - Position Size:     7985 - Value 991966.28
0020 - 2005-01-31 - data percent 0.28
0020 - 2005-01-31 - Order Target Percent: 0.31
0021 - 2005-02-01 - Position Size:     8733 - Value 988008.94
0021 - 2005-02-01 - data percent 0.31
0021 - 2005-02-01 - Order Target Percent: 0.30
0022 - 2005-02-02 - Position Size:     8530 - Value 995005.45
0022 - 2005-02-02 - data percent 0.30
0022 - 2005-02-02 - Order Target Percent: 0.29
0023 - 2005-02-03 - Position Size:     8120 - Value 991240.75
0023 - 2005-02-03 - data percent 0.29
0023 - 2005-02-03 - Order Target Percent: 0.28
...

信息已更改,以查看投资组合中数据代表的%

示例用法

$ ./order_target.py --help
usage: order_target.py [-h] [--data DATA] [--fromdate FROMDATE]
                       [--todate TODATE] [--cash CASH]
                       (--target-size | --target-value | --target-percent)
                       [--plot [kwargs]]
Sample for Order Target
optional arguments:
  -h, --help            show this help message and exit
  --data DATA           Specific data to be read in (default:
                        ../../datas/yhoo-1996-2015.txt)
  --fromdate FROMDATE   Starting date in YYYY-MM-DD format (default:
                        2005-01-01)
  --todate TODATE       Ending date in YYYY-MM-DD format (default: 2006-12-31)
  --cash CASH           Ending date in YYYY-MM-DD format (default: 1000000)
  --target-size         Use order_target_size (default: False)
  --target-value        Use order_target_value (default: False)
  --target-percent      Use order_target_percent (default: False)
  --plot [kwargs], -p [kwargs]
                        Plot the read data applying any kwargs passed For
                        example: --plot style="candle" (to plot candles)
                        (default: None)

示例代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import argparse
from datetime import datetime
import backtrader as bt
class TheStrategy(bt.Strategy):
    '''
    This strategy is loosely based on some of the examples from the Van
    K. Tharp book: *Trade Your Way To Financial Freedom*. The logic:
      - Enter the market if:
        - The MACD.macd line crosses the MACD.signal line to the upside
        - The Simple Moving Average has a negative direction in the last x
          periods (actual value below value x periods ago)
     - Set a stop price x times the ATR value away from the close
     - If in the market:
       - Check if the current close has gone below the stop price. If yes,
         exit.
       - If not, update the stop price if the new stop price would be higher
         than the current
    '''
    params = (
        ('use_target_size', False),
        ('use_target_value', False),
        ('use_target_percent', False),
    )
    def notify_order(self, order):
        if order.status == order.Completed:
            pass
        if not order.alive():
            self.order = None  # indicate no order is pending
    def start(self):
        self.order = None  # sentinel to avoid operrations on pending order
    def next(self):
        dt = self.data.datetime.date()
        portfolio_value = self.broker.get_value()
        print('%04d - %s - Position Size:     %02d - Value %.2f' %
              (len(self), dt.isoformat(), self.position.size, portfolio_value))
        data_value = self.broker.get_value([self.data])
        if self.p.use_target_value:
            print('%04d - %s - data value %.2f' %
                  (len(self), dt.isoformat(), data_value))
        elif self.p.use_target_percent:
            port_perc = data_value / portfolio_value
            print('%04d - %s - data percent %.2f' %
                  (len(self), dt.isoformat(), port_perc))
        if self.order:
            return  # pending order execution
        size = dt.day
        if (dt.month % 2) == 0:
            size = 31 - size
        if self.p.use_target_size:
            target = size
            print('%04d - %s - Order Target Size: %02d' %
                  (len(self), dt.isoformat(), size))
            self.order = self.order_target_size(target=size)
        elif self.p.use_target_value:
            value = size * 1000
            print('%04d - %s - Order Target Value: %.2f' %
                  (len(self), dt.isoformat(), value))
            self.order = self.order_target_value(target=value)
        elif self.p.use_target_percent:
            percent = size / 100.0
            print('%04d - %s - Order Target Percent: %.2f' %
                  (len(self), dt.isoformat(), percent))
            self.order = self.order_target_percent(target=percent)
def runstrat(args=None):
    args = parse_args(args)
    cerebro = bt.Cerebro()
    cerebro.broker.setcash(args.cash)
    dkwargs = dict()
    if args.fromdate is not None:
        dkwargs['fromdate'] = datetime.strptime(args.fromdate, '%Y-%m-%d')
    if args.todate is not None:
        dkwargs['todate'] = datetime.strptime(args.todate, '%Y-%m-%d')
    # data
    data = bt.feeds.YahooFinanceCSVData(dataname=args.data, **dkwargs)
    cerebro.adddata(data)
    # strategy
    cerebro.addstrategy(TheStrategy,
                        use_target_size=args.target_size,
                        use_target_value=args.target_value,
                        use_target_percent=args.target_percent)
    cerebro.run()
    if args.plot:
        pkwargs = dict(style='bar')
        if args.plot is not True:  # evals to True but is not True
            npkwargs = eval('dict(' + args.plot + ')')  # args were passed
            pkwargs.update(npkwargs)
        cerebro.plot(**pkwargs)
def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Sample for Order Target')
    parser.add_argument('--data', required=False,
                        default='../../datas/yhoo-1996-2015.txt',
                        help='Specific data to be read in')
    parser.add_argument('--fromdate', required=False,
                        default='2005-01-01',
                        help='Starting date in YYYY-MM-DD format')
    parser.add_argument('--todate', required=False,
                        default='2006-12-31',
                        help='Ending date in YYYY-MM-DD format')
    parser.add_argument('--cash', required=False, action='store',
                        type=float, default=1000000,
                        help='Ending date in YYYY-MM-DD format')
    pgroup = parser.add_mutually_exclusive_group(required=True)
    pgroup.add_argument('--target-size', required=False, action='store_true',
                        help=('Use order_target_size'))
    pgroup.add_argument('--target-value', required=False, action='store_true',
                        help=('Use order_target_value'))
    pgroup.add_argument('--target-percent', required=False,
                        action='store_true',
                        help=('Use order_target_percent'))
    # Plot options
    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const=True,
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example:\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))
    if pargs is not None:
        return parser.parse_args(pargs)
    return parser.parse_args()
if __name__ == '__main__':
    runstrat()

将期货滚动

原文:www.backtrader.com/blog/posts/2016-08-31-rolling-over-futures/rolling-futures-over/

并非每个提供商都为可以交易的工具提供连续未来数据。有时,提供的数据是仍然有效的到期日期的数据,即:仍在交易的数据

当涉及到回测时,这并不是很有帮助,因为数据分散在几个不同的工具中,而且还… 时间上重叠

能够将那些过去的工具数据正确地连接成连续流可以减轻痛苦。问题在于:

  • 没有一条法律规定如何最好地将不同到期日期的期货合并成一个连续的期货

一些文献,由SierraChart提供:

  • 我的链接

滚动数据源

backtrader在 1.8.10.99 版本中添加了一个功能,可以将不同到期日期的期货数据合并成一个连续的期货:

import backtrader as bt
cerebro = bt.Cerebro()
data0 = bt.feeds.MyFeed(dataname='Expiry0')
data1 = bt.feeds.MyFeed(dataname='Expiry1')
...
dataN = bt.feeds.MyFeed(dataname='ExpiryN')
drollover = cerebro.rolloverdata(data0, data1, ..., dataN, name='MyRoll', **kwargs)
cerebro.run()

下面解释了可能的\*\*kwargs

也可以通过直接访问RollOver数据源来完成(如果进行了子类化,则会有所帮助):

import backtrader as bt
cerebro = bt.Cerebro()
data0 = bt.feeds.MyFeed(dataname='Expiry0')
data1 = bt.feeds.MyFeed(dataname='Expiry1')
...
dataN = bt.feeds.MyFeed(dataname='ExpiryN')
drollover = bt.feeds.RollOver(data0, data1, ..., dataN, dataname='MyRoll', **kwargs)
cerebro.adddata(drollover)
cerebro.run()

下面解释了可能的\*\*kwargs

使用RollOver时,使用dataname分配名称。这是所有数据源用于传递名称/标记的标准参数。在这种情况下,它被重用以为所有滚动期货分配一个公共名称。

对于cerebro.rolloverdata,名称使用name分配给一个数据源,这已经是该方法的一个命名参数

底线:

  • 数据源通常创建,但添加到cerebro
  • 这些数据源作为输入提供给bt.feeds.RollOver
    也给出了一个dataname,主要用于识别目的。
  • 然后将这个滚动数据源添加到cerebro

滚动的选项

提供了两个参数来控制滚动过程

  • checkdate(默认值:None)这必须是一个callable,具有以下签名:
checkdate(dt, d):` 
  • 其中:
  • dt是一个datetime.datetime对象
  • d是当前活跃期货的数据源
  • 预期的返回值:
  • True:只要可调用返回这个,就可以切换到下一个未来
    如果某种商品在三月的第三个星期五到期,则checkdate可以在到期发生的整个周返回True
  • False:到期无法发生
  • checkcondition(默认值:None注意:只有当checkdate返回True时才会调用此函数如果是None,这将内部求值为True(执行滚动)否则,这必须是一个callable,具有以下签名:
checkcondition(d0, d1)` 
  • 其中:
  • d0是当前活跃期货的数据源
  • d1是下一个到期的数据源
  • 预期的返回值:
  • True:滚动到下一个未来
    继续使用checkdate的示例,这可以说明如果d0volume已经小于d1的 volume,则可以进行滚动
  • False:到期无法发生

子类化RollOver

如果指定可调用对象不够,总是可以通过子类化RollOver。要子类化的方法:

  • def _checkdate(self, dt, d):
    与上面同名参数的签名相匹配。预期的返回值也是相同的。
  • def _checkcondition(self, d0, d1)
    与上面同名参数的签名相匹配。预期的返回值也是相同的。


BackTrader 中文文档(二十一)(2)https://developer.aliyun.com/article/1505417

相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
|
8月前
|
存储 算法 Python
BackTrader 中文文档(二十一)(3)
BackTrader 中文文档(二十一)
63 0
|
8月前
BackTrader 中文文档(二十一)(2)
BackTrader 中文文档(二十一)
62 0
|
8月前
|
索引
BackTrader 中文文档(二十一)(4)
BackTrader 中文文档(二十一)
92 0
|
8月前
BackTrader 中文文档(十九)(2)
BackTrader 中文文档(十九)
29 0
|
8月前
BackTrader 中文文档(十九)(1)
BackTrader 中文文档(十九)
55 0
|
8月前
BackTrader 中文文档(十九)(3)
BackTrader 中文文档(十九)
46 0
|
8月前
BackTrader 中文文档(十九)(4)
BackTrader 中文文档(十九)
38 0
|
8月前
BackTrader 中文文档(十八)(1)
BackTrader 中文文档(十八)
40 0
|
8月前
|
存储 缓存 测试技术
BackTrader 中文文档(十八)(4)
BackTrader 中文文档(十八)
75 0
|
8月前
|
测试技术 索引 Python
BackTrader 中文文档(十八)(2)
BackTrader 中文文档(十八)
51 0