Backtrader中文笔记之Order Management and Execution ---几种价格限制交易的详细解释

Order Management and Execution

订单管理与执行

Backtesting, and hence backtrader, would not be complete if orders could not be simulated. To do so, the following is available in the platform.

如果订单不能被模拟,那么回溯测试和回溯交易就不可能完成。为此,平台中提供了以下内容。

For order management 3 primitives:

对于订单管理3个原语:

  • buy

  • sell

  • cancel

Note

An update primitive is obviously something logic but common sense dictates that such a method is mostly used by manual operators working with a judgmental trading approach.

更新原语显然是逻辑上的东西,但常识表明,这种方法主要由从事判断性交易方法的手工操作人员使用。

For order execution logic the following execution types:

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

  • Market

  • Close

  • Limit

  • Stop

  • StopLimit

Order Management

订单管理

Some examples:

一些例子

# buy the main date, with sizer default stake, Market order
# 主日期买入,使用sizer默认的订单数量 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
# 设置激活价格peice,设置限制价格plimt 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)

Note

All order types can be create by creating an Order instance (or one of its subclasses) and then passed to to the broker with:

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

order = self.broker.submit(order)

 Note

There are buy and sell primitives in the broker itself, but they are less forgiving with regards to default parameters.

经纪人本身有buy和sell语句,但是对于默认参数,它们没那么宽容

Order Execution Logic

订单执行逻辑

The broker uses 2 main guidelines (assumptions?) for order execution.

经纪人使用两个主要准则(假设?)执行定单

    • The current data has already happened and cannot be used to execcute an order.

    • 当前数据已发生,无法用于执行订单。
    • If the logic in the strategy is something like:

    • 如果策略的逻辑像下面所展示的
    • if self.data.close > self.sma:  # where sma is a Simple Moving Average
           self.buy()
      
  • The expectation CANNOT be that the order will be executed with the close price which is being examined in the logic BECAUSE it has already happened.

  • 当在检查逻辑的时候,我们不能期望订单将以收盘价执行,因为它已经发生了。
  • The order CAN BE 1st EXECUTED withing the bounds of the next set of Open/High/Low/Close price points (and the conditions set forth herein by the order)

  • 该指令可首先在下一组Open/High/Low/Close价区间内执行(以及该指令所规定的条件)。
  • Volume does not play a role

  • 成交量并不起作用
  • It actually does in real trading if the trader goes for non-liquid assets or precisely the extremes (high/low) of a price bar are hit.

  • 在实际交易中,如果交易员购买非流动性资产,或者确切地说,价格界限的极端(高/低)受到冲击,这种情况就会发生。
  • But hitting the high/low points is a seldom occurrence (if you do … you don’t need backtrader) and the chosen assets will have enough liquidity to absorb the orders of any regular trading

  但是触高/触低是很少发生的(如果你触高/触低,你就不需要backtrader),所选资产将有足够的流动性来吸收任何常规交易的订单

Market

市场价

Execution:

执行

  • Opening price of the next set of Open/High/Low/Close prices (commonly referred as bar)
  • 设定为下一个bar的开盘价

Rationale:

解释

  • If the logic has executed at point X in time and issued a Market order, the next price spot that will happen is the upcoming open price

  如果逻辑已经在X点及时执行并发布了市场指令,下一个将发生的价格点是即将到来的开盘价

Note

This order executes always and disregards any price and valid parameters used to create it

此订单始终执行,并忽略用于创建它的任何priceprice参数

Close

收盘价

Execution:

执行

  • Using the close price of the next bar when the next bar actually CLOSES
  • 当下一个bar真实的关闭了,使用下一个bar的收盘价

Rationale:

解释:

  • Most backtesting feeds contain already closed bars and the order will execute immediately with the close price of the next bar. A daily data feed is the most common example.

  • 大多数回测的数据源包含已经关闭的条,订单将立即与下一个条的收盘价一起执行。最常见的例子是日线的数据输入。
  • But the system could be fed with “tick” prices and the actual bar (time/date wise) is being udpated constantly with the new ticks, without actually moving to the next bar (because time and/or date have not changed)

  • 但是系统可以输入“tick”价格,并且实际的bar(时间/日期方面)会不断地与新的滴答图关联,而不会实际移动到下一个bar(因为时间和/或日期没有改变)
  • Only when the time or date changes, the bar has actually been closed and the order gets executed

  • 只有当时间或日期更改时,bar才会实际关闭,定单才会执行。

感觉就是用在tick数据上面的

Limit

限制

Execution:

执行

  • The price set at order creation if the data touches it, starting with the next price bar.

  • 价格在定单创建的时候设置,如果数据触及到,将在下一个价格栏开始。
  • The order will be canceled if valid is set and the time point is reached

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

Price Matching:

价格匹配

  • backtrader tries to provide most realistic execution price for Limit orders.

  • 对于限制定单backtrader试图提供最切合实际的执行价格
  • Using the 4 price spots (Open/High/Low/Close) it can be partially inferred if the requested price can be improved.

  • 利用4个价格点(开盘/高/低/收盘),可以部分推断所要求的价格是否可以优化
  • For Buy Orders

  • 买单介绍
    • Case 1:

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

    • 如果一个bar的开盘价低于限制价格,定单将立即与开盘价执行。定单在会话的开始阶段就已经开始检查
    • Case 2:

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

    • 如果开盘价高于限制价格,但是最低价低于限制价格,则在交易期间已看到限价,定单便可执行。

    The logic is obviously inverted for Sell orders.

  • 显然,卖出指令的逻辑是相反的。

Stop

用与止损的设置

Execution:

执行

  • The trigger price set at order creation if the data touches it, starting with the next price bar.

  • 触发价格在定单创建时设置。如果数据触碰到,将在下一个价格条开始
  • The order will be canceled if valid is set and the time point is reached

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

Price Matching:

价格匹配

  • backtrader tries to provide most realistic trigger price for Stop orders.

  • backtrader试图为止损单提供最现实的触发价格。
  • Using the 4 price spots (Open/High/Low/Close) it can be partially inferred if the requested price can be improved.

  • 利用4个价格点(开盘/高/低/收盘),可以部分推断所要求的价格是否可以优化
  • For Stoporders whichBuy`

  • 还是买的例子
    • Case 1:

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

    • 如果开盘价大于止损价,定单将在开盘价理解执行。
    • Intended to stop a loss if the price is moving upwards against an existing short position

    • 当价格相对与现有的空头位置向上移动时,用来止损。
    • Case 2:

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

    • 如果开盘价低于止损价,但是最高价高于止损价,则在交易期间已看到限价,定单便可执行。

    The logic is obviously inverted for Stop orders which Sell.

  • 显然,卖出指令的逻辑是相反的。

StopLimit

Execution:

执行

  • The trigger price sets the order in motion starting with the next price bar.
  • 触发价格在定单创建时设置。在下一个价格条开始

Price Matching:

  • Trigger: Uses the Stop matching logic (but only triggers and turns the order into a Limit order)

  • 触发器:使用停止(止损)逻辑(但仅触发并将订单转换为限制订单)
  • Limit: Uses the Limit price matching logic

  • 限制:使用限制价格逻辑

Some samples

As always pictures (with code) are worth several million long explanations. Please note that the snippets concentrate on the order creation part. The full code is at the bottom.

一如既往,图片(带有代码)有着很好的解释。请注意,这些片段集中在订单创建部分。完整的代码在底部。

A price closes above/below a simple moving average strategy will be used for the generation of the buy/sell signals

收盘价格在一个简单的移动平均线之上/之下将被用来产生买/卖信号

The signal is seen at the bottom of the charts: the CrossOver using the crossover indicator.

信号是在图表的底部看到的:交叉使用交叉指示器。

A reference to generated “buy” orders will be kept to only allow one simultaneous order at most in the system.

对生成的“购买”订单的引用将保持在系统中最多只允许一个同时订单。

Execution Type: Market

执行 类型:Market

See in the chart how how the orders are executed one bar after the signal is generated with the opening price.

在图表中看到,在开盘价格产生信号后,指令是如何执行的。

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

 The output chart.

 The command line and output:

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

Execution Type: Close

执行 类型:close

Now the orders are also executed one bar after the signal but with the closing price.

现在订单也在信号发出一个bar后执行,但以收盘价为准。

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

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

 The output chart.

 The command line and output:

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

Execution Type: Limit

Validity is being calculated some lines before in case it has been passed as argument.

有效性是正在计算一些行的前提,他需要作为参数传递。没设置的话,就是永久有效

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

 A limit price 1% below the signal generation price (the close at the signal bar) is set. Notice how this prevents many from the orders above from being executed.

设定低于信号生成价格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)

 The output chart.

 Just 4 orders have been issued. Limiting the price trying to catch a small dip has completly changed the output.

只下了4个订单。为了抓住小幅下跌而限制价格已经完全改变了产量。

The command line and output:

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

Execution Type: Limit with validity

执行 类型:Limit with validity

To not wait forever on a limit order which may only execute when the price is moving against the “buy” order, the order will only be valid 4 (calendar) days.

如果限价指令只能在价格与“买入”指令满足的情况下执行,为了避免永远等待,该指令的有效期仅为4(日历)天。

The output chart.

 More orders have been generated, but all but one “buy” order expired, further limiting the amount of operations.

更多的订单已经产生,但除了一个“购买”订单到期,进一步限制了操作量。

The command line and output:

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

Execution Type: Stop

执行 类型:Stop

A stop price 1% above the signal price is set. That means that the strategy only buys if the signal is generated and the price continues climbing up, which could be intrepreted as a signal of strength.

设定高于信号价格1%的止损价。这意味着,只有在信号产生且价格继续攀升的情况下,该策略才会买入,这可能是一种强势信号。

This completely alters the execution panorama.

 这完全改变了执行的全景。

            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)

 The output chart.

 The command line and output:

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

Execution Type: StopLimit

执行 类型:StopLimit

A stop price 1% above the signal price is set. But the limit price is set 0.5% above the signal (close) price which could be interpreted as: wait for the strength to show up but do not buy the peak. Wait for a dip.

设定高于信号价格1%的止损价。但限价设定在信号(收盘价)之上0.5%,这可以解释为:等待力量显现,但不买高峰。等一等。

其实更好的我的理解,就是price为激活价格,达到这个价格,我准备买了,然后买的价格是限定的价格,要不然会追高

Validity is capped at 20 (calendar) days

有效期为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))

 The output chart.

 The command line and output:

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

Test Script Execution

Detailed in the command line 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

 The full code

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-


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 == order.Submitted:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            self.log('ORDER SUBMITTED', dt=order.created.dt)
            self.order = order
            return

        if order.status == order.Accepted:
            self.log('ORDER ACCEPTED', 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))
                self.log(f'BUY DATA INFO HIGH:{self.data.high[0]};'
                         f'CLOSE:{self.data.close[0]}'
                         f'')

            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
            # print(valid, 'valid')

            # 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:
        todate = 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()

 代码为我自己做了轻微调整输出后的代码

原文地址:https://www.cnblogs.com/sidianok/p/13669507.html