订单管理和执行#

回测,包括 backtrader ,如果不能模拟订单将不完整。为了做到这一点,在平台上提供了以下内容。

订单管理有3个基本操作:

  • 买入

  • 卖出

  • 取消

注意

更新 是一个明显的逻辑操作,但常识告诉我们,这种方法主要由采用判断性交易方法的手动操作员使用。

订单执行逻辑有以下几种执行类型:

  • 市价

  • 收盘价

  • 限价

  • 止损

  • 止损限价

订单管理#

一些示例:#购买主要日期,默认股份规模,市价订单 order = self.buy()

#市价订单 - 有效期将“忽略” order = self.buy(valid=datetime.datetime.now() + datetime.timedelta(days=3))

#市价订单 - 价格将被忽略 order = self.buy(price=self.data.close[0] * 1.02)

#市价订单 - 手动股份 order = self.buy(size=25)

#限价订单 - 想要设置价格并可以设置有效期 order = self.buy(exectype=Order.Limit,

price=self.data.close[0] * 1.02, valid=datetime.datetime.now() + datetime.timedelta(days=3)))

#止损限价订单 - 想要设置价格和价格限制 order = self.buy(exectype=Order.StopLimit,

price=self.data.close[0] * 1.02, plimit=self.data.close[0] * 1.07)

#取消现有订单 self.broker.cancel(order)

注意

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

order = self.broker.submit(order).. 注解:

broker 本身有 buysell 的原语,但是在默认参数方面要严格些。

订单执行逻辑#

broker 有两个主要的指导方针(假设?)用于订单执行。

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

    如果策略中的逻辑是这样的:

     if self.data.close > self.sma:  # sma 是简单移动平均值
         self.buy()
    
    则期望是订单将使用正在检查逻辑中的 ``close`` 价格执行,但是因为该价格已经发生,
    所以这是不可能的。
    
    订单可以首先在下一组开/高/低/收价格点的范围内执行(以及订单中设置的条件)
    
  • Volume 不起作用实际交易中,如果交易员选择非流动性资产,或者命中价格条的极值点(高点/低点),它确实有效。

但是命中高点/低点是个罕见事件(如果你命中了,你不需要 “backtrader”)而且所选资产将有足够的流动性来吸收任何常规交易的订单

市场#

执行:

  • 下一个开/高/低/收价格集的开盘价(通常称为

原因:

  • 如果逻辑在时间 X 处执行并发出一个 “市场” 订单,接下来将发生的价格是即将到来的 “开盘” 价格

注意

这个订单总是执行,并且忽略用于创建它的任何 “价格” 和 “有效” 参数

收盘价 ——-执行:

  • 当下一根K线 实际关闭 时,使用下一根K线的“收盘”价格。

原理:

  • 大多数 回测 的数据源都包含已经 关闭 的K线,订单将立即以下一根K线的“收盘”价格执行。每日数据源是最常见的例子。

    但是,系统也可以使用“tick”价格,并且实际的K线(按时间/日期排序)会随着新的tick不断更新,而不会实际跳到 下一根 K线(因为时间和/或日期没有改变)。

    只有当时间或日期发生变化时,K线才真正关闭,订单才会执行。

限制#

执行:

  • 在订单创建时设置的 价格 ,如果 数据 触及它,则从下一个价格栏开始。

    如果 valid 已设置并且时间点已达到,订单将被取消。价格匹配:

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

    使用4个价格点(开盘价/最高价/最低价/收盘价),可以部分推断出请求的 价格 是否可以改进。

    对于“买入”订单

    • 情况1:

      如果柱状图的“开盘”价低于限价,则订单将立即以“开盘”价执行。订单在交易会话的开盘阶段被清空。

    • 情况2:

      如果“开盘”价没有低于限价,但是“最低”价低于限价,则在交易会话期间已经出现了限价,订单可以执行。

    对于“卖出”订单,逻辑显然相反。

停止 —-执行:

  • 在订单创建时,如果 data 触及了触发器 price ,则会设置该触发器,从下一个价格条开始。

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

价格匹配:

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

    使用4个价格点(开盘价/最高价/最低价/收盘价),可以部分推断出是否可以改进所请求的 price

    对于 买入Stop 订单

    • 情况1:

      如果柱状图的 开盘价 高于止损价,则立即以 开盘价 执行订单。

      目的是在价格向上移动,逆向反对现有的空头头寸时停止损失。- 情况2:

如果“开盘价”没有突破止损价,但“最高价”高于止损价,则在本交易日内看到了止损价,订单可以执行。

显然,“止损”订单与“卖出”订单的逻辑是相反的。

停止限价#

执行:

  • 触发“价格”根据下一个价格柱开始运作。

价格匹配:

  • 触发器 :使用“止损”匹配逻辑(但只触发并将订单转为“限价”订单)

  • 限价 :使用“限价”价格匹配逻辑

一些示例像往常一样,用图片(带代码)胜过长时间的解释。

请注意,代码片段集中在订单创建部分。完整的代码在底部。

将使用“价格突破/跌破简单移动平均线”策略来生成买入/卖出信号。

信号显示在图表底部:使用交叉指标 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 市价

2006-01-26T23:59:59+00:00, 买入创建, 执行方式:市价, 价格:3641.42 2006-01-26T23:59:59+00:00, 订单已接受/已提交 2006-01-27T23:59:59+00:00, 买入执行, 价格:3643.35, 成本:3643.35, 佣金:0.00 2006-03-02T23:59:59+00:00, 卖出创建, 价格:3763.73 2006-03-02T23:59:59+00:00, 订单已接受/已提交 2006-03-03T23:59:59+00:00, 卖出执行, 价格:3763.95, 成本:3763.95, 佣金:0.00 … … 2006-12-11T23:59:59+00:00, 买入创建, 执行方式:市价, 价格:4052.89 2006-12-11T23:59:59+00:00, 订单已接受/已提交 2006-12-12T23:59:59+00:00, 买入执行, 价格:4052.55, 成本:4052.55, 佣金:0.00

执行方式:收盘#

现在订单也在信号后的一根K线收盘价格执行。

            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 收盘 2006-01-26T23:59:59+00:00, 买入创建, 执行方式:收盘, 价格:3641.42 2006-01-26T23:59:59+00:00, 订单已接受/已提交 2006-01-27T23:59:59+00:00, 买入执行, 价格:3685.48, 成本:3685.48, 佣金:0.00 2006-03-02T23:59:59+00:00, 卖出创建, 价格:3763.73 2006-03-02T23:59:59+00:00, 订单已接受/已提交 2006-03-03T23:59:59+00:00, 卖出执行, 价格:3763.95, 成本:3763.95, 佣金:0.00 … … 2006-11-06T23:59:59+00:00, 买入创建, 执行方式:收盘, 价格:4045.22 2006-11-06T23:59:59+00:00, 订单已接受/已提交 2006-11-07T23:59:59+00:00, 买入执行, 价格:4072.86, 成本:4072.86, 佣金:0.00 2006-11-24T23:59:59+00:00, 卖出创建, 价格:4048.16 2006-11-24T23:59:59+00:00, 订单已接受/已提交 2006-11-27T23:59:59+00:00, 卖出执行, 价格:4045.05, 成本:4045.05, 佣金:0.00 2006-12-11T23:59:59+00:00, 买入创建, 执行方式:收盘, 价格:4052.89 2006-12-11T23:59:59+00:00, 订单已接受/已提交 2006-12-12T23:59:59+00:00, 买入执行, 价格:4059.74, 成本:4059.74, 佣金:0.00

执行方式:限价#

在一些行之前计算有效性,以防作为参数传递。.. literalinclude:: ./order-execution-samples.py
language:

python

lines:

103-108

设置一个比信号产生价格(信号栏的收盘价)低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, 买入创建, 执行类型:限价,价格:3605.01,有效期至:2006-01-30 2006-01-26T23:59:59+00:00, 订单被接受/提交 2006-01-30T23:59:59+00:00, 买入订单过期 2006-03-10T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3760.48,有效期至:2006-03-14 2006-03-10T23:59:59+00:00, 订单被接受/提交 2006-03-14T23:59:59+00:00, 买入订单过期 2006-03-30T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3835.86,有效期至:2006-04-03 2006-03-30T23:59:59+00:00, 订单被接受/提交 2006-04-03T23:59:59+00:00, 买入订单过期 2006-04-20T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3821.40,有效期至:2006-04-24 2006-04-20T23:59:59+00:00, 订单被接受/提交 2006-04-24T23:59:59+00:00, 买入订单过期 2006-05-04T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3804.65,有效期至:2006-05-08 2006-05-04T23:59:59+00:00, 订单被接受/提交 2006-05-08T23:59:59+00:00, 买入订单过期 2006-06-01T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3611.85,有效期至:2006-06-05 2006-06-01T23:59:59+00:00, 订单被接受/提交 2006-06-05T23:59:59+00:00, 买入订单过期 2006-06-21T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3491.57,有效期至:2006-06-25 2006-06-21T23:59:59+00:00, 订单被接受/提交 2006-06-26T23:59:59+00:00, 买入订单过期 2006-07-24T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3596.60,有效期至:2006-07-28 2006-07-24T23:59:59+00:00, 订单被接受/提交 2006-07-28T23:59:59+00:00, 买入订单过期 2006-09-12T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3751.07,有效期至:2006-09-16 2006-09-12T23:59:59+00:00, 订单被接受/提交 2006-09-18T23:59:59+00:00, 买入订单过期 2006-09-20T23:59:59+00:00, 买入创建, 执行类型:限价,价格:3802.90,有效期至:2006-09-24 2006-09-20T23:59:59+00:00, 订单被接受/提交 2006-09-22T23:59:59+00:00, 买入执行, 价格:3802.90, 成本:3802.90, 手续费:0.00 2006-11-02T23:59:59+00:00, 卖出创建, 价格:3974.62 2006-11-02T23:59:59+00:00, 订单被接受/提交 2006-11-03T23:59:59+00:00, 卖出执行, 价格:3979.73, 成本:3979.73, 手续费:0.00 2006-11-06T23:59:59+00:00, 买入创建, 执行类型:限价,价格:4004.77,有效期至:2006-11-10 2006-11-06T23:59:59+00:00, 订单被接受/提交 2006-11-10T23:59:59+00:00, 买入订单过期 2006-12-11T23:59:59+00:00, 买入创建, 执行类型:限价,价格:4012.36,有效期至:2006-12-15 2006-12-11T23:59:59+00:00, 订单被接受/提交 2006-12-15T23:59:59+00:00, 买入订单过期

执行类型:止损#

设置停止价格比信号价格高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)

输出图表。.. 缩略图:: ./stop-perc1-1.png

命令行和输出:

$ ./order-execution-samples.py --exectype Stop --perc1 1
2006-01-26T23:59:59+00:00, 买入信号, 执行类型: Stop, 价格 3677.83
2006-01-26T23:59:59+00:00, 订单已接受/已提交
2006-01-27T23:59:59+00:00, 买入已执行, 价格: 3677.83, 成本: 3677.83, 手续费 0.00
2006-03-02T23:59:59+00:00, 卖出信号, 价格 3763.73
2006-03-02T23:59:59+00:00, 订单已接受/已提交
2006-03-03T23:59:59+00:00, 卖出已执行, 价格: 3763.95, 成本: 3763.95, 手续费 0.00
2006-03-10T23:59:59+00:00, 买入信号, 执行类型: Stop, 价格 3836.44
2006-03-10T23:59:59+00:00, 订单已接受/已提交
2006-03-15T23:59:59+00:00, 买入已执行, 价格: 3836.44, 成本: 3836.44, 手续费 0.00
2006-03-28T23:59:59+00:00, 卖出信号, 价格 3811.45
2006-03-28T23:59:59+00:00, 订单已接受/已提交
2006-03-29T23:59:59+00:00, 卖出已执行, 价格: 3811.85, 成本: 3811.85, 手续费 0.00
2006-03-30T23:59:59+00:00, 买入信号, 执行类型: Stop, 价格 3913.36
2006-03-30T23:59:59+00:00, 订单已接受/已提交
2006-09-29T23:59:59+00:00, 买入已执行, 价格: 3913.36, 成本: 3913.36, 手续费 0.00
2006-11-02T23:59:59+00:00, 卖出信号, 价格 3974.62
2006-11-02T23:59:59+00:00, 订单已接受/已提交
2006-11-03T23:59:59+00:00, 卖出已执行, 价格: 3979.73, 成本: 3979.73, 手续费 0.00
2006-11-06T23:59:59+00:00, 买入信号, 执行类型: Stop, 价格 4085.67
2006-11-06T23:59:59+00:00, 订单已接受/已提交
2006-11-13T23:59:59+00:00, 买入已执行, 价格: 4085.67, 成本: 4085.67, 手续费 0.00
2006-11-24T23:59:59+00:00, 卖出信号, 价格 4048.16
2006-11-24T23:59:59+00:00, 订单已接受/已提交
2006-11-27T23:59:59+00:00, 卖出已执行, 价格: 4045.05, 成本: 4045.05, 手续费 0.00
2006-12-11T23:59:59+00:00, 买入信号, 执行类型: Stop, 价格 4093.42
2006-12-11T23:59:59+00:00, 订单已接受/已提交
2006-12-13T23:59:59+00:00, 买入已执行, 价格: 4093.42, 成本: 4093.42, 手续费 0.00

执行类型: 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, 买入建立, 执行类型 StopLimit, 价格 3677.83, 有效期: 2006-02-15, 价格限制: 3659.63 2006-01-26T23:59:59+00:00, 订单已接受/已提交 2006-02-03T23:59:59+00:00, 买入执行, 价格: 3659.63, 成本: 3659.63, 佣金 0.00 2006-03-02T23:59:59+00:00, 卖出建立, 3763.73 2006-03-02T23:59:59+00:00, 订单已接受/已提交 2006-03-03T23:59:59+00:00, 卖出执行, 价格: 3763.95, 成本: 3763.95, 佣金 0.00 2006-03-10T23:59:59+00:00, 买入建立, 执行类型 StopLimit, 价格 3836.44, 有效期: 2006-03-30, 价格限制: 3817.45 2006-03-10T23:59:59+00:00, 订单已接受/已提交 2006-03-21T23:59:59+00:00, 买入执行, 价格: 3817.45, 成本: 3817.45, 佣金 0.00 2006-03-28T23:59:59+00:00, 卖出建立, 3811.45 2006-03-28T23:59:59+00:00, 订单已接受/已提交 2006-03-29T23:59:59+00:00, 卖出执行, 价格: 3811.85, 成本: 3811.85, 佣金 0.00 2006-03-30T23:59:59+00:00, 买入建立, 执行类型 StopLimit, 价格 3913.36, 有效期: 2006-04-19, 价格限制: 3893.98 2006-03-30T23:59:59+00:00, 订单已接受/已提交 2006-04-19T23:59:59+00:00, 订单过期 ... ... 2006-12-11T23:59:59+00:00, 买入建立, 执行类型 StopLimit, 价格 4093.42, 有效期: 2006-12-31, 价格限制: 4073.15 2006-12-11T23:59:59+00:00, 订单已接受/已提交 2006-12-22T23:59:59+00:00, 买入执行, 价格: 4073.15, 成本: 4073.15, 佣金 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]

展示订单执行类型

可选参数:
-h, --help

显示帮助信息并退出

--infile INFILE, -i INFILE

要读取的文件

–csvformat {bt,visualchart,sierrachart,yahoo,yahoo_unreversed}, -c {bt,visualchart,sierrachart,yahoo,yahoo_unreversed}

CSV 格式

--fromdate FROMDATE, -f FROMDATE

开始日期,格式为YYYY-MM-DD

--todate TODATE, -t TODATE

结束日期,格式为YYYY-MM-DD

--plot, -p

绘制读取的数据

–plotstyle {bar,line,candle}, -ps {bar,line,candle}

绘制读取的数据

--numfigs NUMFIGS, -n NUMFIGS

使用n个图形进行绘制

--smaperiod SMAPERIOD, -s SMAPERIOD

简单移动平均周期

--exectype EXECTYPE, -e EXECTYPE
执行类型: 市价 (默认), 收盘价, 限价,

止损, 止损限价

--valid VALID, -v VALID

限价订单的有效期: 默认为0天

–perc1 PERC1, -p1 PERC1

限价/触发价格与下单时收盘价的百分比距离

–perc2 PERC2, -p2 PERC2

止损限价订单中限价价格与下单时收盘价的百分比距离

```

完整代码#

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

```