首页 > 其他分享 >BackTrader 中文文档(二十八)

BackTrader 中文文档(二十八)

时间:2024-04-15 11:37:44浏览次数:27  
标签:00 59 二十八 self BackTrader order 文档 2006 price

原文:www.backtrader.com/

订单管理和执行

原文:www.backtrader.com/blog/posts/2015-08-08-order-creation-execution/order-creation-execution/

回测,因此 backtrader,如果不能模拟订单,将不完整。为此,平台提供了以下功能。

对于订单管理有 3 个原语:

  • 购买

  • 卖出

  • 取消

注意

update原语显然是一种逻辑,但常识告诉我们,这种方法主要由使用判断性交易方法的手动操作员使用。

对于订单执行逻辑,以下执行类型:

  • 市价

  • 关闭

  • 限价

  • 停止

  • StopLimit

订单管理

主要目标是易于使用,因此进行订单管理的最直接(和简单)的方法是从策略本身开始。

buyself原语具有以下签名作为Strategy方法:

  • def buy(self, data=None, size=None, price=None, plimit=None, exectype=None, valid=None):

  • def buy(self, data=None, size=None, price=None, exectype=None, valid=None)

    • data -> 数据源引用,用于购买的资产

    如果传递None,则策略的主要数据将用作目标

    • size -> 确定要应用的股份的 int/long

    如果传递None,则策略中可用的Sizer将被用于自动确定股份。默认的Sizer使用固定状态1

    • price -> 对于Market将被忽略并且可以留空,但对于其他订单类型必须是浮点数。如果留空,则将使用当前收盘价

    • plimit -> 在StopLimit订单中的限价,其中price将被用作触发价格

    如果留空,则price将被用作限价(触发和限价相同)

    • exectype -> 订单执行类型之一。如果传递None,则将假定为Market

    执行类型在Order中被枚举。例如:Order.Limit

    • valid -> 从 date2num(或数据源)获取的浮点值或 datetime.datetime Python 对象

    注意Market订单将在不考虑valid参数的情况下执行

    返回值:一个Order实例

  • def sell(self, data=None, size=None, price=None, exectype=None, valid=None)

因为取消订单只需要buyself返回的order引用,所以经纪人的原语可以使用(见下文)

一些例子:

# buy the main date, with sizer default stake, Market order
order = self.buy()

# Market order - valid will be "IGNORED"
order = self.buy(valid=datetime.datetime.now() + datetime.timedelta(days=3))

# Market order - price will be IGNORED
order = self.buy(price=self.data.close[0] * 1.02)

# Market order - manual stake
order = self.buy(size=25)

# Limit order - want to set the price and can set a validity
order = self.buy(exectype=Order.Limit,
                 price=self.data.close[0] * 1.02,
                 valid=datetime.datetime.now() + datetime.timedelta(days=3)))

# StopLimit order - want to set the price, price limit
order = self.buy(exectype=Order.StopLimit,
                 price=self.data.close[0] * 1.02,
                 plimit=self.data.close[0] * 1.07)

# Canceling an existing order
self.broker.cancel(order)

注意

所有订单类型都可以通过创建Order实例(或其子类之一),然后通过以下方式传递给经纪人:

order = self.broker.submit(order)

注意

broker本身有buysell原语,但对于默认参数要求更严格。

订单执行逻辑

broker使用 2 个主要准则(假设?)进行订单执行。

  • 当前数据已经发生,不能用于执行订单。

    如果策略中的逻辑如下:

     if self.data.close > self.sma:  # where sma is a Simple Moving Average
         self.buy()
    
    The expectation CANNOT be that the order will be executed with the
    ``close`` price which is being examined in the logic BECAUSE it has already
    happened.
    
    The order CAN BE 1st EXECUTED withing the bounds of the next set of
    Open/High/Low/Close price points (and the conditions set forth herein by
    the order)` 
    
  • 交易量不起作用

    实际上,在真实交易中,如果交易员选择非流动性资产,或者精确地击中了价格条的极值(高/低),订单将被取消。

    但是击中高/低点是个罕见的事件(如果你这么做...你就不需要backtrader了),并且所选择的资产将有足够的流动性来吸收任何常规交易的订单

市场

执行:

下一个一组 Open/High/Low/Close 价格(通常称为bar)的开盘价

理由:

如果逻辑在时间点 X 执行并发出了Market订单,则接下来会发生的价格点是即将到来的open价格

注意

这个订单总是执行,忽略任何用于创建它的pricevalid参数

关闭

执行:

当下一个价格条实际上关闭时,使用下一个价格条的close价格

理由:

大多数回测数据源已经包含了已关闭的价格条,订单将立即执行,使用下一个价格条的close价格。每日数据源是最常见的例子。

但是系统可以被喂入“tick”价格,实际的条(按时间/日期)正在不断地随着新的 ticks 更新,而实际上并没有移动到下一个条(因为时间和/或日期没有改变)

只有当时间或日期改变时,条才会实际上被关闭,订单才会执行

限价

执行:

订单创建时设置的price,如果data触及它,则从下一个价格条开始。

如果设置了valid并且到达了时间点,订单将被取消

价格匹配:

backtrader试图为Limit订单提供最真实的执行价格

使用 4 个价格点(Open/High/Low/Close),可以部分推断请求的price是否可以改善。

对于Buy订单

- Case 1:

  If the `open` price of the bar is below the limit price the order
  executes immediately with the `open` price. The order has been swept
  during the opening phase of the session

- Case 2:

  If the `open` price has not penetrated below the limit price but the
  `low` price is below the limit price, then the limit price has been
  seen during the session and the order can be executed

对于Sell订单,逻辑显然是相反的。

止损

执行:

订单创建时设置的触发器price,如果data触及它,则从下一个价格条开始。

如果设置了valid并且到达了时间点,订单将被取消

价格匹配:

backtrader试图为Stop订单提供最真实的触发价格

使用 4 个价格点(Open/High/Low/Close),可以部分推断请求的price是否可以改善。

对于\Stoporders whichBuy`

- Case 1:

  If the `open` price of the bar is above the stop price the order is
  executed immediately with the `open` price.

  Intended to stop a loss if the price is moving upwards against an
  existing short position

- Case 2:

  If the `open` price has not penetrated above the stop price but the
  `high` price is above the stop price, then the stop price has been
  seen during the session and the order can be executed

对于Sell订单,逻辑显然是相反的。

止损限价

执行:

触发器price设置了订单的启动,从下一个价格条开始。

价格匹配:

  • 触发器:使用Stop匹配逻辑(但仅触发并将订单转换为Limit订单)

  • 限制:使用Limit价格匹配逻辑

一些样本

像往常一样,图片(带代码)价值数百万长的解释。请注意,代码片段集中在订单创建部分。完整代码在底部。

一个价格高于/低于简单移动平均线策略将用于生成买入/卖出信号

信号在图表底部可见:使用交叉指标的CrossOver

仅保留对生成的“买入”订单的引用,以允许系统中最多只有一个同时订单。

执行类型:市价

请在图表中查看订单如何在生成信号后一根棒棒后以开盘价执行。

 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])

输出图表。

图像

命令行和输出:

$ ./order-execution-samples.py --exectype Market
2006-01-26T23:59:59+00:00, BUY CREATE, exectype Market, price 3641.42
2006-01-26T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-01-27T23:59:59+00:00, BUY EXECUTED, Price: 3643.35, Cost: 3643.35, 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-12-11T23:59:59+00:00, BUY CREATE, exectype Market, price 4052.89
2006-12-11T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-12-12T23:59:59+00:00, BUY EXECUTED, Price: 4052.55, Cost: 4052.55, Comm 0.00

执行类型:收盘

注意

Issue#11之后,创建了一个开发分支,更新了图表和输出。错误的收盘价格被使用。

现在订单也是在信号后一根棒棒后执行,但是使用的是收盘价。

 elif self.p.exectype == 'Close':
                self.buy(exectype=bt.Order.Close)

                self.log('BUY CREATE, exectype Close, price %.2f' %
                         self.data.close[0])

输出图表。

图像

命令行和输出:

$ ./order-execution-samples.py --exectype Close
2006-01-26T23:59:59+00:00, BUY CREATE, exectype Close, price 3641.42
2006-01-26T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-01-27T23:59:59+00:00, BUY EXECUTED, Price: 3685.48, Cost: 3685.48, 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-11-06T23:59:59+00:00, BUY CREATE, exectype Close, price 4045.22
2006-11-06T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-11-07T23:59:59+00:00, BUY EXECUTED, Price: 4072.86, Cost: 4072.86, Comm 0.00
2006-11-24T23:59:59+00:00, SELL CREATE, 4048.16
2006-11-24T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-11-27T23:59:59+00:00, SELL EXECUTED, Price: 4045.05, Cost: 4045.05, Comm 0.00
2006-12-11T23:59:59+00:00, BUY CREATE, exectype Close, price 4052.89
2006-12-11T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-12-12T23:59:59+00:00, BUY EXECUTED, Price: 4059.74, Cost: 4059.74, Comm 0.00

执行类型:限价

在几行之前计算有效性以防已作为参数传递。

 if self.p.valid:
                valid = self.data.datetime.date(0) + \
                        datetime.timedelta(days=self.p.valid)
            else:
                valid = None

设置了信号生成价格(信号条的收盘价)下跌 1%的限价。请注意,这阻止了上面许多订单的执行。

 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)

输出图表。

图像

仅发布了 4 个订单。尝试捕捉小幅度下跌的价格完全改变了输出。

命令行和输出:

$ ./order-execution-samples.py --exectype Limit --perc1 1
2006-01-26T23:59:59+00:00, BUY CREATE, exectype Limit, price 3605.01
2006-01-26T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-05-18T23:59:59+00:00, BUY EXECUTED, Price: 3605.01, Cost: 3605.01, Comm 0.00
2006-06-05T23:59:59+00:00, SELL CREATE, 3604.33
2006-06-05T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-06-06T23:59:59+00:00, SELL EXECUTED, Price: 3598.58, Cost: 3598.58, Comm 0.00
2006-06-21T23:59:59+00:00, BUY CREATE, exectype Limit, price 3491.57
2006-06-21T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-06-28T23:59:59+00:00, BUY EXECUTED, Price: 3491.57, Cost: 3491.57, Comm 0.00
2006-07-13T23:59:59+00:00, SELL CREATE, 3562.56
2006-07-13T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-07-14T23:59:59+00:00, SELL EXECUTED, Price: 3545.92, Cost: 3545.92, Comm 0.00
2006-07-24T23:59:59+00:00, BUY CREATE, exectype Limit, price 3596.60
2006-07-24T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED

执行类型:限价与有效期

为了不会永远等待一个限价订单,该订单仅在 4(日历)天内有效,该订单可能只有在价格对“买入”订单不利时才执行。

输出图表。

图像

更多订单已生成,但除了一个“买入”订单过期外,其他所有订单都过期了,进一步限制了操作数量。

命令行和输出:

$ ./order-execution-samples.py --exectype Limit --perc1 1 --valid 4
2006-01-26T23:59:59+00:00, BUY CREATE, exectype Limit, price 3605.01, valid: 2006-01-30
2006-01-26T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-01-30T23:59:59+00:00, BUY EXPIRED
2006-03-10T23:59:59+00:00, BUY CREATE, exectype Limit, price 3760.48, valid: 2006-03-14
2006-03-10T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-03-14T23:59:59+00:00, BUY EXPIRED
2006-03-30T23:59:59+00:00, BUY CREATE, exectype Limit, price 3835.86, valid: 2006-04-03
2006-03-30T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-04-03T23:59:59+00:00, BUY EXPIRED
2006-04-20T23:59:59+00:00, BUY CREATE, exectype Limit, price 3821.40, valid: 2006-04-24
2006-04-20T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-04-24T23:59:59+00:00, BUY EXPIRED
2006-05-04T23:59:59+00:00, BUY CREATE, exectype Limit, price 3804.65, valid: 2006-05-08
2006-05-04T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-05-08T23:59:59+00:00, BUY EXPIRED
2006-06-01T23:59:59+00:00, BUY CREATE, exectype Limit, price 3611.85, valid: 2006-06-05
2006-06-01T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-06-05T23:59:59+00:00, BUY EXPIRED
2006-06-21T23:59:59+00:00, BUY CREATE, exectype Limit, price 3491.57, valid: 2006-06-25
2006-06-21T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-06-26T23:59:59+00:00, BUY EXPIRED
2006-07-24T23:59:59+00:00, BUY CREATE, exectype Limit, price 3596.60, valid: 2006-07-28
2006-07-24T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-07-28T23:59:59+00:00, BUY EXPIRED
2006-09-12T23:59:59+00:00, BUY CREATE, exectype Limit, price 3751.07, valid: 2006-09-16
2006-09-12T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-09-18T23:59:59+00:00, BUY EXPIRED
2006-09-20T23:59:59+00:00, BUY CREATE, exectype Limit, price 3802.90, valid: 2006-09-24
2006-09-20T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-09-22T23:59:59+00:00, BUY EXECUTED, Price: 3802.90, Cost: 3802.90, Comm 0.00
2006-11-02T23:59:59+00:00, SELL CREATE, 3974.62
2006-11-02T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-11-03T23:59:59+00:00, SELL EXECUTED, Price: 3979.73, Cost: 3979.73, Comm 0.00
2006-11-06T23:59:59+00:00, BUY CREATE, exectype Limit, price 4004.77, valid: 2006-11-10
2006-11-06T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-11-10T23:59:59+00:00, BUY EXPIRED
2006-12-11T23:59:59+00:00, BUY CREATE, exectype Limit, price 4012.36, valid: 2006-12-15
2006-12-11T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-12-15T23:59:59+00:00, BUY EXPIRED

执行类型:止损

设置了信号价格上涨 1%的止损价。这意味着策略仅在生成信号并价格继续上涨时购买,这可以被解释为强势信号。

这完全改变了执行情况。

 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)

输出图表。

图像

命令行和输出:

$ ./order-execution-samples.py --exectype Stop --perc1 1
2006-01-26T23:59:59+00:00, BUY CREATE, exectype Stop, price 3677.83
2006-01-26T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-01-27T23:59:59+00:00, BUY EXECUTED, Price: 3677.83, Cost: 3677.83, 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 Stop, price 3836.44
2006-03-10T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-03-15T23:59:59+00:00, BUY EXECUTED, Price: 3836.44, Cost: 3836.44, 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 Stop, price 3913.36
2006-03-30T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-09-29T23:59:59+00:00, BUY EXECUTED, Price: 3913.36, Cost: 3913.36, Comm 0.00
2006-11-02T23:59:59+00:00, SELL CREATE, 3974.62
2006-11-02T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-11-03T23:59:59+00:00, SELL EXECUTED, Price: 3979.73, Cost: 3979.73, Comm 0.00
2006-11-06T23:59:59+00:00, BUY CREATE, exectype Stop, price 4085.67
2006-11-06T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-11-13T23:59:59+00:00, BUY EXECUTED, Price: 4085.67, Cost: 4085.67, Comm 0.00
2006-11-24T23:59:59+00:00, SELL CREATE, 4048.16
2006-11-24T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-11-27T23:59:59+00:00, SELL EXECUTED, Price: 4045.05, Cost: 4045.05, Comm 0.00
2006-12-11T23:59:59+00:00, BUY CREATE, exectype Stop, price 4093.42
2006-12-11T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED
2006-12-13T23:59:59+00:00, BUY EXECUTED, Price: 4093.42, Cost: 4093.42, Comm 0.00

执行类型:止损限价

设置了信号价格上涨 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/blog/posts/2015-08-07-extending-a-datafeed/extending-a-datafeed/

GitHub 上的问题实际上正在推动完成文档部分或帮助我了解 backtrader 是否具有我最初设想的易用性和灵活性,并沿途做出的决定。

在这种情况下是问题#9

最终问题似乎归结为:

  • 最终用户是否可以轻松扩展现有机制,以添加额外信息,以行的形式传递到其他现有价格信息点,如openhigh等?

就我理解的问题,答案是:是的

发帖人似乎有这些要求(来自问题#6):

  • 一个被解析为 CSV 格式的数据源

  • 使用GenericCSVData来加载信息

    这种通用的 CSV 支持是为了响应这个问题#6而开发的

  • 一个额外字段,显然包含需要传递到解析的 CSV 数据中的 P/E 信息

让我们在 CSV 数据源开发和通用 CSV 数据源示例帖子的基础上构建。

步骤:

  • 假设 P/E 信息被设置在被解析的 CSV 数据中

  • 使用GenericCSVData作为基类

  • 将现有线(open/high/low/close/volumen/openinterest)扩展为pe

  • 添加一个参数,让调用者确定 P/E 信息的列位置

结果:

from backtrader.feeds import GenericCSVData

class GenericCSV_PE(GenericCSVData):

    # Add a 'pe' line to the inherited ones from the base class
    lines = ('pe',)

    # openinterest in GenericCSVData has index 7 ... add 1
    # add the parameter to the parameters inherited from the base class
    params = (('pe', 8),)

工作完成了...

稍后,在策略中使用这个数据源时:

import backtrader as bt

....

class MyStrategy(bt.Strategy):

    ...

    def next(self):

        if self.data.close > 2000 and self.data.pe < 12:
            # TORA TORA TORA --- Get off this market
            self.sell(stake=1000000, price=0.01, exectype=Order.Limit)
    ...

绘制额外的 P/E 线

显然,在数据源中没有对该额外线的自动绘图支持。

最好的选择是对该线进行简单移动平均,并在单独的轴上绘制它:

import backtrader as bt
import backtrader.indicators as btind

....

class MyStrategy(bt.Strategy):

    def __init__(self):

        # The indicator autoregisters and will plot even if no obvious
        # reference is kept to it in the class
        btind.SMA(self.data.pe, period=1, subplot=False)

    ...

    def next(self):

        if self.data.close > 2000 and self.data.pe < 12:
            # TORA TORA TORA --- Get off this market
            self.sell(stake=1000000, price=0.01, exectype=Order.Limit)
    ...

CSV 数据源开发

原文:www.backtrader.com/blog/posts/2015-08-06-csv-data-feed-development/csv-data-feed-development/

backtrader 已经提供了一个通用的 CSV 数据源和一些特定的 CSV 数据源。总结如下:

  • GenericCSVData

  • VisualChartCSVData

  • YahooFinanceData(用于在线下载)

  • YahooFinanceCSVData(用于已下载的数据)

  • BacktraderCSVData(内部…用于测试目的,但可以使用)

但即使如此,最终用户可能希望为特定的 CSV 数据源开发支持。

通常的格言是:“说起来容易做起来难”。实际上,结构设计得很简单。

步骤:

  • 继承自backtrader.CSVDataBase

  • 如果需要,定义任何params

  • start方法中进行任何初始化

  • stop方法中进行任何清理

  • 定义一个_loadline方法,其中实际工作发生

    此方法接收一个参数:linetokens。

    正如名称所示,这包含了在根据separator参数(从基类继承)分割当前行后的令牌。

    如果在完成其工作后有新数据... 填充相应的行并返回True

    如果没有可用的数据,因此解析已经结束:返回False

    如果在幕后读取文件行的代码发现没有更多可解析的行,则可能甚至不需要返回False

已经考虑到的事项:

  • 打开文件(或接收类似文件的对象)

  • 如果存在,则跳过标题行

  • 读取行

  • 对行进行标记

  • 预加载支持(一次性将整个数据源加载到内存中)

通常,一个例子胜过千言万语的要求描述。让我们使用从BacktraderCSVData定义的简化版本的内部定义的 CSV 解析代码。这个不需要初始化或清理(例如,可以稍后打开套接字并关闭)。

注意

backtrader数据源包含常见的行业标准数据源,这些是需要填充的。即:

  • 日期时间

  • 打开

  • 关闭

  • 成交量

  • 持仓量

如果您的策略/算法或简单的数据查阅只需要,例如收盘价,您可以将其他内容保持不变(每次迭代都会在最终用户代码有机会执行任何操作之前自动使用 float('NaN')值填充它们。

在本例中仅支持每日格式:

import itertools
...
import backtrader import bt

class MyCSVData(bt.CSVDataBase):

    def start(self):
        # Nothing to do for this data feed type
        pass

    def stop(self):
        # Nothing to do for this data feed type
        pass

    def _loadline(self, linetokens):
        i = itertools.count(0)

        dttxt = linetokens[next(i)]
        # Format is YYYY-MM-DD
        y = int(dttxt[0:4])
        m = int(dttxt[5:7])
        d = int(dttxt[8:10])

        dt = datetime.datetime(y, m, d)
        dtnum = date2num(dt)

        self.lines.datetime[0] = dtnum
        self.lines.open[0] = float(linetokens[next(i)])
        self.lines.high[0] = float(linetokens[next(i)])
        self.lines.low[0] = float(linetokens[next(i)])
        self.lines.close[0] = float(linetokens[next(i)])
        self.lines.volume[0] = float(linetokens[next(i)])
        self.lines.openinterest[0] = float(linetokens[next(i)])

        return True

代码期望所有字段都就位,并且可转换为浮点数,除了日期时间之外,它具有固定的 YYYY-MM-DD 格式,并且可以在不使用datetime.datetime.strptime的情况下解析。

只需添加几行代码即可满足更复杂的需求,以处理空值,日期格式解析。GenericCSVData就是这样做的。

买方注意事项

使用GenericCSVData现有的数据源和继承可以实现很多支持格式的功能。

让我们为Sierra Chart的每日格式添加支持(该格式始终以 CSV 格式存储)。

定义(通过查看一个‘.dly’数据文件):

  • 字段:日期、开盘价、最高价、最低价、收盘价、成交量、持仓量

    行业标准和已由 GenericCSVData 支持的那些文件,按相同顺序(这也是行业标准)

  • 分隔符:,

  • 日期格式:YYYY/MM/DD

针对这些文件的解析器:

class SierraChartCSVData(backtrader.feeds.GenericCSVData):

    params = (('dtformat', '%Y/%m/%d'),)

params 的定义只是重新定义基类中的一个现有参数。在这种情况下,只需更改日期的格式化字符串。

哎呀……Sierra Chart 的解析器完成了。

这里是 GenericCSVData 的参数定义作为提醒:

class GenericCSVData(feed.CSVDataBase):
    params = (
        ('nullvalue', float('NaN')),
        ('dtformat', '%Y-%m-%d %H:%M:%S'),
        ('tmformat', '%H:%M:%S'),

        ('datetime', 0),
        ('time', -1),
        ('open', 1),
        ('high', 2),
        ('low', 3),
        ('close', 4),
        ('volume', 5),
        ('openinterest', 6),
    )

通用 CSV 数据源

原文:www.backtrader.com/blog/posts/2015-08-04-generic-csv-datafeed/generic-csv-datafeed/

一个问题导致实现了GenericCSVData,可用于解析不同的 CSV 格式。

GitHub 上的问题,Issue #6清楚地显示需要有能够处理任何传入 CSV 数据源的东西。

参数声明中包含关键信息:

class GenericCSVData(feed.CSVDataBase):
    params = (
        ('nullvalue', float('NaN')),
        ('dtformat', '%Y-%m-%d %H:%M:%S'),
        ('tmformat', '%H:%M:%S'),

        ('datetime', 0),
        ('time', -1),
        ('open', 1),
        ('high', 2),
        ('low', 3),
        ('close', 4),
        ('volume', 5),
        ('openinterest', 6),
    )

因为该类继承自 CSVDataBase,一些标准参数可用:

  • fromdate(接受日期时间对象以限制起始日期)

  • todate(接受日期时间对象)以限制结束日期)

  • headers(默认值:True,指示 CSV 数据是否有标题行)

  • separator(默认值:“,”,分隔字段的字符)

  • dataname(包含 CSV 数据的文件名或类似文件的对象)

其他一些参数如namecompressiontimeframe仅供参考,除非您计划执行重新采样。

当然更重要的是,新定义参数的含义:

  • datetime(默认值:0)列包含日期(或日期时间)字段

  • time(默认值:-1)列包含时间字段,如果与日期时间字段分开(-1 表示不存在)

  • open(默认值:1),high(默认值:2),low(默认值:3),close(默认值:4),volume(默认值:5),openinterest(默认值:6)

    包含相应字段的列的索引

    如果传递负值(例如:-1),表示 CSV 数据中不存在该字段

  • nullvalue(默认值:float('NaN'))

    如果应该存在的值缺失(CSV 字段为空),将使用的值

  • dtformat(默认值:%Y-%m-%d %H:%M:%S)

    用于解析日期时间 CSV 字段的格式

  • tmformat(默认值:%H:%M:%S)

    用于解析时间 CSV 字段的格式(如果“存在”)(“时间”CSV 字段的默认值是不存在)

这可能足以涵盖许多不同的 CSV 格式和值的缺失。

涵盖以下要求的示例用法:

  • 限制输入至 2000 年

  • HLOC 顺序而不是 OHLC

  • 缺失值将被替换为零(0.0)

  • 提供每日 K 线数据,日期时间格式为 YYYY-MM-DD

  • 没有openinterest列存在

代码:

import datetime
import backtrader as bt
import backtrader.feeds as btfeed

...
...

data = btfeed.GenericCSVData(
    dataname='mydata.csv',

    fromdate=datetime.datetime(2000, 1, 1),
    todate=datetime.datetime(2000, 12, 31),

    nullvalue=0.0,

    dtformat=('%Y-%m-%d'),

    datetime=0,
    high=1,
    low=2,
    open=3,
    close=4,
    volume=5,
    openinterest=-1
)

稍微修改的要求:

  • 限制输入至 2000 年

  • HLOC 顺序而不是 OHLC

  • 缺失值将被替换为零(0.0)

  • 提供分钟级 K 线数据,具有单独的日期和时间列

    • 日期格式为 YYYY-MM-DD

    • 时间格式为 HH.MM.SS

  • 没有openinterest列存在

代码:

import datetime
import backtrader as bt
import backtrader.feeds as btfeed

...
...

data = btfeed.GenericCSVData(
    dataname='mydata.csv',

    fromdate=datetime.datetime(2000, 1, 1),
    todate=datetime.datetime(2000, 12, 31),

    nullvalue=0.0,

    dtformat=('%Y-%m-%d'),
    tmformat=('%H.%M.%S'),

    datetime=0,
    time=1,
    high=2,
    low=3,
    open=4,
    close=5,
    volume=6,
    openinterest=-1
)

改进佣金:股票与期货

原文:www.backtrader.com/blog/posts/2015-07-31-commission-schemes-updated/commission-schemes-updated/

发布 backtrader 使用示例让我对一些缺失的东西有了了解。首先:

  • 多核优化

  • 佣金:股票与期货

后者告诉了我:

  • 经纪人在利润和损失的计算方面做得很对,向调用策略提供了正确的订单通知

  • 策略无法访问operations(又称trades),这是订单已经开仓并关闭头寸的结果(后者显示出盈亏数字)

  • 绘制的Operation盈亏数字由一个Observer收集,并且无法访问实际的commission scheme,因此对于类似于期货的操作和类似于股票的操作,将显示相同的盈亏数字。

显然,需要进行一些小的内部重组才能实现:

  • 向策略发送Operation通知

  • Operations显示正确的盈亏数字

broker已经拥有了所有需要的信息,并且已经将大部分信息填入了被通知到strategyorder中,而broker需要做出的唯一决定是是否将额外的信息位放入订单中,或者它可以自己计算operations

由于策略已经获得了orders,并且将operations保留在列表中似乎很自然,broker只是在订单部分/完全关闭头寸时添加实际的盈亏,将计算责任留给了strategy

这进一步简化了Operations Observer的实际角色,即观察新关闭的Operation并记录下来。这是它一直应该有的角色。

下面的代码已经被重新设计,不再计算盈亏数字,而只是注意到notify_operation中通知的数字。

现在图表反映出了真实的盈亏数字(cashvalue已经是真实的)

旧的期货记录:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 2.00
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 2.00
2006-04-12, OPERATION PROFIT, GROSS 328.00, NET 324.00
2006-04-20, BUY CREATE, 3860.00
2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 2000.00, Comm 2.00
2006-04-28, SELL CREATE, 3839.90
2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 2000.00, Comm 2.00
2006-05-02, OPERATION PROFIT, GROSS -243.30, NET -247.30

新的期货记录:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 2.00
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 2.00
2006-04-12, OPERATION PROFIT, GROSS 328.00, NET 324.00
2006-04-20, BUY CREATE, 3860.00
2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 2000.00, Comm 2.00
2006-04-28, SELL CREATE, 3839.90
2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 2000.00, Comm 2.00
2006-05-02, OPERATION PROFIT, GROSS -243.30, NET -247.30
2006-05-02, BUY CREATE, 3862.24

旧的股票记录:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 3754.13, Comm 18.77
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 3786.93, Comm 18.93
2006-04-12, OPERATION PROFIT, GROSS 32.80, NET -4.91
2006-04-20, BUY CREATE, 3860.00
2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 3863.57, Comm 19.32
2006-04-28, SELL CREATE, 3839.90
2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 3839.24, Comm 19.20
2006-05-02, OPERATION PROFIT, GROSS -24.33, NET -62.84

新的股票记录:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 3754.13, Comm 18.77
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 3786.93, Comm 18.93
2006-04-12, OPERATION PROFIT, GROSS 32.80, NET -4.91
2006-04-20, BUY CREATE, 3860.00
2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 3863.57, Comm 19.32
2006-04-28, SELL CREATE, 3839.90
2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 3839.24, Comm 19.20
2006-05-02, OPERATION PROFIT, GROSS -24.33, NET -62.84
2006-05-02, BUY CREATE, 3862.24

图表(仅新的图表)。现在可以清楚地看到futures-like操作和stock-like操作之间的差异,不仅在cashvalue的变化中。

期货佣金

image

股票佣金

image

代码

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

import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind

futures_like = True

if futures_like:
    commission, margin, mult = 2.0, 2000.0, 10.0
else:
    commission, margin, mult = 0.005, None, 1

class SMACrossOver(bt.Strategy):
    def log(self, txt, dt=None):
        ''' Logging function fot this strategy'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def notify(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enougth cash
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            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))

    def notify_trade(self, trade):
        if trade.isclosed:
            self.log('TRADE PROFIT, GROSS %.2f, NET %.2f' %
                     (trade.pnl, trade.pnlcomm))

    def __init__(self):
        sma = btind.SMA(self.data)
        # > 0 crossing up / < 0 crossing down
        self.buysell_sig = btind.CrossOver(self.data, sma)

    def next(self):
        if self.buysell_sig > 0:
            self.log('BUY CREATE, %.2f' % self.data.close[0])
            self.buy()  # keep order ref to avoid 2nd orders

        elif self.position and self.buysell_sig < 0:
            self.log('SELL CREATE, %.2f' % self.data.close[0])
            self.sell()

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

    # Add a strategy
    cerebro.addstrategy(SMACrossOver)

    # 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)

    # set commission scheme -- CHANGE HERE TO PLAY
    cerebro.broker.setcommission(
        commission=commission, margin=margin, mult=mult)

    # Run over everything
    cerebro.run()

    # Plot the result
    cerebro.plot()

佣金:股票 vs 期货

原文:www.backtrader.com/blog/posts/2015-07-26-commission-schemes/commission-schemes/

backtrader 的诞生是出于必要性。我自己...希望有一种感觉,我可以控制自己的回测平台并尝试新的想法。但是在这样做并从一开始就完全开源化之后,很明显它必须有一种方式来满足其他人的需求和愿望。

作为交易者未来,我本可以选择编写基于点数的计算和每轮固定价格的佣金,但那将是一个错误。

注意

2015 年 7 月 31 日

跟进帖子,附带新添加的操作/交易通知,修复交易 P&L 图表的绘制,并避免像下面示例中那样的手动计算。

改善佣金:股票 vs 期货

相反,backtrader提供了使用常规%大小/价格基础方案和固定价格/点方案的可能性。选择权在你手上。

不可知论

在继续之前,让我们记住`backtrader`试图保持对数据表示的不可知。可以将不同的佣金方案应用于相同的数据集。

让我们看看如何做到这一点。

## 使用经纪人快捷方式

这样可以使最终用户远离`CommissionInfo`对象,因为可以通过单个函数调用创建/设置佣金方案。在常规的`cerebro`创建/设置过程中,只需将调用添加到`broker`成员变量上即可。以下调用在使用*InteractiveBrokers*时为**Eurostoxx50**期货设置了一种常规佣金方案:
cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)

由于大多数用户通常只测试单个工具,因此这就是问题的全部。如果您已经为数据源指定了name,因为图表上同时考虑了多个工具,因此此调用可以略微扩展为如下所示:

cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0,
name='Eurostoxxx50')

在这种情况下,这种即时佣金方案将仅应用于名称与Eurostoxx50匹配的工具。

设置佣金参数的含义

  • commission(默认值:0.0)

    每个操作的货币单位以绝对值或百分比形式的成本。

    在上面的示例中,每个buy合约需要 2.0 欧元,每个sell合约也是如此。

    这里的重要问题是何时使用绝对值或百分比值。

    • 如果margin评估为False(例如为 False、0 或 None),则将认为commission表示price乘以size操作值的百分比。

    • 如果margin是其他内容,则认为操作发生在类似期货的工具上,并且commission是每个size合约的固定价格

  • margin(默认值:None)

    使用期货等工具时需要的保证金。如上所述

    • 如果设置了no margin,则commission将被理解为以百分比表示,并应用于buysell操作的price * size组件

    • 如果设置了margin,则commission将被理解为与buysell操作的size分量相乘的固定值

  • mult(默认:1.0)

    对于类似期货的工具,这决定了要应用于利润和损失计算的乘数。

    这就是期货同时具有吸引力和风险的原因。

  • name(默认:无)

    限制佣金方案的应用于与name匹配的工具。

    这可以在创建数据源时设置。

    如果不设置,方案将应用于系统中存在的任何数据。

现在有两个例子:股票 vs 期货

来自上述的期货示例:

cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)

股票的一个例子:

cerebro.broker.setcommission(commission=0.005)  # 0.5% of the operation value

创建永久佣金方案

更持久的佣金方案可以通过直接使用CommissionInfo类来创建。用户可以选择将此定义放在某处:

from bt import CommissionInfo

commEurostoxx50 = CommissionInfo(commission=2.0, margin=2000.0, mult=10.0)

然后在另一个 Python 模块中应用它与addcommissioninfo

from mycomm import commEurostoxx50

...

cerebro.broker.addcomissioninfo(commEuroStoxx50, name='Eurostoxxx50')

CommissionInfo是一个对象,它使用与backtrader环境中的其他对象一样的params声明。因此,上述内容也可以表示为:

from bt import CommissionInfo

class CommEurostoxx50(CommissionInfo):
    params = dict(commission=2.0, margin=2000.0, mult=10.0)

后来:

from mycomm import CommEurostoxx50

...

cerebro.broker.addcomissioninfoCommEuroStoxx50(), name='Eurostoxxx50')

现在是与 SMA 交叉的“真实”比较

使用 SimpleMovingAverage 交叉作为入场/出场信号,将使用类似期货的佣金方案对同一数据集进行测试,然后再使用类似股票的方案。

注意

期货头寸不仅可以在每次发生时赋予进入/退出行为,还可以在每次发生时赋予反转行为。但是,此示例是关于比较佣金方案的。

代码(请参阅底部获取完整策略)是相同的,可以在定义策略之前选择方案。

futures_like = True

if futures_like:
    commission, margin, mult = 2.0, 2000.0, 10.0
else:
    commission, margin, mult = 0.005, None, 1

只需将futures_like设置为 false 即可使用类似股票的方案运行。

已添加一些记录代码以评估不同佣金方案的影响。让我们只关注前两个操作。

对于期货:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 2.00
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 2.00
2006-04-12, OPERATION PROFIT, GROSS 328.00, NET 324.00
2006-04-20, BUY CREATE, 3860.00
2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 2000.00, Comm 2.00
2006-04-28, SELL CREATE, 3839.90
2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 2000.00, Comm 2.00
2006-05-02, OPERATION PROFIT, GROSS -243.30, NET -247.30

对于股票:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 3754.13, Comm 18.77
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 3786.93, Comm 18.93
2006-04-12, OPERATION PROFIT, GROSS 32.80, NET -4.91
2006-04-20, BUY CREATE, 3860.00
2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 3863.57, Comm 19.32
2006-04-28, SELL CREATE, 3839.90
2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 3839.24, Comm 19.20
2006-05-02, OPERATION PROFIT, GROSS -24.33, NET -62.84

第一次操作具有以下价格:

  • 买入(执行)-> 3754.13 / 卖出(执行)-> 3786.93

    • 期货利润和损失(含佣金):324.0

    • 股票利润和损失(含佣金):-4.91

    嘿!! 佣金完全吞噬了股票操作的任何利润,但对期货操作只是造成了小小的凹痕。

第二次操作:

  • 买入(执行)-> 3863.57 / 卖出(执行)-> 3389.24

    • 期货利润和损失(含佣金):-247.30

    • 股票利润和损失(含佣金):-62.84

    这次负面操作对于期货的咬度明显更大

但:

  • 期货累计净利润和损失:324.00 + (-247.30) = 76.70

  • 股票累计净利润和损失:(-4.91) + (-62.84) = -67.75

累计效果可以在下面的图表中看到,在完整年份结束时,期货产生了更大的利润,但也遭受了更大的回撤(深入水中更深)

但重要的是:无论是期货还是股票都可以进行回测。

期货佣金

图像

股票佣金

图像

代码

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

import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind

futures_like = True

if futures_like:
    commission, margin, mult = 2.0, 2000.0, 10.0
else:
    commission, margin, mult = 0.005, None, 1

class SMACrossOver(bt.Strategy):
    def log(self, txt, dt=None):
        ''' Logging function fot this strategy'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def notify(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enougth cash
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
                self.opsize = order.executed.size
            else:  # Sell
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

                gross_pnl = (order.executed.price - self.buyprice) * \
                    self.opsize

                if margin:
                    gross_pnl *= mult

                net_pnl = gross_pnl - self.buycomm - order.executed.comm
                self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                         (gross_pnl, net_pnl))

    def __init__(self):
        sma = btind.SMA(self.data)
        # > 0 crossing up / < 0 crossing down
        self.buysell_sig = btind.CrossOver(self.data, sma)

    def next(self):
        if self.buysell_sig > 0:
            self.log('BUY CREATE, %.2f' % self.data.close[0])
            self.buy()  # keep order ref to avoid 2nd orders

        elif self.position and self.buysell_sig < 0:
            self.log('SELL CREATE, %.2f' % self.data.close[0])
            self.sell()

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

    # Add a strategy
    cerebro.addstrategy(SMACrossOver)

    # 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)

    # set commission scheme -- CHANGE HERE TO PLAY
    cerebro.broker.setcommission(
        commission=commission, margin=margin, mult=mult)

    # Run over everything
    cerebro.run()

    # Plot the result
    cerebro.plot()

多核优化

www.backtrader.com/blog/posts/2015-07-23-multicore-optimization/multicore-optimization/

利用所有可用核心是我对 backtrader 有的想法,但从未实现。支持自然操作,移除数组表示法,包含新的指标等等。

实际上,我并不是优化的忠实粉丝,因此对于为此利用所有核心也不是忠实粉丝。在我看来,一个好主意值得百万次优化。

注意

初始的多核支持已经存在,并且对于众所周知的一组测试用例有效。鉴于pickle所展示的行为,预计还需要进行一些其他调整,以确保在进行多核优化时可以在进程之间传递所有指标和函数。

注意

针对多核的一些额外校正已经发布为 1.0.10.88 版本,以使更多的“不可序列化”项变得可序列化。到目前为止,指标测试没有出现任何问题。

但是 BigMikeTrading 论坛上有人询问这个平台相比其他平台有什么优势,我提到了一些功能,包括PyAlgoTrade,例如,已经有了(甚至是多机器的)。

这需要做一点小而正确的推动。根据过去的经验以及因为互联网上充满了参考资料,我已经知道:多线程即使是最简单的(无论 GIL 律师们可能说什么),在 Python 中也是行不通的,无论版本如何。在 Python 中,多线程是假的,因为你有多个线程,但没有代码的并行执行。在 Python 中使用多线程可能会创建抽象,并用 IO 绑定的线程分开代码路径的执行,但这确实是一个致命问题。

那么我只剩下一个选择:模块multiprocessing或类似的模块。

展望光明的未来,我决定选择现代版本:concurrent.futures(后来证明是一个错误的选择)。即使这意味着为 Python 2.6/2.7 支持添加外部依赖。

历史:

  • Python 的一些动态特性与在进程之间发送数据不兼容

  • 序列化一些像类不在模块级别定义、lambda 表达式、对实例方法的引用以及没有唯一名称的动态类(即使类本身是唯一的)时,所涉及的模块(pickle)会出错。

我把这些东西散落在代码中。然后我发现了dill和 pathos 多进程的兄弟姐妹pypi.python.org/pypi/multiprocess。显然它们可以解决序列化问题,但是添加更多的外部依赖……不行不行。

回到起点,看看那些不可序列化的项是否可以被序列化,即使pickle模块产生了一些错误,这将使一些旧的 GCC 开发人员非常高兴。

它完成了吗……还是没有?

  • 将不可选的项目改造为可选项目

  • 用 Python 2.7.9 进行测试,并像风一样轻松地运行……我的机器的 8 个核心顺畅且令人耳目一新

  • 使用 Python 3.4.3 进行测试,8 个核心开始运作,但在进行一些优化后,每个后续策略的执行时间会越来越长……直到不堪忍受为止。

    显然,将结果(完整的执行策略)反向 pickling 到主进程中触及了一些与内存分配相关的限制(我的机器有大量空闲 RAM……足够多以进行几小时的并行优化)

阅读了一些额外的内容后,我考虑简化我的情景:

  • 使用 concurrent.futures 看起来更具未来性

  • 但标准的 multiprocessing 模块已经具备了 backtrader 所需的功能

闻起来好像有点过度,一些行被迅速改写成:

  • 测试用 Python 2.7 运行正常(甚至比以前更快)

  • 测试用 Python 3.4 同样快速运行

进行清理,运行完整的一系列测试并执行推送,发布 1.0.9.88。没有新的指标……只是多核优化的普通旧方式

读完这些……是时候写一个关于如何控制优化以使用多个核心的清爽脚本了

  • 好消息……不需要做任何事情……它在用户不介入的情况下完成了

当用户希望优化 strategy 时,Strategy 子类将被添加到 Cerebro 实例中,如下所示:

cerebro.optstrategy(StrategyClass, *args, **kwargs)

与向 Cerebro 传递策略的常规方式相反:

cerebro.addstrategy(StrategyClass, *args, **kwargs)

这一直都是这样,没有改变。背景是:

  • Cerebro 需要了解是否要优化策略,以正确处理可能已经是常规策略的策略的参数

现在……通过 optstrategy 传递给 cerebro策略将获得使用机器上所有可用核心的额外好处。

当然,如果最终用户希望对使用的核心进行精细控制……是可能的。创建 Cerebro 的标准方式:

cerebro = bt.Cerebro() # runonce 为 True,preload 为 True,且 “new” maxcpus 为 None

maxcpus(此版本中的新参数)是控制键:

  • maxcpus = None -> 使用所有可用的 CPU

  • maxcpus = 1 -> 不要运行多核

  • maxcpues = 2 … -> 使用指定数量的核心

这是一种选择退出策略,因为多核已经存在。

在拥有 16 GBytes RAM 的 4 核(每核 2 个线程 - 总共 8 个逻辑处理器)机器上进行比较,运行 Windows 8.1 和 Python 64 位 2.7.9

  • 使用 1 个核心执行:326 秒

  • 使用 8 个核心执行:127 秒

不同的测试运行显示,平均比例约为 2.75:1。

不幸的是,进程的创建/销毁和对象的反复 pickling 带来了潜在的好处,但加速效果仍然显著。

图像显示了正在使用的 8 个核心。

image

代码如下。只需将maxcpus参数的1更改为限制测试为 1 个核心。

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

import time

from six.moves import xrange

import backtrader as bt
import backtrader.indicators as btind
import backtrader.feeds as btfeeds

class OptimizeStrategy(bt.Strategy):
    params = (('smaperiod', 15),
              ('macdperiod1', 12),
              ('macdperiod2', 26),
              ('macdperiod3', 9),
              )

    def __init__(self):
        # Add indicators to add load

        btind.SMA(period=self.p.smaperiod)
        btind.MACD(period_me1=self.p.macdperiod1,
                   period_me2=self.p.macdperiod2,
                   period_signal=self.p.macdperiod3)

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

    # Add a strategy
    cerebro.optstrategy(
        OptimizeStrategy,
        smaperiod=xrange(5, 40),
        macdperiod1=xrange(12, 20),
        macdperiod2=xrange(26, 30),
        macdperiod3=xrange(9, 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)

    # clock the start of the process
    tstart = time.clock()

    # Run over everything
    stratruns = cerebro.run()

    # clock the end of the process
    tend = time.clock()

    print('==================================================')
    for stratrun in stratruns:
        print('**************************************************')
        for strat in stratrun:
            print('--------------------------------------------------')
            print(strat.p._getkwargs())
    print('==================================================')

    # print out the result
    print('Time used:', str(tend - tstart))

扩展一个指标

原文:www.backtrader.com/blog/posts/2015-07-20-extending-an-indicator/extending-an-indicator/

在面向对象编程中,当然也包括 Python 本身,对现有类的扩展可以通过两种方式实现。

  • 继承(或子类化)

  • 组合(或嵌入)

在开发一个指标时,指标Trix只需几行代码就可以开发完成。ChartSchool - Trix 参考文献中有一个带有信号线的Trix,显示了与 MACD 的相似之处。

让我们使用已经开发的Trix“组合”MyTrixSignal

class MyTrixSignalComposed(bt.Indicator):

    lines = ('trix', 'signal')
    params = (('period', 15), ('sigperiod', 9))

    def __init__(self):
        self.lines.trix = MyTrix(self.data, period=self.p.period)
        self.lines.signal = btind.EMA(self.lines.trix, period=self.p.sigperiod)

在定义中有一些必须重复的内容,比如trix线的名称和用于计算的period。定义了一个新的signal线和相应的sigperiod参数。

这个两行的结果很好。

现在让我们来看看继承,但首先回顾一下Trix的样子:

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作为基类,这是TrixSignal的外观

class MyTrixSignalInherited(MyTrix):

    lines = ('signal',)
    params = (('sigperiod', 9),)

    def __init__(self):
        super(MyTrixSignalInherited, self).__init__()
        self.lines.signal = btind.EMA(self.lines.trix, period=self.p.sigperiod)

继承的指标最终也是一个两行代码,但是:

  • 不需要重新定义trix线

  • 不需要重新定义period参数

两者都是从基类Trix继承而来。trix线的计算是在基类__init__方法中完成的:

  • super(MyTrixSignalInherited, self).init()

组合继承的选择是一个经典问题。这个例子并不是为了澄清哪种更好,而更多是为了展示:

注意

即使存在linesparams的元定义,它们也继承自基类的元定义

最后是代码和图表,展示两个版本的运行情况。

  1. 第一个展示了继承版本
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import backtrader as bt
import backtrader.feeds as btfeeds

from mytrix import MyTrixSignalInherited

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

    def __init__(self):
        MyTrixSignalInherited(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()

image

  1. 第一个展示了组合版本
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import backtrader as bt
import backtrader.feeds as btfeeds

from mytrix import MyTrixSignalComposed

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

    def __init__(self):
        MyTrixSignalComposed(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()

image

标签:00,59,二十八,self,BackTrader,order,文档,2006,price
From: https://www.cnblogs.com/apachecn/p/18135567

相关文章

  • BackTrader 中文文档(二十九)
    原文:www.backtrader.com/开发一个指标原文:www.backtrader.com/blog/posts/2015-07-18-developing-an-indicator/developing-an-indicator/在对backtrader进行了大量的微调之后(因为它已经运行了一段时间),我决定不仅通过GitHub分享它,还要告诉世界它的存在,并在"Reddit"上发......
  • 【合合TextIn】智能文档处理系列—电子文档解析技术全格式解析
    一、引言在当今的数字化时代,电子文档已成为信息存储和交流的基石。从简单的文本文件到复杂的演示文档,各种格式的电子文档承载着丰富的知识与信息,支撑着教育、科研、商业和日常生活的各个方面。随着信息量的爆炸性增长,如何高效、准确地处理和分析这些电子文档,已经成为信息技术领......
  • BackTrader 中文文档(二十一)
    原文:www.backtrader.com/目标订单原文:www.backtrader.com/blog/posts/2016-09-02-target-orders/target-orders/直到版本1.8.10.96,通过Strategy方法:买入和卖出,在backtrader上实现了智能的投注。一切都是关于向方程中添加一个Sizer,它负责赌注的大小。Sizer无法决定操作是......
  • BackTrader 中文文档(二十三)
    原文:www.backtrader.com/基准测试原文:www.backtrader.com/blog/posts/2016-07-22-benchmarking/benchmarking/backtrader包括两种不同类型的对象,可以帮助跟踪:观察者分析器问题#89是关于添加针对资产的基准测试。这是合理的,因为一个人可能实际上有一种策略,即使是......
  • BackTrader 中文文档(二十四)
    原文:www.backtrader.com/终极振荡器原文:www.backtrader.com/blog/posts/2016-06-22-ultimate-oscillator/ultimate-oscillator/当启动backtrader的开发时,其中一个目标是使其非常容易(至少对于作者本人来说)开发新的Indicators以测试数学和视觉上的想法。Ticket#102是关于将......
  • BackTrader 中文文档(二十五)
    原文:www.backtrader.com/Bid-Ask数据到OHLC原文:www.backtrader.com/blog/posts/2016-04-14-bidask-data-to-ohlc/bidask-data-to-ohlc/最近,backtrader通过实现线覆盖来执行了从ohlcland的逃逸,这允许重新定义整个层次结构,例如拥有仅包含bid、ask和datetime行的数据......
  • BackTrader 中文文档(二十六)
    原文:www.backtrader.com/条形图同步www.backtrader.com/blog/posts/2015-10-04-bar-synchronization/bar-synchronization/文献和/或行业中缺乏标准公式并不是问题,因为问题实际上可以总结为:条形图同步工单#23提出了一些关于backtrader是否可以计算相对成交量指标的疑......
  • BackTrader 中文文档(八)
    原文:www.backtrader.com/订单订单。译文:www.backtrader.com/docu/order/Cerebro是backtrader中的关键控制系统,而Strategy(一个子类)是最终用户的关键控制点。后者需要一种方法将系统的其他部分链接起来,这就是订单发挥关键作用的地方。订单将Strategy中的逻辑决策转化......
  • BackTrader 中文文档(十)
    原文:www.backtrader.com/用户自定义佣金原文:www.backtrader.com/docu/user-defined-commissions/commission-schemes-subclassing/重塑CommInfo对象到实际形式的最重要部分涉及:保留原始的CommissionInfo类和行为为轻松创建用户定义的佣金打开大门将格式xx%......
  • BackTrader 中文文档(十一)
    原文:www.backtrader.com/基准测试原文:www.backtrader.com/docu/observer-benchmark/benchmarking/票号#89是关于添加对资产的基准测试的。理智的做法是,即使有一种策略,即使是正的,也低于简单跟踪资产将提供的内容。backtrader包含两种不同类型的对象,可以帮助进行跟踪:......