订单管理和执行#
回测,包括 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
本身有 buy
和 sell
的原语,但是在默认参数方面要严格些。
订单执行逻辑#
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()