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

BackTrader 中文文档(八)

时间:2024-04-15 11:16:18浏览次数:21  
标签:中文 59 BackTrader self 00 Order 文档 2006 order

原文:www.backtrader.com/

订单

订单。

译文:www.backtrader.com/docu/order/

Cerebrobacktrader 中的关键控制系统,而 Strategy(一个子类)是最终用户的关键控制点。后者需要一种方法将系统的其他部分链接起来,这就是订单发挥关键作用的地方。

订单Strategy 中的逻辑决策转化为适合 Broker 执行操作的消息。这是通过以下方式完成的:

  • 创建

    通过 Strategy 的方法:buy\``,sellclose(Strategy),它们返回一个order`实例作为参考。

  • 取消

    通过 Strategy 的方法:cancel(Strategy),它接受一个订单实例进行操作。

订单也作为一种向用户传达信息的方法,通知经纪人运行情况。

  • 通知

    对 Strategy 方法:notify_order(Strategy),报告一个order实例。

订单创建。

在调用 buysellclose 时,以下参数适用于创建:

  • data(默认值:None)。

    要创建订单的数据。如果为 None,则系统中的第一个数据,self.datas[0] 或 self.data0(又名 self.data),将被使用。

  • size(默认值:None)。

    要使用的单位数据的大小(正数)用于订单。

    如果为 None,则将使用通过 getsizer 检索到的 sizer 实例来确定大小。

  • price(默认值:None)。

    要使用的价格(实时经纪人可能对实际格式施加限制,如果不符合最小跳动要求)。

    None 对于 MarketClose 订单是有效的(市场确定价格)。

    对于 LimitStopStopLimit 订单,此值确定触发点(在 Limit 的情况下,触发显然是订单应该匹配的价格)。

  • plimit(默认值:None)。

    仅适用于 StopLimit 订单。这是在Stop触发后设置隐式限价订单的价格(其中使用了price)。

  • exectype(默认值:None)。

    可能的值:

    • Order.MarketNone。市价订单将以下一个可用价格执行。在回测中,它将是下一个柱的开盘价。

    • Order.Limit。只能以给定的 price 或更好的价格执行的订单。

    • Order.Stop。在 price 触发并像 Order.Market 订单一样执行的订单。

    • Order.StopLimit。在 price 触发并作为隐式限价订单执行的订单,其价格由 pricelimit 给出。

  • valid(默认值:None)。

    可能的值:

    • None:这将生成一个不会过期的订单(也称为有效直至取消),并保持在市场上直到匹配或取消。实际上,经纪人往往会强加时间限制,但通常时间跨度很长,可以视为不会过期。

    • datetime.datetimedatetime.date 实例:日期将用于生成一个在给定日期时间之前有效的订单(也称为有效截止日期)。

    • Order.DAY0timedelta(): 将生成一个直到Session 结束(又称day订单)有效的订单

    • numeric value: 假定这是与matplotlib编码中的日期时间相对应的值(backtrader使用的编码),并将用于生成直到那个时间点有效的订单(good till date)。

  • tradeid(默认:0

    这是backtrader用来跟踪同一资产上重叠交易的内部值。当订单状态发生变化时,该tradeid会发送回策略

  • **kwargs:其他经纪人实现可能支持额外的参数。backtraderkwargs传递给创建的订单对象

    例如:如果backtrader直接支持的 4 种订单执行类型不够用,例如Interactive Brokers的情况下,可以将以下内容作为kwargs传递:

    orderType='LIT', lmtPrice=10.0, auxPrice=9.8` 
    

    这将覆盖由backtrader创建的设置,并生成具有touched价格为 9.8 和limit价格为 10.0 的LIMIT IF TOUCHED订单。

注意

close方法将检查当前仓位,并相应地使用buysell来有效地close该仓位。 size也将自动计算,除非参数是来自用户的输入,在这种情况下可以实现部分closereversal

订单通知

要接收通知,用户子类的Strategy必须重写notify_order方法(默认行为是什么也不做)。以下内容适用于这些通知:

  • 在调用策略的next方法之前发出

  • 可能(并且将)在相同的next周期内多次发生对于相同或不同状态的相同order

    订单可能在next再次被调用之前提交给经纪人接受并且其执行完成

    在这种情况下,至少会有 3 个通知,其status值如下:

    • Order.Submitted因为订单已发送给经纪人

    • Order.Accepted因为订单已被经纪人接受并等待可能的执行。

    • Order.Completed因为在示例中它被迅速匹配和完全填充(这在Market订单通常情况下可能是发生的)。

即使在相同的状态下,通知也可能多次发生在Order.Partial的情况下。这个状态不会在backtesting经纪人(不考虑匹配时的成交量)中看到,但是肯定会被真实的经纪人设置。

真实的经纪人可能在更新仓位之前发出一个或多个执行,这组执行将组成Order.Partial通知。

实际执行数据在属性中:order.executed,它是OrderData类型的对象(参见下文的引用),具有常见的字段如sizeprice

创建时的值存储在order.created中,在order的整个生命周期内保持不变。

订单状态值

以下内容已定义:

  • Order.Created:在创建Order实例时设置。 除非订单实例是手动创建而不是通过买入卖出关闭,否则永远不会被最终用户看到。

  • Order.Submitted:在订单实例已传输到经纪人时设置。 这只意味着它已经发送。 在回测模式下,这将是一个立即的动作,但是在实际的时间中,这可能需要一段时间才能与真正的经纪人完成,这可能会接收订单,只有在转发到交易所时才会首次通知。

  • Order.Acceptedbroker已接受订单并且已经在系统中(或已经在交易所中)等待根据设置的参数(如执行类型,大小,价格和有效性)执行

  • Order.Partial:订单已部分执行。 order.executed包含当前填充的size和平均价格。

    order.executed.exbits包含一个详细的ExecutionBits列表,详细说明了部分填充情况

  • Order.Complete:订单已完全填充的平均价格。

  • Order.Rejected:经纪人已拒绝订单。 一个参数(例如确定其生存期的valid)可能不被经纪人接受,订单将无法被接受。

    原因将通过strategynotify_store方法通知。 尽管这可能看起来很奇怪,但原因是真实生活的经纪人将通过事件通知这一点,该事件可能与订单直接相关也可能与订单无关。 但是来自经纪人的通知仍然可以在notify_store中看到。

    此状态将不会在回测经纪人中看到

  • Order.Margin:订单执行将意味着保证金调用,并且先前接受的订单已从系统中移除

  • Order.Cancelled(或Order.Canceled):用户请求取消的确认

    必须考虑到通过策略的cancel方法取消订单的请求不是取消的保证。 订单可能已经被执行,但是经纪人可能尚未通知,和/或通知可能尚未传递给策略。

  • Order.Expired:先前接受的订单其具有时间有效性已过期并已从系统中移除

参考:订单和相关类

这些对象是backtrader生态系统中的通用类。 在与其他经纪人操作时,它们可以被扩展和/或包含额外的嵌入信息。 请参阅适当经纪人的参考资料。

backtrader.order.Order()

持有创建/执行数据和订单类型的类。

订单可能具有以下状态:

  • 已提交:已发送到经纪人并等待确认

  • 已接受:经纪人接受的

  • 部分:部分执行

  • 已完成:已完全执行

  • 已取消:用户取消的已取消

  • 已过期:已过期

  • 保证金:没有足够的现金执行订单。

  • 被拒绝:被经纪人拒绝

    这可能发生在订单提交时(因此订单不会达到已接受的状态)或在执行之前,每个新的条价格因为现金已经被其他来源提取(类似期货的工具可能会减少现金或订单已经被执行)

成员属性:

  • ref:唯一的订单标识符

  • created:持有创建数据的 OrderData

  • executed:持有执行数据的订单数据

  • info:通过方法 addinfo() 传递的自定义信息。它以 OrderedDict 的形式保留,已经被子类化,因此还可以使用‘.’符号指定键

用户方法:

  • isbuy():返回一个布尔值,指示订单是否购买

  • issell():返回一个布尔值,指示订单是否卖出

  • alive():如果订单处于部分或已接受状态,则返回布尔值

类 backtrader.order.OrderData(dt=None, size=0, price=0.0, pricelimit=0.0, remsize=0, pclose=0.0, trailamount=0.0, trailpercent=0.0)

包含创建和执行的实际订单数据。

在创建时进行的请求,在执行时进行的实际结果。

成员属性:

  • exbits:此 OrderData 的 OrderExecutionBits 的可迭代对象

  • dt:日期时间(浮点数)创建/执行时间

  • size:请求/执行大小

  • price:执行价格注意:如果未给出价格且未给出价格限制,则将在订单创建时使用当前收盘价作为参考

  • pricelimit:保存 StopLimit 的价格限制(先触发)

  • trailamount:追踪止损的绝对价格距离

  • trailpercent:追踪止损的百分比价格距离

  • value:整个比特大小的市场价值

  • comm:整个比特执行的佣金

  • pnl:由此比特生成的 pnl(如果有东西被关闭)

  • margin:订单所产生的保证金(如果有)

  • psize:当前开放位置大小

  • pprice:当前开放位置价格

类 backtrader.order.OrderExecutionBit(dt=None, size=0, price=0.0, closed=0, closedvalue=0.0, closedcomm=0.0, opened=0, openedvalue=0.0, openedcomm=0.0, pnl=0.0, psize=0, pprice=0.0)

用于保存订单执行信息。 “比特” 不确定订单是否已完全/部分执行,它只是保存信息。

成员属性:

  • dt:日期时间(浮点数)执行时间

  • size:执行了多少

  • price:执行价格

  • closed:执行关闭了现有的位置多少

  • opened:执行打开了多少新的位置

  • openedvalue:打开部分的市值

  • closedvalue:关闭部分的市值

  • closedcomm:关闭部分的佣金

  • openedcomm:打开部分的佣金

  • value:整个比特大小的市场价值

  • comm:整个比特执行的佣金

  • pnl:由此比特生成的 pnl(如果有东西被关闭)

  • psize:当前开放位置大小

  • pprice:当前开放位置价格

订单管理和执行

原文:www.backtrader.com/docu/order-creation-execution/order-creation-execution/

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

对于订单管理的 3 个基本原则:

  • buy

  • sell

  • cancel

注意

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

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

  • Market

  • Close

  • Limit

  • Stop

  • StopLimit

订单管理

一些例子:

# 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的基本原则,但对于默认参数要求更严格。

订单执行逻辑

经纪人对订单执行有两个主要准则(假设?)

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

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

     if self.data.close > self.sma:  # where sma is a Simple Moving Average
         self.buy()` 
    

    期望不能是订单将以正在逻辑中检查的close价格执行,因为它已经发生。

    订单可以在下一个一组开/高/低/收价格点的范围内(以及订单中规定的条件)第一次执行

  • 交易量不起作用

    如果交易者选择非流动资产或者精确地击中价格柱的极端点(高/低),它在实际交易中确实会发生。

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

市场

执行:

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

原因:

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

注意

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

Close

执行:

  • 当下一个柱子实际关闭时,使用下一个柱子的close价格

原因:

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

    但是系统可以提供“tick”价格,并且实际柱(时间/日期方面)会不断更新新的 tick,而不会实际移动到下一个柱(因为时间和/或日期尚未更改)

    只有当时间或日期发生变化时,柱子才会真正关闭并执行订单

Limit

执行:

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

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

价格匹配:

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

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

    对于Buy订单

    • 情况 1:

      如果柱的开盘价低于限价,则订单立即以开盘价执行。订单已在会话开启阶段清算

    • 情况 2:

      如果开盘价没有穿过限价但最低价低于限价,则在会话期间已见到限价,订单可以执行

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

停止

执行:

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

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

价格匹配:

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

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

    对于Buy\Stoporders

    • 情况 1:

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

      旨在在价格向上移动反对现有空头头寸时停止损失

    • 情况 2:

      如果开盘价没有穿过止损价但最高价高于止损价,则在会话期间已见到止损价,订单可以执行

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

停止限价

执行:

  • 触发价格设置订单,从下一个价格柱开始。

价格匹配:

  • 触发:使用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])

输出图表。

image

命令行和输出:

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

执行类型:关闭

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

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

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

输出图表。

image

命令行和输出:

$ ./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)

输出图表。

image

仅发出了 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(日历)天内有效,而这可能只有在价格对“买入”订单不利时才能执行。

输出图表。

image

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

命令行和输出:

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

执行类型:Stop

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

输出图表。

image

命令行和输出:

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

执行类型: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))

输出图表。

image

命令行和输出:

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

测试脚本执行

在命令行help中详细说明:

$ ./order-execution-samples.py --help
usage: order-execution-samples.py [-h] [--infile INFILE]
                                  [--csvformat {bt,visualchart,sierrachart,yahoo,yahoo_unreversed}]
                                  [--fromdate FROMDATE] [--todate TODATE]
                                  [--plot] [--plotstyle {bar,line,candle}]
                                  [--numfigs NUMFIGS] [--smaperiod SMAPERIOD]
                                  [--exectype EXECTYPE] [--valid VALID]
                                  [--perc1 PERC1] [--perc2 PERC2]

Showcase for Order Execution Types

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

完整代码

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

import argparse
import datetime
import os.path
import time
import sys

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

class OrderExecutionStrategy(bt.Strategy):
    params = (
        ('smaperiod', 15),
        ('exectype', 'Market'),
        ('perc1', 3),
        ('perc2', 1),
        ('valid', 4),
    )

    def log(self, txt, dt=None):
  ''' Logging function fot this strategy'''
        dt = dt or self.data.datetime[0]
        if isinstance(dt, float):
            dt = bt.num2date(dt)
        print('%s, %s' % (dt.isoformat(), txt))

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            self.log('ORDER ACCEPTED/SUBMITTED', dt=order.created.dt)
            self.order = order
            return

        if order.status in [order.Expired]:
            self.log('BUY EXPIRED')

        elif order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))

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

        # Sentinel to None: new orders allowed
        self.order = None

    def __init__(self):
        # SimpleMovingAverage on main data
        # Equivalent to -> sma = btind.SMA(self.data, period=self.p.smaperiod)
        sma = btind.SMA(period=self.p.smaperiod)

        # CrossOver (1: up, -1: down) close / sma
        self.buysell = btind.CrossOver(self.data.close, sma, plot=True)

        # Sentinel to None: new ordersa allowed
        self.order = None

    def next(self):
        if self.order:
            # An order is pending ... nothing can be done
            return

        # Check if we are in the market
        if self.position:
            # In the maerket - check if it's the time to sell
            if self.buysell < 0:
                self.log('SELL CREATE, %.2f' % self.data.close[0])
                self.sell()

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

            # Not in the market and signal to buy
            if self.p.exectype == 'Market':
                self.buy(exectype=bt.Order.Market)  # default if not given

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

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

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

            elif self.p.exectype == 'Limit':
                price = self.data.close * (1.0 - self.p.perc1 / 100.0)

                self.buy(exectype=bt.Order.Limit, price=price, valid=valid)

                if self.p.valid:
                    txt = 'BUY CREATE, exectype Limit, price %.2f, valid: %s'
                    self.log(txt % (price, valid.strftime('%Y-%m-%d')))
                else:
                    txt = 'BUY CREATE, exectype Limit, price %.2f'
                    self.log(txt % price)

            elif self.p.exectype == 'Stop':
                price = self.data.close * (1.0 + self.p.perc1 / 100.0)

                self.buy(exectype=bt.Order.Stop, price=price, valid=valid)

                if self.p.valid:
                    txt = 'BUY CREATE, exectype Stop, price %.2f, valid: %s'
                    self.log(txt % (price, valid.strftime('%Y-%m-%d')))
                else:
                    txt = 'BUY CREATE, exectype Stop, price %.2f'
                    self.log(txt % price)

            elif self.p.exectype == 'StopLimit':
                price = self.data.close * (1.0 + self.p.perc1 / 100.0)

                plimit = self.data.close * (1.0 + self.p.perc2 / 100.0)

                self.buy(exectype=bt.Order.StopLimit, price=price, valid=valid,
                         plimit=plimit)

                if self.p.valid:
                    txt = ('BUY CREATE, exectype StopLimit, price %.2f,'
                           ' valid: %s, pricelimit: %.2f')
                    self.log(txt % (price, valid.strftime('%Y-%m-%d'), plimit))
                else:
                    txt = ('BUY CREATE, exectype StopLimit, price %.2f,'
                           ' pricelimit: %.2f')
                    self.log(txt % (price, plimit))

def runstrat():
    args = parse_args()

    cerebro = bt.Cerebro()

    data = getdata(args)
    cerebro.adddata(data)

    cerebro.addstrategy(
        OrderExecutionStrategy,
        exectype=args.exectype,
        perc1=args.perc1,
        perc2=args.perc2,
        valid=args.valid,
        smaperiod=args.smaperiod
    )
    cerebro.run()

    if args.plot:
        cerebro.plot(numfigs=args.numfigs, style=args.plotstyle)

def getdata(args):

    dataformat = dict(
        bt=btfeeds.BacktraderCSVData,
        visualchart=btfeeds.VChartCSVData,
        sierrachart=btfeeds.SierraChartCSVData,
        yahoo=btfeeds.YahooFinanceCSVData,
        yahoo_unreversed=btfeeds.YahooFinanceCSVData
    )

    dfkwargs = dict()
    if args.csvformat == 'yahoo_unreversed':
        dfkwargs['reverse'] = True

    if args.fromdate:
        fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
        dfkwargs['fromdate'] = fromdate

    if args.todate:
        fromdate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
        dfkwargs['todate'] = todate

    dfkwargs['dataname'] = args.infile

    dfcls = dataformat[args.csvformat]

    return dfcls(**dfkwargs)

def parse_args():
    parser = argparse.ArgumentParser(
        description='Showcase for Order Execution Types')

    parser.add_argument('--infile', '-i', required=False,
                        default='../../datas/2006-day-001.txt',
                        help='File to be read in')

    parser.add_argument('--csvformat', '-c', required=False, default='bt',
                        choices=['bt', 'visualchart', 'sierrachart',
                                 'yahoo', 'yahoo_unreversed'],
                        help='CSV Format')

    parser.add_argument('--fromdate', '-f', required=False, default=None,
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--todate', '-t', required=False, default=None,
                        help='Ending date in YYYY-MM-DD format')

    parser.add_argument('--plot', '-p', action='store_false', required=False,
                        help='Plot the read data')

    parser.add_argument('--plotstyle', '-ps', required=False, default='bar',
                        choices=['bar', 'line', 'candle'],
                        help='Plot the read data')

    parser.add_argument('--numfigs', '-n', required=False, default=1,
                        help='Plot using n figures')

    parser.add_argument('--smaperiod', '-s', required=False, default=15,
                        help='Simple Moving Average Period')

    parser.add_argument('--exectype', '-e', required=False, default='Market',
                        help=('Execution Type: Market (default), Close, Limit,'
                              ' Stop, StopLimit'))

    parser.add_argument('--valid', '-v', required=False, default=0, type=int,
                        help='Validity for Limit sample: default 0 days')

    parser.add_argument('--perc1', '-p1', required=False, default=0.0,
                        type=float,
                        help=('%% distance from close price at order creation'
                              ' time for the limit/trigger price in Limit/Stop'
                              ' orders'))

    parser.add_argument('--perc2', '-p2', required=False, default=0.0,
                        type=float,
                        help=('%% distance from close price at order creation'
                              ' time for the limit price in StopLimit orders'))

    return parser.parse_args()

if __name__ == '__main__':
    runstrat()

目标订单

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

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

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

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

  • size -> 特定资产组合中的股票、合约数量

  • value -> 组合中资产的货币单位价值

  • percent -> 百分比(来自当前组合)资产在当前组合中的价值

注意

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

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

  • 如果target大于持仓量,则发出买入指令,差额为target - position_size

    示例:

    • Pos: 0, target: 7 -> 买入(size=7 - 0) -> 买入(size=7)

    • Pos: 3, target: 7 -> 买入(size=7 - 3) -> 买入(size=4)

    • Pos: -3, target: 7 -> 买入(size=7 - -3) -> 买入(size=10)

    • Pos: -3, target: -2 -> 买入(size=-2 - -3) -> 买入(size=1)

  • 如果target小于持仓量,则发出卖出指令,差额为position_size - target

    示例:

    • Pos: 0, target: -7 -> 卖出(size=0 - -7) -> 卖出(size=7)

    • Pos: 3, target: -7 -> 卖出(size=3 - -7) -> 卖出(size=10)

    • Pos: -3, target: -7 -> sell(size=-3 - -7) -> sell(size=4)

    • Pos: 3, target: 2 -> sell(size=3 - 2) -> sell(size=1)

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

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

因此,逻辑如下:

  • 如果target > valuesize >=0 -> 买入

  • 如果target > valuesize < 0 -> 卖出

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

  • 如果target < valuesize < 0 -> 买入

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

示例

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

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

  • 奇数月(一月,三月,...),使用作为目标(对于order_target_value,将日乘以1000

    这模仿了一个递增的目标

  • 偶数月(二月,四月,...)使用31 - day作为目标

    这模仿了一个递减的目标

order_target_size

让我们看看在一月二月会发生什么。

$ ./order_target.py --target-size -- plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - Order Target Size: 03
0002 - 2005-01-04 - Position Size:     03 - Value 999994.39
0002 - 2005-01-04 - Order Target Size: 04
0003 - 2005-01-05 - Position Size:     04 - Value 999992.48
0003 - 2005-01-05 - Order Target Size: 05
0004 - 2005-01-06 - Position Size:     05 - Value 999988.79
...
0020 - 2005-01-31 - Position Size:     28 - Value 999968.70
0020 - 2005-01-31 - Order Target Size: 31
0021 - 2005-02-01 - Position Size:     31 - Value 999954.68
0021 - 2005-02-01 - Order Target Size: 30
0022 - 2005-02-02 - Position Size:     30 - Value 999979.65
0022 - 2005-02-02 - Order Target Size: 29
0023 - 2005-02-03 - Position Size:     29 - Value 999966.33
0023 - 2005-02-03 - Order Target Size: 28
...

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

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

图片

order_target_value

类似的行为可预期来自目标值

$ ./order_target.py --target-value --plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - data value 0.00
0001 - 2005-01-03 - Order Target Value: 3000.00
0002 - 2005-01-04 - Position Size:     78 - Value 999854.14
0002 - 2005-01-04 - data value 2853.24
0002 - 2005-01-04 - Order Target Value: 4000.00
0003 - 2005-01-05 - Position Size:     109 - Value 999801.68
0003 - 2005-01-05 - data value 3938.17
0003 - 2005-01-05 - Order Target Value: 5000.00
0004 - 2005-01-06 - Position Size:     138 - Value 999699.57
...
0020 - 2005-01-31 - Position Size:     808 - Value 999206.37
0020 - 2005-01-31 - data value 28449.68
0020 - 2005-01-31 - Order Target Value: 31000.00
0021 - 2005-02-01 - Position Size:     880 - Value 998807.33
0021 - 2005-02-01 - data value 30580.00
0021 - 2005-02-01 - Order Target Value: 30000.00
0022 - 2005-02-02 - Position Size:     864 - Value 999510.21
0022 - 2005-02-02 - data value 30706.56
0022 - 2005-02-02 - Order Target Value: 29000.00
0023 - 2005-02-03 - Position Size:     816 - Value 999130.05
0023 - 2005-02-03 - data value 28633.44
0023 - 2005-02-03 - Order Target Value: 28000.00
...

有一行额外的信息告诉实际数据值(在投资组合中)是什么。这有助于确定是否已达到目标值

初始目标是3000.0,报告的初始值是2853.24。这里的问题是这是否足够接近。答案是是的

  • 示例在每日 K 线图结束时使用Market订单和最后可用价格来计算满足目标价值目标大小

  • 执行随后使用下一天的open价格,这不太可能是前一天的close

以任何其他方式做都意味着一个人在欺骗自己。

下一个目标值最终值要接近得多:40003938.17

当转换为二月时,目标值31000减少到3000029000。数据值也是如此,从30580.0030706.56,然后到28633.44。等等:

  • 30580 -> 30706.56是一个正变化。

    确实。在这种情况下,计算出的目标值大小遇到了一个将值提高到30706.56开盘价

如何避免这种影响:

  • 示例在订单中使用Market类型的执行,这种效果无法避免。

  • 方法order_target_xxx允许指定执行类型价格

    一个可以指定Limit作为执行订单的方式,并让价格成为收盘价格(如果没有提供其他内容,则由该方法选择)或者甚至提供具体定价。

图片

order_target_percent

在这种情况下,它只是当前投资组合价值的百分比。

$ ./order_target.py --target-percent --plot
0001 - 2005-01-03 - Position Size:     00 - Value 1000000.00
0001 - 2005-01-03 - data percent 0.00
0001 - 2005-01-03 - Order Target Percent: 0.03
0002 - 2005-01-04 - Position Size:     785 - Value 998532.05
0002 - 2005-01-04 - data percent 0.03
0002 - 2005-01-04 - Order Target Percent: 0.04
0003 - 2005-01-05 - Position Size:     1091 - Value 998007.44
0003 - 2005-01-05 - data percent 0.04
0003 - 2005-01-05 - Order Target Percent: 0.05
0004 - 2005-01-06 - Position Size:     1381 - Value 996985.64
...
0020 - 2005-01-31 - Position Size:     7985 - Value 991966.28
0020 - 2005-01-31 - data percent 0.28
0020 - 2005-01-31 - Order Target Percent: 0.31
0021 - 2005-02-01 - Position Size:     8733 - Value 988008.94
0021 - 2005-02-01 - data percent 0.31
0021 - 2005-02-01 - Order Target Percent: 0.30
0022 - 2005-02-02 - Position Size:     8530 - Value 995005.45
0022 - 2005-02-02 - data percent 0.30
0022 - 2005-02-02 - Order Target Percent: 0.29
0023 - 2005-02-03 - Position Size:     8120 - Value 991240.75
0023 - 2005-02-03 - data percent 0.29
0023 - 2005-02-03 - Order Target Percent: 0.28
...

数据信息已更改,以查看投资组合中数据所代表的%

图片

示例用法

$ ./order_target.py --help
usage: order_target.py [-h] [--data DATA] [--fromdate FROMDATE]
                       [--todate TODATE] [--cash CASH]
                       (--target-size | --target-value | --target-percent)
                       [--plot [kwargs]]

Sample for Order Target

optional arguments:
  -h, --help            show this help message and exit
  --data DATA           Specific data to be read in (default:
                        ../../datas/yhoo-1996-2015.txt)
  --fromdate FROMDATE   Starting date in YYYY-MM-DD format (default:
                        2005-01-01)
  --todate TODATE       Ending date in YYYY-MM-DD format (default: 2006-12-31)
  --cash CASH           Ending date in YYYY-MM-DD format (default: 1000000)
  --target-size         Use order_target_size (default: False)
  --target-value        Use order_target_value (default: False)
  --target-percent      Use order_target_percent (default: False)
  --plot [kwargs], -p [kwargs]
                        Plot the read data applying any kwargs passed For
                        example: --plot style="candle" (to plot candles)
                        (default: None)

示例代码

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

import argparse
from datetime import datetime

import backtrader as bt

class TheStrategy(bt.Strategy):
  '''
 This strategy is loosely based on some of the examples from the Van
 K. Tharp book: *Trade Your Way To Financial Freedom*. The logic:

 - Enter the market if:
 - The MACD.macd line crosses the MACD.signal line to the upside
 - The Simple Moving Average has a negative direction in the last x
 periods (actual value below value x periods ago)

 - Set a stop price x times the ATR value away from the close

 - If in the market:

 - Check if the current close has gone below the stop price. If yes,
 exit.
 - If not, update the stop price if the new stop price would be higher
 than the current
 '''

    params = (
        ('use_target_size', False),
        ('use_target_value', False),
        ('use_target_percent', False),
    )

    def notify_order(self, order):
        if order.status == order.Completed:
            pass

        if not order.alive():
            self.order = None  # indicate no order is pending

    def start(self):
        self.order = None  # sentinel to avoid operrations on pending order

    def next(self):
        dt = self.data.datetime.date()

        portfolio_value = self.broker.get_value()
        print('%04d - %s - Position Size: %02d - Value %.2f' %
              (len(self), dt.isoformat(), self.position.size, portfolio_value))

        data_value = self.broker.get_value([self.data])

        if self.p.use_target_value:
            print('%04d - %s - data value %.2f' %
                  (len(self), dt.isoformat(), data_value))

        elif self.p.use_target_percent:
            port_perc = data_value / portfolio_value
            print('%04d - %s - data percent %.2f' %
                  (len(self), dt.isoformat(), port_perc))

        if self.order:
            return  # pending order execution

        size = dt.day
        if (dt.month % 2) == 0:
            size = 31 - size

        if self.p.use_target_size:
            target = size
            print('%04d - %s - Order Target Size: %02d' %
                  (len(self), dt.isoformat(), size))

            self.order = self.order_target_size(target=size)

        elif self.p.use_target_value:
            value = size * 1000

            print('%04d - %s - Order Target Value: %.2f' %
                  (len(self), dt.isoformat(), value))

            self.order = self.order_target_value(target=value)

        elif self.p.use_target_percent:
            percent = size / 100.0

            print('%04d - %s - Order Target Percent: %.2f' %
                  (len(self), dt.isoformat(), percent))

            self.order = self.order_target_percent(target=percent)

def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()
    cerebro.broker.setcash(args.cash)

    dkwargs = dict()
    if args.fromdate is not None:
        dkwargs['fromdate'] = datetime.strptime(args.fromdate, '%Y-%m-%d')
    if args.todate is not None:
        dkwargs['todate'] = datetime.strptime(args.todate, '%Y-%m-%d')

    # data
    data = bt.feeds.YahooFinanceCSVData(dataname=args.data, **dkwargs)
    cerebro.adddata(data)

    # strategy
    cerebro.addstrategy(TheStrategy,
                        use_target_size=args.target_size,
                        use_target_value=args.target_value,
                        use_target_percent=args.target_percent)

    cerebro.run()

    if args.plot:
        pkwargs = dict(style='bar')
        if args.plot is not True:  # evals to True but is not True
            npkwargs = eval('dict(' + args.plot + ')')  # args were passed
            pkwargs.update(npkwargs)

        cerebro.plot(**pkwargs)

def parse_args(pargs=None):

    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Sample for Order Target')

    parser.add_argument('--data', required=False,
                        default='../../datas/yhoo-1996-2015.txt',
                        help='Specific data to be read in')

    parser.add_argument('--fromdate', required=False,
                        default='2005-01-01',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--todate', required=False,
                        default='2006-12-31',
                        help='Ending date in YYYY-MM-DD format')

    parser.add_argument('--cash', required=False, action='store',
                        type=float, default=1000000,
                        help='Ending date in YYYY-MM-DD format')

    pgroup = parser.add_mutually_exclusive_group(required=True)

    pgroup.add_argument('--target-size', required=False, action='store_true',
                        help=('Use order_target_size'))

    pgroup.add_argument('--target-value', required=False, action='store_true',
                        help=('Use order_target_value'))

    pgroup.add_argument('--target-percent', required=False,
                        action='store_true',
                        help=('Use order_target_percent'))

    # Plot options
    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const=True,
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example:\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))

    if pargs is not None:
        return parser.parse_args(pargs)

    return parser.parse_args()

if __name__ == '__main__':
    runstrat()

OCO 订单

原文:www.backtrader.com/docu/order-creation-execution/oco/oco/

版本1.9.34.116添加了OCO(也称为One Cancel Others)到回测工具中。

注意

这仅在回测中实现,尚未为实时经纪人实现

注意

更新至版本1.9.36.116。交互经纪人支持StopTrailStopTrailLimitOCO

  • OCO总是将组中的第 1 个订单指定为参数oco

  • StopTrailLimit: 经纪人模拟和IB经纪人具有相同的行为。指定:price作为初始止损触发价格(还要指定trailamount),然后plimi作为初始限价。两者之间的差异将确定limitoffset(限价与止损触发价格之间的距离)

使用模式尽量保持用户友好。因此,如果策略中的逻辑决定发出订单,使用OCO可以这样做:

def next(self):
    ...
    o1 = self.buy(...)
    ...
    o2 = self.buy(..., oco=o1)
    ...
    o3 = self.buy(..., oco=o1)  # or even oco=o2, o2 is already in o1 group

简单。第 1 个订单o1将成为组长。o2o3通过指定o1oco命名参数成为OCO 组的一部分。请注意代码片段中的注释指出,o3也可以通过指定o2(已经是组的一部分)成为组的一部分

组成后将发生以下情况:

  • 如果组中的任何订单被执行、取消或到期,其他订单将被取消

下面的示例展示了OCO概念的运用。一个带有绘图的标准执行:

$ ./oco.py --broker cash=50000 --plot

注意

现金增加到50000,因为资产达到4000的值,3 个1个项目的订单至少需要12000货币单位(经纪人的默认值为10000

使用以下图表。

image

实际上并没有提供太多信息(这是一个标准的SMA Crossover策略)。示例执行以下操作:

  • 当快速SMA向上穿越慢速SMA时,将发出 3 个订单

  • order1是一个Limit订单,将在limdays天(策略的参数)内到期,限价为close价格减少的百分比

  • order2是一个Limit订单,具有更长的到期时间和更低的限价。

  • order3是一个Limit订单,进一步降低了限价

因此,order2order3的执行不会发生,因为:

  • order1将首先执行,这应该触发其他订单的取消

  • order1将到期,这将触发其他订单的取消

系统保留 3 个订单的ref标识符,并且只有在notify_order中看到这三个ref标识符为CompletedCancelledMarginExpired时才会发出新的buy订单

退出只需在持有头寸一段时间后进行。

为了尝试跟踪实际执行情况,会产生文本输出。其中一些内容:

2005-01-28: Oref 1 / Buy at 2941.11055
2005-01-28: Oref 2 / Buy at 2896.7722
2005-01-28: Oref 3 / Buy at 2822.87495
2005-01-31: Order ref: 1 / Type Buy / Status Submitted
2005-01-31: Order ref: 2 / Type Buy / Status Submitted
2005-01-31: Order ref: 3 / Type Buy / Status Submitted
2005-01-31: Order ref: 1 / Type Buy / Status Accepted
2005-01-31: Order ref: 2 / Type Buy / Status Accepted
2005-01-31: Order ref: 3 / Type Buy / Status Accepted
2005-02-01: Order ref: 1 / Type Buy / Status Expired
2005-02-01: Order ref: 3 / Type Buy / Status Canceled
2005-02-01: Order ref: 2 / Type Buy / Status Canceled
...
2006-06-23: Oref 49 / Buy at 3532.39925
2006-06-23: Oref 50 / Buy at 3479.147
2006-06-23: Oref 51 / Buy at 3390.39325
2006-06-26: Order ref: 49 / Type Buy / Status Submitted
2006-06-26: Order ref: 50 / Type Buy / Status Submitted
2006-06-26: Order ref: 51 / Type Buy / Status Submitted
2006-06-26: Order ref: 49 / Type Buy / Status Accepted
2006-06-26: Order ref: 50 / Type Buy / Status Accepted
2006-06-26: Order ref: 51 / Type Buy / Status Accepted
2006-06-26: Order ref: 49 / Type Buy / Status Completed
2006-06-26: Order ref: 51 / Type Buy / Status Canceled
2006-06-26: Order ref: 50 / Type Buy / Status Canceled
...
2006-11-10: Order ref: 61 / Type Buy / Status Canceled
2006-12-11: Oref 63 / Buy at 4032.62555
2006-12-11: Oref 64 / Buy at 3971.8322
2006-12-11: Oref 65 / Buy at 3870.50995
2006-12-12: Order ref: 63 / Type Buy / Status Submitted
2006-12-12: Order ref: 64 / Type Buy / Status Submitted
2006-12-12: Order ref: 65 / Type Buy / Status Submitted
2006-12-12: Order ref: 63 / Type Buy / Status Accepted
2006-12-12: Order ref: 64 / Type Buy / Status Accepted
2006-12-12: Order ref: 65 / Type Buy / Status Accepted
2006-12-15: Order ref: 63 / Type Buy / Status Expired
2006-12-15: Order ref: 65 / Type Buy / Status Canceled
2006-12-15: Order ref: 64 / Type Buy / Status Canceled

出现了以下情况:

  • 第 1 个订单批次被下发。订单 1 到期,而 2 和 3 被取消。正如预期的那样。

  • 几个月后,又下发了另一批 3 个订单。在这种情况下,订单 49 被标记为已完成,而 50 和 51 则立即被取消。

  • 最后一个批次与第 1 个批次完全相同

现在让我们来检查一下没有OCO时的行为:

$ ./oco.py --strat do_oco=False --broker cash=50000

2005-01-28: Oref 1 / Buy at 2941.11055
2005-01-28: Oref 2 / Buy at 2896.7722
2005-01-28: Oref 3 / Buy at 2822.87495
2005-01-31: Order ref: 1 / Type Buy / Status Submitted
2005-01-31: Order ref: 2 / Type Buy / Status Submitted
2005-01-31: Order ref: 3 / Type Buy / Status Submitted
2005-01-31: Order ref: 1 / Type Buy / Status Accepted
2005-01-31: Order ref: 2 / Type Buy / Status Accepted
2005-01-31: Order ref: 3 / Type Buy / Status Accepted
2005-02-01: Order ref: 1 / Type Buy / Status Expired

这就是全部,其实并不多(没有顺序执行,也不需要太多图表)

  • 订单批次被下发

  • 订单 1 到期了,但是因为策略已经设置了参数do_oco=False,订单 2 和 3 没有被纳入OCO

  • 因此,订单 2 和 3 并没有被取消,而且由于默认到期时间差为1000天后,根据样本数据(2 年的数据)它们永远不会到期。

  • 系统从未下发第 2 批订单。

使用示例

$ ./oco.py --help
usage: oco.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE]
              [--cerebro kwargs] [--broker kwargs] [--sizer kwargs]
              [--strat kwargs] [--plot [kwargs]]

Sample Skeleton

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --fromdate FROMDATE  Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --todate TODATE      Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --cerebro kwargs     kwargs in key=value format (default: )
  --broker kwargs      kwargs in key=value format (default: )
  --sizer kwargs       kwargs in key=value format (default: )
  --strat kwargs       kwargs in key=value format (default: )
  --plot [kwargs]      kwargs in key=value format (default: )

代码示例

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

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        ma=bt.ind.SMA,
        p1=5,
        p2=15,
        limit=0.005,
        limdays=3,
        limdays2=1000,
        hold=10,
        switchp1p2=False,  # switch prices of order1 and order2
        oco1oco2=False,  # False - use order1 as oco for order3, else order2
        do_oco=True,  # use oco or not
    )

    def notify_order(self, order):
        print('{}: Order ref: {} / Type {} / Status {}'.format(
            self.data.datetime.date(0),
            order.ref, 'Buy' * order.isbuy() or 'Sell',
            order.getstatusname()))

        if order.status == order.Completed:
            self.holdstart = len(self)

        if not order.alive() and order.ref in self.orefs:
            self.orefs.remove(order.ref)

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.cross = bt.ind.CrossOver(ma1, ma2)

        self.orefs = list()

    def next(self):
        if self.orefs:
            return  # pending orders do nothing

        if not self.position:
            if self.cross > 0.0:  # crossing up

                p1 = self.data.close[0] * (1.0 - self.p.limit)
                p2 = self.data.close[0] * (1.0 - 2 * 2 * self.p.limit)
                p3 = self.data.close[0] * (1.0 - 3 * 3 * self.p.limit)

                if self.p.switchp1p2:
                    p1, p2 = p2, p1

                o1 = self.buy(exectype=bt.Order.Limit, price=p1,
                              valid=datetime.timedelta(self.p.limdays))

                print('{}: Oref {} / Buy at {}'.format(
                    self.datetime.date(), o1.ref, p1))

                oco2 = o1 if self.p.do_oco else None
                o2 = self.buy(exectype=bt.Order.Limit, price=p2,
                              valid=datetime.timedelta(self.p.limdays2),
                              oco=oco2)

                print('{}: Oref {} / Buy at {}'.format(
                    self.datetime.date(), o2.ref, p2))

                if self.p.do_oco:
                    oco3 = o1 if not self.p.oco1oco2 else oco2
                else:
                    oco3 = None

                o3 = self.buy(exectype=bt.Order.Limit, price=p3,
                              valid=datetime.timedelta(self.p.limdays2),
                              oco=oco3)

                print('{}: Oref {} / Buy at {}'.format(
                    self.datetime.date(), o3.ref, p3))

                self.orefs = [o1.ref, o2.ref, o3.ref]

        else:  # in the market
            if (len(self) - self.holdstart) >= self.p.hold:
                self.close()

def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))

def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'Sample Skeleton'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat()

括号订单

原文:www.backtrader.com/docu/order-creation-execution/bracket/bracket/

发布 1.9.37.116 添加了 bracket 订单,提供了由回测经纪人支持的非常广泛的订单范围(MarketLimitCloseStopStopLimitStopTrailStopTrailLimitOCO

注意

这是为了 回测交互式经纪人 存储而实现的

bracket 订单不是单个订单,而实际上是由 3 个订单组成的。让我们考虑长侧

  • 一个主要的 buy 订单,通常设置为 LimitStopLimit 订单

  • 一个低侧 sell 订单,通常设置为 Stop 订单以限制损失

  • 高侧 sell 订单,通常设置为 Limit 订单以获利

对应的 sell 和 2 x buy 订单用于短侧。

低/高侧订单实际上会围绕主要侧订单创建一个括号。

为了加入一些逻辑,以下规则适用:

  • 3 个订单一起提交,以避免它们中的任何一个独立触发

  • 低/高侧订单被标记为主要侧的子订单

  • 子订单在主要侧执行前不活动

  • 取消主要侧会同时取消低侧和高侧

  • 主要侧的执行会激活低侧和高侧

  • 一旦激活

    • 任何一侧订单的执行或取消都会自动取消另一侧订单

使用模式

创建括号订单集的两种可能性

  • 单独发出 3 个订单

  • 手动发出 3 个订单

单独发出括号

backtraderStrategy 中提供了两种控制 bracket 订单的新方法。

  • buy_bracketsell_bracket

注意

签名和信息如下或在 Strategy 参考部分中。

通过一个单一语句完成 3 个订单的完整集合。例如:

brackets = self.buy_bracket(limitprice=14.00, price=13.50, stopprice=13.00)

注意 stoppricelimitprice 如何围绕主要的 price 设置。

这应该足够了。实际目标 data 将是 data0,而 size 将由默认大小器自动确定。当然,可以指定许多其他参数以对执行进行精细控制。

返回值为:

  • 一个包含这 3 个订单的列表,顺序如下:[main, stop, limit]

因为在发出 sell_bracket 订单时,低侧和高侧会被调转,参数按照约定命名为 stoplimit

  • stop 旨在停止损失(长操作的低侧和短操作的高侧)

  • limit 旨在获取利润(长操作的高侧和短操作的低侧)

手动发出括号

这涉及生成 3 个订单并玩弄 transmitparent 参数。规则如下:

  • 主要侧订单必须首先创建并具有 transmit=False

  • 低/高侧订单必须具有 parent=main_side_order

  • 要创建的第 1 个低/高侧订单必须具有 transmit=False

  • 最后创建的订单(无论是低端还是高端)设置transmit=True

执行上面单个命令的实际示例:

mainside = self.buy(price=13.50, exectype=bt.Order.Limit, transmit=False)
lowside  = self.sell(price=13.00, size=mainside.size, exectype=bt.Order.Stop,
                     transmit=False, parent=mainside)
highside = self.sell(price=14.00, size=mainside.size, exectype=bt.Order.Limit,
                     transmit=True, parent=mainside)

还有很多工作要做的地方:

  • 跟踪mainside订单以指示它是其他订单的父订单

  • 控制transmit以确保只有最后一个订单触发联合传输

  • 指定执行类型

  • 为低端和高端指定size

    因为size必须相同。如果未手动指定参数并且最终用户已引入调整器,则调整器实际上可以指示订单的不同值。这就是为什么在为mainside订单设置后必须手动将其添加到调用中的原因。

其中的一个样本

从下面的示例运行产生了这个输出(为简洁起见截断)

$ ./bracket.py --plot

2005-01-28: Oref 1 / Buy at 2941.11055
2005-01-28: Oref 2 / Sell Stop at 2881.99275
2005-01-28: Oref 3 / Sell Limit at 3000.22835
2005-01-31: Order ref: 1 / Type Buy / Status Submitted
2005-01-31: Order ref: 2 / Type Sell / Status Submitted
2005-01-31: Order ref: 3 / Type Sell / Status Submitted
2005-01-31: Order ref: 1 / Type Buy / Status Accepted
2005-01-31: Order ref: 2 / Type Sell / Status Accepted
2005-01-31: Order ref: 3 / Type Sell / Status Accepted
2005-02-01: Order ref: 1 / Type Buy / Status Expired
2005-02-01: Order ref: 2 / Type Sell / Status Canceled
2005-02-01: Order ref: 3 / Type Sell / Status Canceled
...
2005-08-11: Oref 16 / Buy at 3337.3892
2005-08-11: Oref 17 / Sell Stop at 3270.306
2005-08-11: Oref 18 / Sell Limit at 3404.4724
2005-08-12: Order ref: 16 / Type Buy / Status Submitted
2005-08-12: Order ref: 17 / Type Sell / Status Submitted
2005-08-12: Order ref: 18 / Type Sell / Status Submitted
2005-08-12: Order ref: 16 / Type Buy / Status Accepted
2005-08-12: Order ref: 17 / Type Sell / Status Accepted
2005-08-12: Order ref: 18 / Type Sell / Status Accepted
2005-08-12: Order ref: 16 / Type Buy / Status Completed
2005-08-18: Order ref: 17 / Type Sell / Status Completed
2005-08-18: Order ref: 18 / Type Sell / Status Canceled
...
2005-09-26: Oref 22 / Buy at 3383.92535
2005-09-26: Oref 23 / Sell Stop at 3315.90675
2005-09-26: Oref 24 / Sell Limit at 3451.94395
2005-09-27: Order ref: 22 / Type Buy / Status Submitted
2005-09-27: Order ref: 23 / Type Sell / Status Submitted
2005-09-27: Order ref: 24 / Type Sell / Status Submitted
2005-09-27: Order ref: 22 / Type Buy / Status Accepted
2005-09-27: Order ref: 23 / Type Sell / Status Accepted
2005-09-27: Order ref: 24 / Type Sell / Status Accepted
2005-09-27: Order ref: 22 / Type Buy / Status Completed
2005-10-04: Order ref: 24 / Type Sell / Status Completed
2005-10-04: Order ref: 23 / Type Sell / Status Canceled
...

显示了 3 种不同的结果:

  • 在第一种情况下,主要的副作用已经过期,这自动取消了其他两个

  • 在第二种情况下,主要的副作用已经完成,低(在购买情况下停止)被执行以限制损失

  • 在第三种情况下,主要的副作用已经完成,而高侧(限制)被执行

    这可以注意到,因为已完成的 ids 是2224端订单最后被发出,这意味着未执行的低端订单的 id 为 23。

视觉上

image

可以立即看到,失败的交易围绕着相同的值以及成功的交易,这就是背书的目的。控制两侧。

正在运行的示例手动发出了 3 个订单,但可以告诉它使用buy_bracket。让我们看看输出:

$ ./bracket.py --strat usebracket=True

具有相同结果

image

一些参考资料

查看新的buy_bracketsell_bracket方法

def buy_bracket(self, data=None, size=None, price=None, plimit=None,
                exectype=bt.Order.Limit, valid=None, tradeid=0,
                trailamount=None, trailpercent=None, oargs={},
                stopprice=None, stopexec=bt.Order.Stop, stopargs={},
                limitprice=None, limitexec=bt.Order.Limit, limitargs={},
                **kwargs):
  '''
 Create a bracket order group (low side - buy order - high side). The
 default behavior is as follows:

 - Issue a **buy** order with execution ``Limit`

 - Issue a *low side* bracket **sell** order with execution ``Stop``

 - Issue a *high side* bracket **sell** order with execution
 ``Limit``.

 See below for the different parameters

 - ``data`` (default: ``None``)

 For which data the order has to be created. If ``None`` then the
 first data in the system, ``self.datas[0] or self.data0`` (aka
 ``self.data``) will be used

 - ``size`` (default: ``None``)

 Size to use (positive) of units of data to use for the order.

 If ``None`` the ``sizer`` instance retrieved via ``getsizer`` will
 be used to determine the size.

 **Note**: The same size is applied to all 3 orders of the bracket

 - ``price`` (default: ``None``)

 Price to use (live brokers may place restrictions on the actual
 format if it does not comply to minimum tick size requirements)

 ``None`` is valid for ``Market`` and ``Close`` orders (the market
 determines the price)

 For ``Limit``, ``Stop`` and ``StopLimit`` orders this value
 determines the trigger point (in the case of ``Limit`` the trigger
 is obviously at which price the order should be matched)

 - ``plimit`` (default: ``None``)

 Only applicable to ``StopLimit`` orders. This is the price at which
 to set the implicit *Limit* order, once the *Stop* has been
 triggered (for which ``price`` has been used)

 - ``trailamount`` (default: ``None``)

 If the order type is StopTrail or StopTrailLimit, this is an
 absolute amount which determines the distance to the price (below
 for a Sell order and above for a buy order) to keep the trailing
 stop

 - ``trailpercent`` (default: ``None``)

 If the order type is StopTrail or StopTrailLimit, this is a
 percentage amount which determines the distance to the price (below
 for a Sell order and above for a buy order) to keep the trailing
 stop (if ``trailamount`` is also specified it will be used)

 - ``exectype`` (default: ``bt.Order.Limit``)

 Possible values: (see the documentation for the method ``buy``

 - ``valid`` (default: ``None``)

 Possible values: (see the documentation for the method ``buy``

 - ``tradeid`` (default: ``0``)

 Possible values: (see the documentation for the method ``buy``

 - ``oargs`` (default: ``{}``)

 Specific keyword arguments (in a ``dict``) to pass to the main side
 order. Arguments from the default ``**kwargs`` will be applied on
 top of this.

 - ``**kwargs``: additional broker implementations may support extra
 parameters. ``backtrader`` will pass the *kwargs* down to the
 created order objects

 Possible values: (see the documentation for the method ``buy``

 **Note**: this ``kwargs`` will be applied to the 3 orders of a
 bracket. See below for specific keyword arguments for the low and
 high side orders

 - ``stopprice`` (default: ``None``)

 Specific price for the *low side* stop order

 - ``stopexec`` (default: ``bt.Order.Stop``)

 Specific execution type for the *low side* order

 - ``stopargs`` (default: ``{}``)

 Specific keyword arguments (in a ``dict``) to pass to the low side
 order. Arguments from the default ``**kwargs`` will be applied on
 top of this.

 - ``limitprice`` (default: ``None``)

 Specific price for the *high side* stop order

 - ``stopexec`` (default: ``bt.Order.Limit``)

 Specific execution type for the *high side* order

 - ``limitargs`` (default: ``{}``)

 Specific keyword arguments (in a ``dict``) to pass to the high side
 order. Arguments from the default ``**kwargs`` will be applied on
 top of this.

 Returns:
 - A list containing the 3 bracket orders [order, stop side, limit
 side]
 '''

def sell_bracket(self, data=None,
                 size=None, price=None, plimit=None,
                 exectype=bt.Order.Limit, valid=None, tradeid=0,
                 trailamount=None, trailpercent=None,
                 oargs={},
                 stopprice=None, stopexec=bt.Order.Stop, stopargs={},
                 limitprice=None, limitexec=bt.Order.Limit, limitargs={},
                 **kwargs):
  '''
 Create a bracket order group (low side - buy order - high side). The
 default behavior is as follows:

 - Issue a **sell** order with execution ``Limit`

 - Issue a *high side* bracket **buy** order with execution ``Stop``

 - Issue a *low side* bracket **buy** order with execution ``Limit``.

 See ``bracket_buy`` for the meaning of the parameters

 Returns:
 - A list containing the 3 bracket orders [order, stop side, limit
 side]
 '''

样本用法

$ ./bracket.py --help
usage: bracket.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE]
                  [--cerebro kwargs] [--broker kwargs] [--sizer kwargs]
                  [--strat kwargs] [--plot [kwargs]]

Sample Skeleton

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --fromdate FROMDATE  Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --todate TODATE      Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --cerebro kwargs     kwargs in key=value format (default: )
  --broker kwargs      kwargs in key=value format (default: )
  --sizer kwargs       kwargs in key=value format (default: )
  --strat kwargs       kwargs in key=value format (default: )
  --plot [kwargs]      kwargs in key=value format (default: )

样本代码

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

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        ma=bt.ind.SMA,
        p1=5,
        p2=15,
        limit=0.005,
        limdays=3,
        limdays2=1000,
        hold=10,
        usebracket=False,  # use order_target_size
        switchp1p2=False,  # switch prices of order1 and order2
    )

    def notify_order(self, order):
        print('{}: Order ref: {} / Type {} / Status {}'.format(
            self.data.datetime.date(0),
            order.ref, 'Buy' * order.isbuy() or 'Sell',
            order.getstatusname()))

        if order.status == order.Completed:
            self.holdstart = len(self)

        if not order.alive() and order.ref in self.orefs:
            self.orefs.remove(order.ref)

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.cross = bt.ind.CrossOver(ma1, ma2)

        self.orefs = list()

        if self.p.usebracket:
            print('-' * 5, 'Using buy_bracket')

    def next(self):
        if self.orefs:
            return  # pending orders do nothing

        if not self.position:
            if self.cross > 0.0:  # crossing up

                close = self.data.close[0]
                p1 = close * (1.0 - self.p.limit)
                p2 = p1 - 0.02 * close
                p3 = p1 + 0.02 * close

                valid1 = datetime.timedelta(self.p.limdays)
                valid2 = valid3 = datetime.timedelta(self.p.limdays2)

                if self.p.switchp1p2:
                    p1, p2 = p2, p1
                    valid1, valid2 = valid2, valid1

                if not self.p.usebracket:
                    o1 = self.buy(exectype=bt.Order.Limit,
                                  price=p1,
                                  valid=valid1,
                                  transmit=False)

                    print('{}: Oref {} / Buy at {}'.format(
                        self.datetime.date(), o1.ref, p1))

                    o2 = self.sell(exectype=bt.Order.Stop,
                                   price=p2,
                                   valid=valid2,
                                   parent=o1,
                                   transmit=False)

                    print('{}: Oref {} / Sell Stop at {}'.format(
                        self.datetime.date(), o2.ref, p2))

                    o3 = self.sell(exectype=bt.Order.Limit,
                                   price=p3,
                                   valid=valid3,
                                   parent=o1,
                                   transmit=True)

                    print('{}: Oref {} / Sell Limit at {}'.format(
                        self.datetime.date(), o3.ref, p3))

                    self.orefs = [o1.ref, o2.ref, o3.ref]

                else:
                    os = self.buy_bracket(
                        price=p1, valid=valid1,
                        stopprice=p2, stopargs=dict(valid=valid2),
                        limitprice=p3, limitargs=dict(valid=valid3),)

                    self.orefs = [o.ref for o in os]

        else:  # in the market
            if (len(self) - self.holdstart) >= self.p.hold:
                pass  # do nothing in this case

def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))

def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'Sample Skeleton'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat()

标签:中文,59,BackTrader,self,00,Order,文档,2006,order
From: https://www.cnblogs.com/apachecn/p/18135480

相关文章

  • 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包含两种不同类型的对象,可以帮助进行跟踪:......
  • BackTrader 中文文档(十二)
    原文:www.backtrader.com/VisualChart原文:www.backtrader.com/docu/live/vc/vc/与VisualChart的集成支持两者:实时数据提供实时交易VisualChart是完整的交易解决方案:在单个平台上集成图表、数据源和经纪功能更多信息,请访问:www.visualchart.com需求Vi......
  • BackTrader 中文文档(十三)
    原文:www.backtrader.com/交易日历原文:www.backtrader.com/docu/tradingcalendar/tradingcalendar/发布1.9.42.116版本添加了对交易日历的支持。在例如以下情况下重采样时,这很有用:现在,从每日到每周的重采样可以将每周柱与周的最后一根柱一起交付。这是因为交易日历可以......
  • BackTrader 中文文档(十四)
    原文:www.backtrader.com/在backtrader中交易加密货币的分数大小原文:www.backtrader.com/blog/posts/2019-08-29-fractional-sizes/fractional-sizes/首先,让我们用两行总结一下backtrader的工作方式:就像一个基本构建块(Cerebro)的构建套件,可以将许多不同的部件插入其中......
  • BackTrader 中文文档(十五)
    原文:www.backtrader.com/动量策略原文:www.backtrader.com/blog/2019-05-20-momentum-strategy/momentum-strategy/在另一篇很棒的文章中,TeddyKoker再次展示了算法交易策略的发展路径:首先应用pandas进行研究使用backtrader进行回测真棒!!!文章可以在以下位置......
  • BackTrader 中文文档(二)
    原文:www.backtrader.com/概念平台概念原文:www.backtrader.com/docu/concepts/这是平台某些概念的集合。它试图收集可在使用平台时有用的信息片段。开始之前所有小代码示例都假设以下导入可用:importbacktraderasbtimportbacktrader.indicatorsasbtindimportbac......
  • BackTrader 中文文档(四)
    原文:www.backtrader.com/数据供稿-过滤器过滤器原文:www.backtrader.com/docu/filters/此功能是相对较晚添加到backtrader中的,并且必须适应已经存在的内部结构。这使得它不像希望的那样灵活和100%功能齐全,但在许多情况下仍然可以实现目的。尽管实现尝试允许即插即用的......
  • 一种融合指代消解序列标注方法在中文人名识别上的应用(下)
    二、使用了BERT模型和指代消解算法:加入BERT语言预处理模型,获取到高质量动态词向量。融入指代消解算法,根据指代词找出符合要求的子串/短语。【2】融入指代消解算法,根据指代词找出符合要求的子串/短语指代消解算法如图2所示,简单来说,就是考虑文档中子串/短语以及学习子......
  • 数字转中文(含小数点)
    numberToChinese(num){return(num)=>{varAA=newArray('零','一','二','三','四','五','六','七','八','九','十');var......