BackTrader 中文文档(八)(2)

本文涉及的产品
数据可视化DataV,5个大屏 1个月
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介: BackTrader 中文文档(八)

BackTrader 中文文档(八)(1)https://developer.aliyun.com/article/1505277

执行类型:StopLimit

设置了比信号价格高 1%的停止价格。但限价设置在信号(收盘)价格的 0.5%以上,这可能被解释为:等待力量显现,但不要买入顶峰。等待下跌。

有效期限制为 20(日历)天

elif self.p.exectype == 'StopLimit':
                price = self.data.close * (1.0 + self.p.perc1 / 100.0)
                plimit = self.data.close * (1.0 + self.p.perc2 / 100.0)
                self.buy(exectype=bt.Order.StopLimit, price=price, valid=valid,
                         plimit=plimit)
                if self.p.valid:
                    txt = ('BUY CREATE, exectype StopLimit, price %.2f,'
                           ' valid: %s, pricelimit: %.2f')
                    self.log(txt % (price, valid.strftime('%Y-%m-%d'), plimit))
                else:
                    txt = ('BUY CREATE, exectype StopLimit, price %.2f,'
                           ' pricelimit: %.2f')
                    self.log(txt % (price, plimit))

输出图表。

命令行和输出:

$ ./order-execution-samples.py --exectype StopLimit --perc1 1 --perc2 0.5 --valid 20
2006-01-26T23:59:59+00:00, BUY CREATE, exectype StopLimit, price 3677.83, valid: 2006-02-15, pricelimit: 3659.63
2006-01-26T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-02-03T23:59:59+00:00, BUY EXECUTED, Price: 3659.63, Cost: 3659.63, Comm 0.00
2006-03-02T23:59:59+00:00, SELL CREATE, 3763.73
2006-03-02T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-03-03T23:59:59+00:00, SELL EXECUTED, Price: 3763.95, Cost: 3763.95, Comm 0.00
2006-03-10T23:59:59+00:00, BUY CREATE, exectype StopLimit, price 3836.44, valid: 2006-03-30, pricelimit: 3817.45
2006-03-10T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-03-21T23:59:59+00:00, BUY EXECUTED, Price: 3817.45, Cost: 3817.45, Comm 0.00
2006-03-28T23:59:59+00:00, SELL CREATE, 3811.45
2006-03-28T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-03-29T23:59:59+00:00, SELL EXECUTED, Price: 3811.85, Cost: 3811.85, Comm 0.00
2006-03-30T23:59:59+00:00, BUY CREATE, exectype StopLimit, price 3913.36, valid: 2006-04-19, pricelimit: 3893.98
2006-03-30T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-04-19T23:59:59+00:00, BUY EXPIRED
...
...
2006-12-11T23:59:59+00:00, BUY CREATE, exectype StopLimit, price 4093.42, valid: 2006-12-31, pricelimit: 4073.15
2006-12-11T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-12-22T23:59:59+00:00, BUY EXECUTED, Price: 4073.15, Cost: 4073.15, Comm 0.00

测试脚本执行

在命令行help中详细说明:

$ ./order-execution-samples.py --help
usage: order-execution-samples.py [-h] [--infile INFILE]
                                  [--csvformat {bt,visualchart,sierrachart,yahoo,yahoo_unreversed}]
                                  [--fromdate FROMDATE] [--todate TODATE]
                                  [--plot] [--plotstyle {bar,line,candle}]
                                  [--numfigs NUMFIGS] [--smaperiod SMAPERIOD]
                                  [--exectype EXECTYPE] [--valid VALID]
                                  [--perc1 PERC1] [--perc2 PERC2]
Showcase for Order Execution Types
optional arguments:
  -h, --help            show this help message and exit
  --infile INFILE, -i INFILE
                        File to be read in
  --csvformat {bt,visualchart,sierrachart,yahoo,yahoo_unreversed},
  -c {bt,visualchart,sierrachart,yahoo,yahoo_unreversed}
                        CSV Format
  --fromdate FROMDATE, -f FROMDATE
                        Starting date in YYYY-MM-DD format
  --todate TODATE, -t TODATE
                        Ending date in YYYY-MM-DD format
  --plot, -p            Plot the read data
  --plotstyle {bar,line,candle}, -ps {bar,line,candle}
                        Plot the read data
  --numfigs NUMFIGS, -n NUMFIGS
                        Plot using n figures
  --smaperiod SMAPERIOD, -s SMAPERIOD
                      Simple Moving Average Period
  --exectype EXECTYPE, -e EXECTYPE
                        Execution Type: Market (default), Close, Limit,
                        Stop, StopLimit
  --valid VALID, -v VALID
                        Validity for Limit sample: default 0 days
  --perc1 PERC1, -p1 PERC1
                        % distance from close price at order creation time for
                        the limit/trigger price in Limit/Stop orders
  --perc2 PERC2, -p2 PERC2
                        % distance from close price at order creation time for
                        the limit price in StopLimit orders

完整代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import argparse
import datetime
import os.path
import time
import sys
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind
class OrderExecutionStrategy(bt.Strategy):
    params = (
        ('smaperiod', 15),
        ('exectype', 'Market'),
        ('perc1', 3),
        ('perc2', 1),
        ('valid', 4),
    )
    def log(self, txt, dt=None):
  ''' Logging function fot this strategy'''
        dt = dt or self.data.datetime[0]
        if isinstance(dt, float):
            dt = bt.num2date(dt)
        print('%s, %s' % (dt.isoformat(), txt))
    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            self.log('ORDER ACCEPTED/SUBMITTED', dt=order.created.dt)
            self.order = order
            return
        if order.status in [order.Expired]:
            self.log('BUY EXPIRED')
        elif 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))
            else:  # Sell
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))
        # Sentinel to None: new orders allowed
        self.order = None
    def __init__(self):
        # SimpleMovingAverage on main data
        # Equivalent to -> sma = btind.SMA(self.data, period=self.p.smaperiod)
        sma = btind.SMA(period=self.p.smaperiod)
        # CrossOver (1: up, -1: down) close / sma
        self.buysell = btind.CrossOver(self.data.close, sma, plot=True)
        # Sentinel to None: new ordersa allowed
        self.order = None
    def next(self):
        if self.order:
            # An order is pending ... nothing can be done
            return
        # Check if we are in the market
        if self.position:
            # In the maerket - check if it's the time to sell
            if self.buysell < 0:
                self.log('SELL CREATE, %.2f' % self.data.close[0])
                self.sell()
        elif self.buysell > 0:
            if self.p.valid:
                valid = self.data.datetime.date(0) + \
                        datetime.timedelta(days=self.p.valid)
            else:
                valid = None
            # Not in the market and signal to buy
            if self.p.exectype == 'Market':
                self.buy(exectype=bt.Order.Market)  # default if not given
                self.log('BUY CREATE, exectype Market, price %.2f' %
                         self.data.close[0])
            elif self.p.exectype == 'Close':
                self.buy(exectype=bt.Order.Close)
                self.log('BUY CREATE, exectype Close, price %.2f' %
                         self.data.close[0])
            elif self.p.exectype == 'Limit':
                price = self.data.close * (1.0 - self.p.perc1 / 100.0)
                self.buy(exectype=bt.Order.Limit, price=price, valid=valid)
                if self.p.valid:
                    txt = 'BUY CREATE, exectype Limit, price %.2f, valid: %s'
                    self.log(txt % (price, valid.strftime('%Y-%m-%d')))
                else:
                    txt = 'BUY CREATE, exectype Limit, price %.2f'
                    self.log(txt % price)
            elif self.p.exectype == 'Stop':
                price = self.data.close * (1.0 + self.p.perc1 / 100.0)
                self.buy(exectype=bt.Order.Stop, price=price, valid=valid)
                if self.p.valid:
                    txt = 'BUY CREATE, exectype Stop, price %.2f, valid: %s'
                    self.log(txt % (price, valid.strftime('%Y-%m-%d')))
                else:
                    txt = 'BUY CREATE, exectype Stop, price %.2f'
                    self.log(txt % price)
            elif self.p.exectype == 'StopLimit':
                price = self.data.close * (1.0 + self.p.perc1 / 100.0)
                plimit = self.data.close * (1.0 + self.p.perc2 / 100.0)
                self.buy(exectype=bt.Order.StopLimit, price=price, valid=valid,
                         plimit=plimit)
                if self.p.valid:
                    txt = ('BUY CREATE, exectype StopLimit, price %.2f,'
                           ' valid: %s, pricelimit: %.2f')
                    self.log(txt % (price, valid.strftime('%Y-%m-%d'), plimit))
                else:
                    txt = ('BUY CREATE, exectype StopLimit, price %.2f,'
                           ' pricelimit: %.2f')
                    self.log(txt % (price, plimit))
def runstrat():
    args = parse_args()
    cerebro = bt.Cerebro()
    data = getdata(args)
    cerebro.adddata(data)
    cerebro.addstrategy(
        OrderExecutionStrategy,
        exectype=args.exectype,
        perc1=args.perc1,
        perc2=args.perc2,
        valid=args.valid,
        smaperiod=args.smaperiod
    )
    cerebro.run()
    if args.plot:
        cerebro.plot(numfigs=args.numfigs, style=args.plotstyle)
def getdata(args):
    dataformat = dict(
        bt=btfeeds.BacktraderCSVData,
        visualchart=btfeeds.VChartCSVData,
        sierrachart=btfeeds.SierraChartCSVData,
        yahoo=btfeeds.YahooFinanceCSVData,
        yahoo_unreversed=btfeeds.YahooFinanceCSVData
    )
    dfkwargs = dict()
    if args.csvformat == 'yahoo_unreversed':
        dfkwargs['reverse'] = True
    if args.fromdate:
        fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
        dfkwargs['fromdate'] = fromdate
    if args.todate:
        fromdate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
        dfkwargs['todate'] = todate
    dfkwargs['dataname'] = args.infile
    dfcls = dataformat[args.csvformat]
    return dfcls(**dfkwargs)
def parse_args():
    parser = argparse.ArgumentParser(
        description='Showcase for Order Execution Types')
    parser.add_argument('--infile', '-i', required=False,
                        default='../../datas/2006-day-001.txt',
                        help='File to be read in')
    parser.add_argument('--csvformat', '-c', required=False, default='bt',
                        choices=['bt', 'visualchart', 'sierrachart',
                                 'yahoo', 'yahoo_unreversed'],
                        help='CSV Format')
    parser.add_argument('--fromdate', '-f', required=False, default=None,
                        help='Starting date in YYYY-MM-DD format')
    parser.add_argument('--todate', '-t', required=False, default=None,
                        help='Ending date in YYYY-MM-DD format')
    parser.add_argument('--plot', '-p', action='store_false', required=False,
                        help='Plot the read data')
    parser.add_argument('--plotstyle', '-ps', required=False, default='bar',
                        choices=['bar', 'line', 'candle'],
                        help='Plot the read data')
    parser.add_argument('--numfigs', '-n', required=False, default=1,
                        help='Plot using n figures')
    parser.add_argument('--smaperiod', '-s', required=False, default=15,
                        help='Simple Moving Average Period')
    parser.add_argument('--exectype', '-e', required=False, default='Market',
                        help=('Execution Type: Market (default), Close, Limit,'
                              ' Stop, StopLimit'))
    parser.add_argument('--valid', '-v', required=False, default=0, type=int,
                        help='Validity for Limit sample: default 0 days')
    parser.add_argument('--perc1', '-p1', required=False, default=0.0,
                        type=float,
                        help=('%% distance from close price at order creation'
                              ' time for the limit/trigger price in Limit/Stop'
                              ' orders'))
    parser.add_argument('--perc2', '-p2', required=False, default=0.0,
                        type=float,
                        help=('%% distance from close price at order creation'
                              ' time for the limit price in StopLimit orders'))
    return parser.parse_args()
if __name__ == '__main__':
    runstrat()

目标订单

原文:www.backtrader.com/docu/order_target/order_target/

直到版本1.8.10.96,通过Strategy方法buysellbacktrader上实现了智能的投注。这一切都是关于在方程中添加一个Sizer,负责赌注的大小。

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

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

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

注意

方法的参考可以在 Strategy 中找到。总结是,这些方法使用与buysell相同的signature,只是参数size被参数target替换

在这种情况下,关键在于指定最终的target,而方法决定操作是买入还是卖出。这个逻辑也适用于 3 种方法。让我们从order_target_size开始

  • 如果target大于持仓量,则发出买入指令,差额为target - position_size示例:
  • Pos: 0, target: 7 -> 买入(size=7 - 0) -> 买入(size=7)
  • Pos: 3, target: 7 -> 买入(size=7 - 3) -> 买入(size=4)
  • Pos: -3, target: 7 -> 买入(size=7 - -3) -> 买入(size=10)
  • Pos: -3, target: -2 -> 买入(size=-2 - -3) -> 买入(size=1)
  • 如果target小于持仓量,则发出卖出指令,差额为position_size - target示例:
  • Pos: 0, target: -7 -> 卖出(size=0 - -7) -> 卖出(size=7)
  • Pos: 3, target: -7 -> 卖出(size=3 - -7) -> 卖出(size=10)
  • Pos: -3, target: -7 -> sell(size=-3 - -7) -> sell(size=4)
  • Pos: 3, target: 2 -> sell(size=3 - 2) -> sell(size=1)

使用order_target_value来设定目标值时,会考虑组合中资产的当前valueposition size,以决定最终的基础操作。推理如下:

  • 如果position size为负(空头)且target value必须大于当前值,这意味着:卖出更多

因此,逻辑如下:

  • 如果target > valuesize >=0 -> 买入
  • 如果target > valuesize < 0 -> 卖出
  • 如果target < valuesize >= 0 -> 卖出
  • 如果target < valuesize < 0 -> 买入

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

示例

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

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

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

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
...

一月目标从第一交易日的3开始增加。初始时,仓位大小从03,然后以1的增量递增。

完成一月时,最后一个order_target31,当进入二月的第一天时,报告了该仓位大小,新的目标方向要求为30,并随着仓位递减。

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.0030706.56,然后到28633.44。等等:

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

如何避免这种影响:

  • 示例在订单中使用Market类型的执行,这种效果无法避免。
  • 方法order_target_xxx允许指定执行类型价格
    一个可以指定Limit作为执行订单的方式,并让价格成为收盘价格(如果没有提供其他内容,则由该方法选择)或者甚至提供具体定价。

BackTrader 中文文档(八)(3)https://developer.aliyun.com/article/1505281

相关实践学习
Github实时数据分析与可视化
基于Github Archive公开数据集,将项目、行为等20+种事件类型数据实时采集至Hologres进行分析,并搭建可视化大屏。
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
|
3月前
|
C++ 索引
BackTrader 中文文档(七)(3)
BackTrader 中文文档(七)
27 3
|
3月前
BackTrader 中文文档(七)(2)
BackTrader 中文文档(七)
26 3
|
3月前
|
机器学习/深度学习 人工智能 测试技术
BackTrader 中文文档(十四)(3)
BackTrader 中文文档(十四)
32 0
BackTrader 中文文档(十四)(3)
|
3月前
|
算法 数据可视化 程序员
BackTrader 中文文档(十四)(1)
BackTrader 中文文档(十四)
49 0
BackTrader 中文文档(十四)(1)
|
3月前
|
存储
BackTrader 中文文档(十四)(4)
BackTrader 中文文档(十四)
27 0
BackTrader 中文文档(十四)(4)
|
3月前
|
存储
BackTrader 中文文档(八)(4)
BackTrader 中文文档(八)
19 0
|
3月前
|
存储 索引
BackTrader 中文文档(十)(4)
BackTrader 中文文档(十)
28 0
|
3月前
BackTrader 中文文档(九)(4)
BackTrader 中文文档(九)
28 0
|
3月前
BackTrader 中文文档(八)(3)
BackTrader 中文文档(八)
23 0
|
3月前
|
存储 Python
BackTrader 中文文档(八)(1)
BackTrader 中文文档(八)
31 0