Movatterモバイル変換


[0]ホーム

URL:


Skip to content

OCO orders

Release1.9.34.116 addsOCO (akaOne Cancel Others) to thebacktesting arsenal.

Note

This is only implemented in backtesting and there isn’t yet animplementation for live brokers

Note

Updated with release1.9.36.116. Interactive Brokers support forStopTrail,StopTrailLimit andOCO.

  • OCO Specify always the 1st order in a group as parameteroco

  • StopTrailLimit: the broker simulation and theIB broker have the asme behavior. Specify:price as the initial stop trigger price (specify alsotrailamount) and thenplimi as the initial limit price. The difference between the two will determine thelimitoffset (the distance at which the limit price remains from the stop trigger price)

The usage pattern tries to remain user friendly. As such and if the logic inthe strategy has decided it is the moment to issue orders, usingOCO can bedone like this:

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

Easy. The 1st ordero1 will something like the group leader.o2 ando3 become part of theOCO Group by specifyingo1 with theoconamed argument. See that the comment in the snippet indicates thato3could have also become part of the group by specifyingo2 (which as alreadypart of the group)

With the group formed the following will happen:

  • If any order in the group is executed, cancelled or expires, the other orders will be cancelled

The sample below puts theOCO concept in play. A standard execution with a plot:

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

Note

cash is increased to50000, because the asset reaches values of4000 and 3 orders of1 item would require at least12000monetary units (the default in the broker is10000)

With the following chart.

image

which actually doesn’t provide much information (it is a standardSMACrossover strategy). The sample does the following:

  • When the fastSMA crosses the slowSMA to the upside 3 orders are issued

  • order1 is aLimit order which will expire inlimdays days (parameter to the strategy) with theclose price reduced by a percentage as the limit price

  • order2 is aLimit order with a much longer period to expire and a much more reduced limit price.

  • order3 is aLimit order which further reduces the limit price

As such the execution oforder2 andorder3 is not going to happenbecause:

  • order1 will be executed first and this should trigger the cancellation of the others

or

  • order1 will expire and this will trigger the the cancellation of the others

The system keeps theref identifier of the 3 orders and will only issue newbuy orders if the threeref identifiers are seen innotify_order aseitherCompleted,Cancelled,Margin orExpired

Exiting is simply done after holding the position for some bars.

To try to keep track of the actual execution, textual output is produced. Someof it:

2005-01-28: Oref 1 / Buy at 2941.110552005-01-28: Oref 2 / Buy at 2896.77222005-01-28: Oref 3 / Buy at 2822.874952005-01-31: Order ref: 1 / Type Buy / Status Submitted2005-01-31: Order ref: 2 / Type Buy / Status Submitted2005-01-31: Order ref: 3 / Type Buy / Status Submitted2005-01-31: Order ref: 1 / Type Buy / Status Accepted2005-01-31: Order ref: 2 / Type Buy / Status Accepted2005-01-31: Order ref: 3 / Type Buy / Status Accepted2005-02-01: Order ref: 1 / Type Buy / Status Expired2005-02-01: Order ref: 3 / Type Buy / Status Canceled2005-02-01: Order ref: 2 / Type Buy / Status Canceled...2006-06-23: Oref 49 / Buy at 3532.399252006-06-23: Oref 50 / Buy at 3479.1472006-06-23: Oref 51 / Buy at 3390.393252006-06-26: Order ref: 49 / Type Buy / Status Submitted2006-06-26: Order ref: 50 / Type Buy / Status Submitted2006-06-26: Order ref: 51 / Type Buy / Status Submitted2006-06-26: Order ref: 49 / Type Buy / Status Accepted2006-06-26: Order ref: 50 / Type Buy / Status Accepted2006-06-26: Order ref: 51 / Type Buy / Status Accepted2006-06-26: Order ref: 49 / Type Buy / Status Completed2006-06-26: Order ref: 51 / Type Buy / Status Canceled2006-06-26: Order ref: 50 / Type Buy / Status Canceled...2006-11-10: Order ref: 61 / Type Buy / Status Canceled2006-12-11: Oref 63 / Buy at 4032.625552006-12-11: Oref 64 / Buy at 3971.83222006-12-11: Oref 65 / Buy at 3870.509952006-12-12: Order ref: 63 / Type Buy / Status Submitted2006-12-12: Order ref: 64 / Type Buy / Status Submitted2006-12-12: Order ref: 65 / Type Buy / Status Submitted2006-12-12: Order ref: 63 / Type Buy / Status Accepted2006-12-12: Order ref: 64 / Type Buy / Status Accepted2006-12-12: Order ref: 65 / Type Buy / Status Accepted2006-12-15: Order ref: 63 / Type Buy / Status Expired2006-12-15: Order ref: 65 / Type Buy / Status Canceled2006-12-15: Order ref: 64 / Type Buy / Status Canceled

With the following happening:

  • The 1st batch of orders is issued. Order 1 expires and 2 and 3 are cancelled. As expected.

  • Some months later another batch of 3 orders is issued. In this case Order 49 getsCompleted and 50 and 51 are immediately cancelled

  • The last batch is just like the 1st

Let’s check now the behavior withoutOCO:

$ ./oco.py --strat do_oco=False --broker cash=500002005-01-28: Oref 1 / Buy at 2941.110552005-01-28: Oref 2 / Buy at 2896.77222005-01-28: Oref 3 / Buy at 2822.874952005-01-31: Order ref: 1 / Type Buy / Status Submitted2005-01-31: Order ref: 2 / Type Buy / Status Submitted2005-01-31: Order ref: 3 / Type Buy / Status Submitted2005-01-31: Order ref: 1 / Type Buy / Status Accepted2005-01-31: Order ref: 2 / Type Buy / Status Accepted2005-01-31: Order ref: 3 / Type Buy / Status Accepted2005-02-01: Order ref: 1 / Type Buy / Status Expired

And that’s it, which isn’t much (no order execution, not much need for a charteither)

  • The batch of orders is issued

  • Order 1 expires, but because the strategy has gotten the parameterdo_oco=False, orders 2 and 3 are not made part of theOCO group

  • Orders 2 and 3 are therefore not cancelled and because the default expiration delta is1000 days later, they never expire with the available data for the sample (2 years of data)

  • The system never issues a 2nd bath of orders.

Sample usage

$ ./oco.py --helpusage: oco.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE]              [--cerebro kwargs] [--broker kwargs] [--sizer kwargs]              [--strat kwargs] [--plot [kwargs]]Sample Skeletonoptional 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 Code

from__future__import(absolute_import,division,print_function,unicode_literals)importargparseimportdatetimeimportbacktraderasbtclassSt(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 order2oco1oco2=False,# False - use order1 as oco for order3, else order2do_oco=True,# use oco or not)defnotify_order(self,order):print('{}: Order ref:{} / Type{} / Status{}'.format(self.data.datetime.date(0),order.ref,'Buy'*order.isbuy()or'Sell',order.getstatusname()))iforder.status==order.Completed:self.holdstart=len(self)ifnotorder.alive()andorder.refinself.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()defnext(self):ifself.orefs:return# pending orders do nothingifnotself.position:ifself.cross>0.0:# crossing upp1=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)ifself.p.switchp1p2:p1,p2=p2,p1o1=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=o1ifself.p.do_ocoelseNoneo2=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))ifself.p.do_oco:oco3=o1ifnotself.p.oco1oco2elseoco2else:oco3=Noneo3=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 marketif(len(self)-self.holdstart)>=self.p.hold:self.close()defrunstrat(args=None):args=parse_args(args)cerebro=bt.Cerebro()# Data feed kwargskwargs=dict()# Parse from/to-datedtfmt,tmfmt='%Y-%m-%d','T%H:%M:%S'fora,din((getattr(args,x),x)forxin['fromdate','todate']):ifa:strpfmt=dtfmt+tmfmt*('T'ina)kwargs[d]=datetime.datetime.strptime(a,strpfmt)# Data feeddata0=bt.feeds.BacktraderCSVData(dataname=args.data0,**kwargs)cerebro.adddata(data0)# Brokercerebro.broker=bt.brokers.BackBroker(**eval('dict('+args.broker+')'))# Sizercerebro.addsizer(bt.sizers.FixedSize,**eval('dict('+args.sizer+')'))# Strategycerebro.addstrategy(St,**eval('dict('+args.strat+')'))# Executecerebro.run(**eval('dict('+args.cerebro+')'))ifargs.plot:# Plot if requested tocerebro.plot(**eval('dict('+args.plot+')'))defparse_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 datesparser.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')returnparser.parse_args(pargs)if__name__=='__main__':runstrat()

[8]ページ先頭

©2009-2025 Movatter.jp