Backtrader中文筆記之Cheat On Open
這個感覺就是隻能用來,在當前的開盤前下單,用的當天的開盤價可以參考買入股份,訊號的處理,買入的時間,買入的價格,與正常的沒有任何區別
Release 1.9.44.116
adds support for Cheat-On-Open
. This seems to be a demanded feature for people who go all-in, having made a calculation after the close of a bar, but expecting to be matched against the open
price.
版本1.9.44.116增加了對Cheat-On-Open
Such a use case fails when the opening price gaps (up or down, depending on whether buy
or sell
is in effect) and the cash is not enough for an all-in operation. This forces the broker to reject the operation.
當開盤價出現缺口(上漲或下跌,取決於買入還是賣出)且現金不足以進行全面操作時,這樣的用例就失敗了。這將強制代理拒絕該操作。
And although people can try to look into the future with a positive [1]
index approach, this requires preloading data which is not always available.
儘管人們可以嘗試用積極的[1]指數方法來展望未來,但這需要預載入資料,而這並不總是可用的。
The pattern:
cerebro = bt.Cerebro(cheat_on_open=True)
This:
-
Activates an extra cycle in the system which calls the methods in the strategy
next_open
nextstart_open
andprenext_open
- 在系統中啟用一個額外的迴圈,該迴圈呼叫策略next_open、nextstart_open和prenext_open
The decision to have an additional family of methods has been made to make a clear separation between the regular methods which operate on the basis that the prices being examined are no longer available and the future is unknown and the operation in cheating mode.
- 決定增加一系列方法是為了明確區分常規方法和作弊模式下的操作方法,這些方法是基於被檢查的價格不再可用且未來未知。
-
This also avoids having 2 calls to the regular
next
method. - 這也避免了對常規next方法的兩次呼叫。
The following holds true when inside a xxx_open
method:
在xxx_open方法中,以下情況適用:
-
The indicators have not been recalculated and hold the values that were last seen during the previous cycle in the equivalent
xxx
regular methods - 這些指標尚未重新計算,並保留了上一個週期中採用等效xxx常規方法最後看到的數值
-
The broker has not yet evaluated the pending orders for the new cycle and new orders can be introduced which will be evaluated if possible.
- 【券商經紀人】尚未評估新週期的待定訂單,可以引入新訂單,如果可能,將對其進行評估。
Notice that:
-
Cerebro
also has abroker_coo
(default:True
) parameter which tells cerebro that ifcheat-on-open
has been activated, it shall try to activate it also in the broker if possible. Cerebro
也有一個broker_coo(預設值:True)引數,它告訴大腦,如果“欺騙開啟”被啟用,它也應儘可能在代理中啟用它。-
The simulation broker has a parameter named:
coo
and a method to set it namedset_coo
- 模擬券商【經紀人】有一個名為:coo的引數和一個設定它的方法set_coo
Trying cheat-on-open
The sample below has a strategy with 2 different behaviors:
下面的示例有兩種不同行為的策略:
-
If cheat-on-open is True, it will only operate from
next_open
- 如果cheat-on-open為True,它將只操作
next_open
-
If cheat-on-open is False, it will only operate from
next
- 如果cheat-on-open為False,它將只操作
next
In both cases the matching price must be the same
在這兩種情況下,匹配價格必須相同
-
If not cheating, the order is issued at the end of the previous day and will be matched with the next incoming price which is the
open
price - 如果不作弊,訂單將在前一天結束時發出,並將與下一個進入價格匹配,即開盤價。
-
If cheating, the order is issued on the same day it is executed. Because the order is issued before the broker has evaluated orders, it will also be matched with the next incoming price, the
open
price. - 如果作弊,命令在執行當天發出。因為訂單是在經紀人評估訂單之前發出的,它還將與下一個入市價格,即開盤價相匹配。
-
This second scenario, allows calculation of exact stakes for all-in strategies, because one can directly access the current
open
price. - 第二種情況下,可以計算所有策略的確切風險,因為可以直接獲得當前的開盤價。
In both cases
- The current
open
andclose
prices will be printed fromnext
. - 當前的開盤價和收盤價將從下一頁打印出來。
Regular execution:
$ ./cheat-on-open.py --cerebro cheat_on_open=False ... 2005-04-07 next, open 3073.4 close 3090.72 2005-04-08 next, open 3092.07 close 3088.92 Strat Len 68 2005-04-08 Send Buy, fromopen False, close 3088.92 2005-04-11 Buy Executed at price 3088.47 2005-04-11 next, open 3088.47 close 3080.6 2005-04-12 next, open 3080.42 close 3065.18 ...
The order:
-
Is issued on 2005-04-08 after the close
- 在2005-04-08收盤之後發出定單
-
It is executed on 2005-04-11 with the
open
price of3088.47
- 執行在2005-04-11的開盤價
3088.47
Cheating execution:
$ ./cheat-on-open.py --cerebro cheat_on_open=True ... 2005-04-07 next, open 3073.4 close 3090.72 2005-04-08 next, open 3092.07 close 3088.92 2005-04-11 Send Buy, fromopen True, close 3080.6 2005-04-11 Buy Executed at price 3088.47 2005-04-11 next, open 3088.47 close 3080.6 2005-04-12 next, open 3080.42 close 3065.18 ...
The order:
-
Is issued on 2005-04-11 before the open
- 在4月11日之前發出定單
-
It is executed on 2005-04-11 with the
open
price of3088.47
- 執行在4月11日的開盤價
And the overall result as seen on the chart is also the same.
圖上的結果也是一樣的
Conclusion
結論
Cheating on the open allows issuing orders before the open which can for example allow the exact calculation of stakes for all-in scenarios.
在公開市場上作弊允許在公開之前釋出命令,這可以允許在所有場景中精確計算購買股數【賭注】。
Sample usage
$ ./cheat-on-open.py --help usage: cheat-on-open.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE] [--cerebro kwargs] [--broker kwargs] [--sizer kwargs] [--strat kwargs] [--plot [kwargs]] Cheat-On-Open Sample 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: )
Sample source
from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import datetime import backtrader as bt class St(bt.Strategy): params = dict( periods=[10, 30], matype=bt.ind.SMA, ) def __init__(self): self.cheating = self.cerebro.p.cheat_on_open mas = [self.p.matype(period=x) for x in self.p.periods] self.signal = bt.ind.CrossOver(*mas) self.order = None def notify_order(self, order): if order.status != order.Completed: return self.order = None print('{} {} Executed at price {}'.format( bt.num2date(order.executed.dt).date(), 'Buy' * order.isbuy() or 'Sell', order.executed.price) ) def operate(self, fromopen): if self.order is not None: return if self.position: if self.signal < 0: self.order = self.close() elif self.signal > 0: print('{} Send Buy, fromopen {}, close {}'.format( self.data.datetime.date(), fromopen, self.data.close[0]) ) self.order = self.buy() def next(self): print('{} next, open {} close {}'.format( self.data.datetime.date(), self.data.open[0], self.data.close[0]) ) if self.cheating: return self.operate(fromopen=False) def next_open(self): if not self.cheating: return self.operate(fromopen=True) 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=( 'Cheat-On-Open Sample' ) ) 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()