BackTrader 中文文档(二十三)(3)

简介: BackTrader 中文文档(二十三)

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

代码

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
class St(bt.Strategy):
    params = (
        ('stakeperc', 10.0),
        ('opbreak', 10),
    )
    def notify_order(self, order):
        print('-- NOTIFY ORDER BEGIN')
        print(order)
        print('-- NOTIFY ORDER END')
        print('-- ORDER REMSIZE:', order.executed.remsize)
        if order.status == order.Completed:
            print('++ ORDER COMPLETED at data.len:', len(order.data))
            self.doop = -self.p.opbreak
    def __init__(self):
        pass
    def start(self):
        self.callcounter = 0
        txtfields = list()
        txtfields.append('Len')
        txtfields.append('Datetime')
        txtfields.append('Open')
        txtfields.append('High')
        txtfields.append('Low')
        txtfields.append('Close')
        txtfields.append('Volume')
        txtfields.append('OpenInterest')
        print(','.join(txtfields))
        self.doop = 0
    def next(self):
        txtfields = list()
        txtfields.append('%04d' % len(self))
        txtfields.append(self.data0.datetime.date(0).isoformat())
        txtfields.append('%.2f' % self.data0.open[0])
        txtfields.append('%.2f' % self.data0.high[0])
        txtfields.append('%.2f' % self.data0.low[0])
        txtfields.append('%.2f' % self.data0.close[0])
        txtfields.append('%.2f' % self.data0.volume[0])
        txtfields.append('%.2f' % self.data0.openinterest[0])
        print(','.join(txtfields))
        # Single order
        if self.doop == 0:
            if not self.position.size:
                stakevol = (self.data0.volume[0] * self.p.stakeperc) // 100
                print('++ STAKE VOLUME:', stakevol)
                self.buy(size=stakevol)
            else:
                self.close()
        self.doop += 1
FILLERS = {
    'FixedSize': bt.broker.filler.FixedSize,
    'FixedBarPerc': bt.broker.filler.FixedBarPerc,
    'BarPointPerc': bt.broker.filler.BarPointPerc,
}
def runstrat():
    args = parse_args()
    datakwargs = dict()
    if args.fromdate:
        fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
        datakwargs['fromdate'] = fromdate
    if args.todate:
        fromdate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
        datakwargs['todate'] = todate
    data = bt.feeds.BacktraderCSVData(dataname=args.data, **datakwargs)
    cerebro = bt.Cerebro()
    cerebro.adddata(data)
    cerebro.broker.set_cash(args.cash)
    if args.filler is not None:
        fillerkwargs = dict()
        if args.filler_args is not None:
            fillerkwargs = eval('dict(' + args.filler_args + ')')
        filler = FILLERSargs.filler
        cerebro.broker.set_filler(filler)
    cerebro.addstrategy(St, stakeperc=args.stakeperc, opbreak=args.opbreak)
    cerebro.run()
    if args.plot:
        cerebro.plot(style='bar')
def parse_args():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Volume Filling Sample')
    parser.add_argument('--data', required=False,
                        default='../../datas/2006-volume-day-001.txt',
                        help='Data to be read in')
    parser.add_argument('--cash', required=False, action='store',
                        default=500e6, type=float,
                        help=('Starting cash'))
    parser.add_argument('--filler', required=False, action='store',
                        default=None, choices=FILLERS.keys(),
                        help=('Apply a volume filler for the execution'))
    parser.add_argument('--filler-args', required=False, action='store',
                        default=None,
                        help=('kwargs for the filler with format:\n'
                              '\n'
                              'arg1=val1,arg2=val2...'))
    parser.add_argument('--stakeperc', required=False, action='store',
                        type=float, default=10.0,
                        help=('Percentage of 1st bar to use for stake'))
    parser.add_argument('--opbreak', required=False, action='store',
                        type=int, default=10,
                        help=('Bars to wait for new op after completing '
                              'another'))
    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', required=False, action='store_true',
                        help=('Plot the result'))
    return parser.parse_args()
if __name__ == '__main__':
    runstrat()

以步骤方式交易一天

原文:www.backtrader.com/blog/posts/2016-07-13-day-in-steps/day-in-steps/

看起来世界上的某个地方存在一种可以总结如下的兴趣:

  • 使用每日柱状图但使用开盘价引入订单

这来自于票证交流中的对话#105 Order execution logic with current day data#101 Dynamic stake calculation

backtrader在处理每日柱状图时尽可能保持真实,并且在使用每日柱状图时适用以下前提:

  • 当评估每日柱状图时,柱状图已经结束

这是有道理的,因为所有价格(open/high/low/close)组件都是已知的。当已知close价格时允许在open价格上采取行动似乎是不合逻辑的。

这个问题的明显解决方案是使用日内数据,在已知开盘价时进入。但是似乎日内数据并不那么普遍。

这就是在数据源中添加过滤器可以帮助的地方。一个过滤器:

  • 将每日数据转换为类似日内数据的数据

碧海蓝天!!!好奇的读者会立即指出,例如从MinutesDays上采样是合乎逻辑且有效的,但是DaysMinutes的下采样是不可能的。

而且百分百正确。下面呈现的过滤器不会尝试这样做,但是一个更加谦卑和简单的目标:

  • 将每日柱状图分解为 2 部分
  1. 一个只有开盘价而没有成交量的柱状图
  2. 一个是正常的每日柱状图的副本

这仍然可以被视为一种合乎逻辑的方法:

  • 看到开盘价时,交易员可以采取行动
  • 订单在一天的其余时间匹配(实际上可能匹配也可能不匹配,取决于执行类型和价格限制)

下面呈现了完整的代码。让我们看一个使用255每日柱状图的众所周知的数据的示例运行:

$ ./daysteps.py --data ../../datas/2006-day-001.txt

输出:

Calls,Len Strat,Len Data,Datetime,Open,High,Low,Close,Volume,OpenInterest
0001,0001,0001,2006-01-02T23:59:59,3578.73,3578.73,3578.73,3578.73,0.00,0.00
- I could issue a buy order during the Opening
0002,0001,0001,2006-01-02T23:59:59,3578.73,3605.95,3578.73,3604.33,0.00,0.00
0003,0002,0002,2006-01-03T23:59:59,3604.08,3604.08,3604.08,3604.08,0.00,0.00
- I could issue a buy order during the Opening
0004,0002,0002,2006-01-03T23:59:59,3604.08,3638.42,3601.84,3614.34,0.00,0.00
0005,0003,0003,2006-01-04T23:59:59,3615.23,3615.23,3615.23,3615.23,0.00,0.00
- I could issue a buy order during the Opening
0006,0003,0003,2006-01-04T23:59:59,3615.23,3652.46,3615.23,3652.46,0.00,0.00
...
...
0505,0253,0253,2006-12-27T23:59:59,4079.70,4079.70,4079.70,4079.70,0.00,0.00
- I could issue a buy order during the Opening
0506,0253,0253,2006-12-27T23:59:59,4079.70,4134.86,4079.70,4134.86,0.00,0.00
0507,0254,0254,2006-12-28T23:59:59,4137.44,4137.44,4137.44,4137.44,0.00,0.00
- I could issue a buy order during the Opening
0508,0254,0254,2006-12-28T23:59:59,4137.44,4142.06,4125.14,4130.66,0.00,0.00
0509,0255,0255,2006-12-29T23:59:59,4130.12,4130.12,4130.12,4130.12,0.00,0.00
- I could issue a buy order during the Opening
0510,0255,0255,2006-12-29T23:59:59,4130.12,4142.01,4119.94,4119.94,0.00,0.00

以下情况发生:

  • next被调用:510 次,即255 x 2
  • 策略数据len总共达到了255,这是预期的:数据只有这么多根柱状图
  • 每当数据len增加时,4 个价格组件具有相同的值,即open价格
    这里打印出一条备注,指示在这个开盘阶段可以采取行动,例如购买。

实际上:

  • 每日数据源正在使用每天 2 步重播,在open和其余价格组件之间提供操作选项

过滤器将在下一个版本中添加到backtrader的默认分发中。

包括过滤器的示例代码。

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import argparse
from datetime import datetime, time
import backtrader as bt
class DayStepsFilter(object):
    def __init__(self, data):
        self.pendingbar = None
    def __call__(self, data):
        # Make a copy of the new bar and remove it from stream
        newbar = [data.lines[i][0] for i in range(data.size())]
        data.backwards()  # remove the copied bar from stream
        openbar = newbar[:]  # Make an open only bar
        o = newbar[data.Open]
        for field_idx in [data.High, data.Low, data.Close]:
            openbar[field_idx] = o
        # Nullify Volume/OpenInteres at the open
        openbar[data.Volume] = 0.0
        openbar[data.OpenInterest] = 0.0
        # Overwrite the new data bar with our pending data - except start point
        if self.pendingbar is not None:
            data._updatebar(self.pendingbar)
        self.pendingbar = newbar  # update the pending bar to the new bar
        data._add2stack(openbar)  # Add the openbar to the stack for processing
        return False  # the length of the stream was not changed
    def last(self, data):
        '''Called when the data is no longer producing bars
        Can be called multiple times. It has the chance to (for example)
        produce extra bars'''
        if self.pendingbar is not None:
            data.backwards()  # remove delivered open bar
            data._add2stack(self.pendingbar)  # add remaining
            self.pendingbar = None  # No further action
            return True  # something delivered
        return False  # nothing delivered here
class St(bt.Strategy):
    params = ()
    def __init__(self):
        pass
    def start(self):
        self.callcounter = 0
        txtfields = list()
        txtfields.append('Calls')
        txtfields.append('Len Strat')
        txtfields.append('Len Data')
        txtfields.append('Datetime')
        txtfields.append('Open')
        txtfields.append('High')
        txtfields.append('Low')
        txtfields.append('Close')
        txtfields.append('Volume')
        txtfields.append('OpenInterest')
        print(','.join(txtfields))
        self.lcontrol = 0
    def next(self):
        self.callcounter += 1
        txtfields = list()
        txtfields.append('%04d' % self.callcounter)
        txtfields.append('%04d' % len(self))
        txtfields.append('%04d' % len(self.data0))
        txtfields.append(self.data.datetime.datetime(0).isoformat())
        txtfields.append('%.2f' % self.data0.open[0])
        txtfields.append('%.2f' % self.data0.high[0])
        txtfields.append('%.2f' % self.data0.low[0])
        txtfields.append('%.2f' % self.data0.close[0])
        txtfields.append('%.2f' % self.data0.volume[0])
        txtfields.append('%.2f' % self.data0.openinterest[0])
        print(','.join(txtfields))
        if len(self.data) > self.lcontrol:
            print('- I could issue a buy order during the Opening')
        self.lcontrol = len(self.data)
def runstrat():
    args = parse_args()
    cerebro = bt.Cerebro()
    data = bt.feeds.BacktraderCSVData(dataname=args.data)
    data.addfilter(DayStepsFilter)
    cerebro.adddata(data)
    cerebro.addstrategy(St)
    cerebro.run(stdstats=False, runonce=False, preload=False)
    if args.plot:
        cerebro.plot(style='bar')
def parse_args():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Sample for pivot point and cross plotting')
    parser.add_argument('--data', required=False,
                        default='../../datas/2005-2006-day-001.txt',
                        help='Data to be read in')
    parser.add_argument('--plot', required=False, action='store_true',
                        help=('Plot the result'))
    return parser.parse_args()
if __name__ == '__main__':
    runstrat()

Visual Chart 实时数据/交易

原文:www.backtrader.com/blog/posts/2016-07-12-visualchart-feed/visualchart-feed/

从版本 1.5.1.93 开始,backtrader 支持 Visual Chart 实时数据和实时交易。

需要的东西:

  • Visual Chart 6(这个版本运行在 Windows 上)
  • comtypes,具体来说是这个分支:github.com/mementum/comtypes
    使用以下命令安装:pip install https://github.com/mementum/comtypes/archive/master.zip
    Visual Chart 的 API 基于 COM,当前的 comtypes 主分支不支持对 VT_RECORDVT_ARRAYS 进行解包。而 Visual Chart 正是使用了这个。
    Pull Request #104 已经提交但尚未集成。一旦集成,就可以使用主分支了。
  • pytz(可选但强烈建议)
    在许多情况下,数据提供的内部 SymbolInfo.TimeOffset 就足以返回市场时间的数据流(即使默认配置是 Visual Chart 中的 LocalTime

如果您不知道什么是 Visual Chart 和/或其当前关联的经纪商 Esfera Capital,请访问以下网站:

初始声明:

  • 如往常一样,在冒险之前测试,测试测试再次测试 一千次。
    从这个软件中的 bugs,到您自己的软件中的 bug 以及处理意外情况的管理:任何事情都可能出错

关于此的一些说明:

  • 数据提供非常好,并支持内置重采样。好处在于无需进行重采样。
  • 数据流不支持 Seconds 分辨率。这不太好,但可以通过 backtrader 的内置重采样解决。
  • 内置了回填功能
  • 一些国际指数市场(在交易所 096)具有奇怪的时区和市场偏移。
    对此进行了一些工作,例如以预期的 US/Eastern 时区提供 096.DJI
  • 数据提供了 continuous futures,非常方便拥有大量历史数据。
    因此,可以向数据传递第二个参数,指示实际的交易资产。
  • Good Til Date 订单的日期时间只能指定为 日期时间 部分会被忽略。
  • 没有直接的方法可以找到本地设备到数据服务器的偏移量,需要通过会话开始时的 实时数据点 进行启发式分析来找出这一点。
  • 传递带有 时间 组件的 datetime(而不是默认的 00:00:00)似乎会在 COM API 中创建一个 时间过滤器。例如,如果您想要 Minute 数据,从 3 天前开始到 14:30,您可以这样做:
dt = datetime.now() - timedelta(days=3)
dt.replace(hour=14, minute=30)
vcstore.getdata(dataname='001ES', fromdate=dt)` 
  • 数据会一直跳过直到 14:30 不仅是 3 天前,而是以后每一天
    因此,请只传递完整日期,即默认的时间部分不受影响。
  • 经纪人 支持 Positions 的概念,但仅在它们是 open 时。 关于 Position 的最后事件(其 size 为 0)不会发送。
    因此,Position 记账完全由 backtrader 完成。
  • 经纪人 不报告佣金。
    解决方法是在实例化经纪人时提供自己的 CommissionInfo 派生类。 请参阅 backtader 文档以创建自己的类。 这相当容易。
  • CancelledExpired 订单。 此区别不存在,需要启发式方法来尝试清除这种区别。
    因此,只有Cancelled将被报告。

一些额外的说明:

  • 实时ticks 大部分情况下不被使用。 它们为backtrader目的产生大量不需要的信息。 在被backtrader完全断开连接之前,它们有两个主要目的。
  1. 查找符号是否存在。
  2. 计算到数据服务器的偏移量。
  • 当然,价格信息是实时收集的,但是来自 DataSource 对象,它们同时提供历史数据。

尽可能多地记录并在通常的文档链接处提供。

从样本 vctest.pyeVisual ChartDemo Broker 进行了一些运行。

首先:015ESEuroStoxx50 连续)重新采样为 1 分钟,并具有断开连接和重新连接:

$ ./vctest.py --data0 015ES --timeframe Minutes --compression 1 --fromdate 2016-07-12

输出:

--------------------------------------------------
Strategy Created
--------------------------------------------------
Datetime, Open, High, Low, Close, Volume, OpenInterest, SMA
***** DATA NOTIF: CONNECTED
***** DATA NOTIF: DELAYED
0001, 2016-07-12T08:01:00.000000, 2871.0, 2872.0, 2869.0, 2872.0, 1915.0, 0.0, nan
0002, 2016-07-12T08:02:00.000000, 2872.0, 2872.0, 2870.0, 2871.0, 479.0, 0.0, nan
0003, 2016-07-12T08:03:00.000000, 2871.0, 2871.0, 2869.0, 2870.0, 518.0, 0.0, nan
0004, 2016-07-12T08:04:00.000000, 2870.0, 2871.0, 2870.0, 2871.0, 248.0, 0.0, nan
0005, 2016-07-12T08:05:00.000000, 2870.0, 2871.0, 2870.0, 2871.0, 234.0, 0.0, 2871.0
...
...
0639, 2016-07-12T18:39:00.000000, 2932.0, 2933.0, 2932.0, 2932.0, 1108.0, 0.0, 2932.8
0640, 2016-07-12T18:40:00.000000, 2931.0, 2932.0, 2931.0, 2931.0, 65.0, 0.0, 2932.6
***** DATA NOTIF: LIVE
0641, 2016-07-12T18:41:00.000000, 2932.0, 2932.0, 2930.0, 2930.0, 2093.0, 0.0, 2931.8
***** STORE NOTIF: (u'VisualChart is Disconnected', -65520)
***** DATA NOTIF: CONNBROKEN
***** STORE NOTIF: (u'VisualChart is Connected', -65521)
***** DATA NOTIF: CONNECTED
***** DATA NOTIF: DELAYED
0642, 2016-07-12T18:42:00.000000, 2931.0, 2931.0, 2931.0, 2931.0, 137.0, 0.0, 2931.2
0643, 2016-07-12T18:43:00.000000, 2931.0, 2931.0, 2931.0, 2931.0, 432.0, 0.0, 2931.0
...
0658, 2016-07-12T18:58:00.000000, 2929.0, 2929.0, 2929.0, 2929.0, 4.0, 0.0, 2930.0
0659, 2016-07-12T18:59:00.000000, 2929.0, 2930.0, 2929.0, 2930.0, 353.0, 0.0, 2930.0
***** DATA NOTIF: LIVE
0660, 2016-07-12T19:00:00.000000, 2930.0, 2930.0, 2930.0, 2930.0, 376.0, 0.0, 2930.0
0661, 2016-07-12T19:01:00.000000, 2929.0, 2930.0, 2929.0, 2930.0, 35.0, 0.0, 2929.8

注意

执行环境安装了 pytz

注意

注意没有 --resample:对于 Minutes,重新采样是内置于 Visual Chart 中的。

最后一些交易,购买 015ES2 个合约,单个 Market 订单,并将它们卖出为 1 个合约的 2 个订单。

执行:

$ ./vctest.py --data0 015ES --timeframe Minutes --compression 1 --fromdate 2016-07-12 2>&1 --broker --account accname --trade --stake 2

输出相当冗长,显示了订单执行的所有部分。 简要总结一下:

--------------------------------------------------
Strategy Created
--------------------------------------------------
Datetime, Open, High, Low, Close, Volume, OpenInterest, SMA
***** DATA NOTIF: CONNECTED
***** DATA NOTIF: DELAYED
0001, 2016-07-12T08:01:00.000000, 2871.0, 2872.0, 2869.0, 2872.0, 1915.0, 0.0, nan
...
0709, 2016-07-12T19:50:00.000000, 2929.0, 2930.0, 2929.0, 2930.0, 11.0, 0.0, 2930.4
***** DATA NOTIF: LIVE
0710, 2016-07-12T19:51:00.000000, 2930.0, 2930.0, 2929.0, 2929.0, 134.0, 0.0, 2930.0
-------------------------------------------------- ORDER BEGIN 2016-07-12 19:52:01.629000
Ref: 1
OrdType: 0
OrdType: Buy
Status: 1
Status: Submitted
Size: 2
Price: None
Price Limit: None
ExecType: 0
ExecType: Market
CommInfo: <backtrader.brokers.vcbroker.VCCommInfo object at 0x000000001100CE10>
End of Session: 736157.916655
Info: AutoOrderedDict()
Broker: <backtrader.brokers.vcbroker.VCBroker object at 0x000000000475D400>
Alive: True
-------------------------------------------------- ORDER END
-------------------------------------------------- ORDER BEGIN 2016-07-12 19:52:01.629000
Ref: 1
OrdType: 0
OrdType: Buy
Status: 2
Status: Accepted
Size: 2
Price: None
Price Limit: None
ExecType: 0
ExecType: Market
CommInfo: <backtrader.brokers.vcbroker.VCCommInfo object at 0x000000001100CE10>
End of Session: 736157.916655
Info: AutoOrderedDict()
Broker: None
Alive: True
-------------------------------------------------- ORDER END
-------------------------------------------------- ORDER BEGIN 2016-07-12 19:52:01.629000
Ref: 1
OrdType: 0
OrdType: Buy
Status: 4
Status: Completed
Size: 2
Price: None
Price Limit: None
ExecType: 0
ExecType: Market
CommInfo: <backtrader.brokers.vcbroker.VCCommInfo object at 0x000000001100CE10>
End of Session: 736157.916655
Info: AutoOrderedDict()
Broker: None
Alive: False
-------------------------------------------------- ORDER END
-------------------------------------------------- TRADE BEGIN 2016-07-12 19:52:01.629000
ref:1
data:<backtrader.feeds.vcdata.VCData object at 0x000000000475D9E8>
tradeid:0
size:2.0
price:2930.0
value:5860.0
commission:0.0
pnl:0.0
pnlcomm:0.0
justopened:True
isopen:True
isclosed:0
baropen:710
dtopen:736157.74375
barclose:0
dtclose:0.0
barlen:0
historyon:False
history:[]
status:1
-------------------------------------------------- TRADE END
...

以下发生了:

  • 数据正常接收。
  • 发出了一个执行类型为MarketBUY,数量为2
  • 收到 SubmittedAccepted 通知(仅显示了 Submitted)。
  • 一连串的 Partial 执行(仅显示了 1 个)直到收到 Completed
  • 实际执行没有显示,但在 order.executed 下收到的 order 实例中可用。
  • 虽然没有显示,但发出了 2 x Market SELL 订单来撤销操作。
    屏幕截图显示了在一个晚上使用 015ESEuroStoxx 50)和 034EURUSEUR.USD 外汇对)进行两次不同运行后,在 Visual Chart 中的日志。

示例可以做得更多,旨在彻底测试设施,如果可能的话,揭示任何粗糙的边缘。


BackTrader 中文文档(二十三)(4)https://developer.aliyun.com/article/1505438

相关文章
|
5天前
|
编解码 索引
BackTrader 中文文档(二十三)(2)
BackTrader 中文文档(二十三)
7 0
|
5天前
BackTrader 中文文档(二十三)(4)
BackTrader 中文文档(二十三)
11 0
|
5天前
|
Oracle 测试技术 编译器
BackTrader 中文文档(二十三)(1)
BackTrader 中文文档(二十三)
10 0
|
5天前
BackTrader 中文文档(二十二)(1)
BackTrader 中文文档(二十二)
11 0
|
5天前
|
存储 Python
BackTrader 中文文档(二十二)(3)
BackTrader 中文文档(二十二)
18 0
|
5天前
|
存储 测试技术 Python
BackTrader 中文文档(二十二)(2)
BackTrader 中文文档(二十二)
14 0
|
5天前
|
测试技术
BackTrader 中文文档(二十二)(4)
BackTrader 中文文档(二十二)
14 0
|
5天前
|
数据可视化 API Python
BackTrader 中文文档(二十五)(1)
BackTrader 中文文档(二十五)
9 0
|
5天前
BackTrader 中文文档(二十五)(2)
BackTrader 中文文档(二十五)
9 0
|
5天前
|
数据可视化
BackTrader 中文文档(二十五)(4)
BackTrader 中文文档(二十五)
12 0