For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

Opening bracket limit buy and bracket limit sell order at same time



  • Is this possible?

    Here's the scenario: when a given signal occurs, I want to place a limit buy and a limit sell order, say 5% away in each direction from current price. Depending on which is entered, I would like an associated Stop Loss and Take Profit that gets attached to the main side order as well.

    Thanks for any help.

    (I think I saw a post once about a similar question, but wasn't able to find it)

    EDIT: I found the post: https://community.backtrader.com/topic/1546/problem-with-bracket-orders ... seems like it might be possible.

    Could I do something like this?

    mainside = self.buy(price=14.50, exectype=bt.Order.Limit, transmit=False)
    lowside  = self.sell(price=12.00, size=mainside.size, exectype=bt.Order.Stop,
                         transmit=False, parent=mainside)
    highside = self.sell(price=15.00, size=mainside.size, exectype=bt.Order.Limit,
                         transmit=False, parent=mainside)
    shortside = self.sell(price=12.50, exectype=bt.Order.Limit, transmit=False)
    shortstop  = self.buy(price=14.00, size= shortside.size, exectype=bt.Order.Stop,
                                              transmit=False, parent=shortside)
    shortprofit = self.buy(price=10.00, size= shortside.size, exectype=bt.Order.Limit,
                                              transmit=True, parent=shortside)
    

    Note: I'm leaving 'transmit' to False until the very last order, is that right?



  • Between this post: https://community.backtrader.com/topic/523/simultaneous-bracket-order-cancel-one-when-another-has-been-submitted-accepted/2

    and this post: https://community.backtrader.com/topic/1546/problem-with-bracket-orders

    I'm getting somewhere...

    My order placing is like this (truncated to not take up too much space):

                            self.longside = self.buy(data=dataname,
    
                            self.long_lowside  = self.sell(data=dataname, 
    
                            self.long_highside = self.sell(data=dataname, 
    
                            self.shortside = self.sell(data=dataname, 
    
                            self.short_lowside  = self.buy(data=dataname, 
    
                            self.short_highside = self.buy(data=dataname, 
    
    

    My notify_order is like this:

           def notify_order(self, order):
        
                # if order.status in [order.Submitted]:
                #     print('*SUBMITTED*', dict(order.info)['info'])
        
                # if order.status in [order.Accepted]:
                    # print('*ACCEPTED*', dict(order.info)['info']['setup'],  dict(order.info)['info']['datetime'])
        
                date = self.data.datetime.datetime()
                if order.status in [order.Completed]:
                    # print('*COMPLETED*', dict(order.info)['info']['setup'], dict(order.info)['info']['datetime']) 
                    if order.isbuy():
                        if ('open trade' in dict(order.info)['info']['setup']) & ('LONG' in dict(order.info)['info']['direction']):
                            # print('*CANCELING*', dict(self.shortside.info)['info']['setup'], dict(self.shortside.info)['info']['direction'])
                            self.cancel(self.shortside)
                            # print('*CANCELING*', dict(self.short_lowside.info)['info']['setup'], dict(self.shortside.info)['info']['direction'])
                            self.cancel(self.short_lowside)
                            # print('*CANCELING*', dict(self.short_highside.info)['info']['setup'], dict(self.shortside.info)['info']['direction'])
                            self.cancel(self.short_highside)
                    elif order.issell():
                        if ('open trade' in dict(order.info)['info']['setup']) & ('SHORT' in dict(order.info)['info']['direction']):
                            # print('*CANCELING*', dict(self.longside.info)['info']['setup'], dict(self.longside.info)['info']['direction'])
                            self.cancel(self.longside)
                            # print('*CANCELING*', dict(self.long_lowside.info)['info']['setup'], dict(self.long_lowside.info)['info']['direction'])
                            self.cancel(self.long_lowside)
                            # print('*CANCELING*', dict(self.long_highside.info)['info']['setup'], dict(self.long_highside.info)['info']['direction'])
                            self.cancel(self.long_highside)
    

    I know it's not quite working yet because over 950+ trading days it only ends up placing 5-10 trades, where normally if I did longs only or shorts only it would be placing more like 300-500. Still troubleshooting...

    PS: I am ending each block that makes up a bracket order with a transmit=True



  • Figured it out!

    If anyone tries this in the future -- don't forget your Sizer has to be able to handle placing those trades all at once :)


  • administrators

    @tw00000 said in Opening bracket limit buy and bracket limit sell order at same time:

    Note: I'm leaving 'transmit' to False until the very last order, is that right?

    For sure it isn't. You will only transmit the shortside bracket related orders.

    @tw00000 said in Opening bracket limit buy and bracket limit sell order at same time:

    Could I do something like this?

    Yes, but when one of brackets is active (because its middle order has executed) the other is still in the system awaiting activation. You probably want to manage that.

    @tw00000 said in Opening bracket limit buy and bracket limit sell order at same time:

    PS: I am ending each block that makes up a bracket order with a transmit=True

    You apparently (because the actual code isn't shown) already corrected the error from above.

    @tw00000 said in Opening bracket limit buy and bracket limit sell order at same time:

                            # print('*CANCELING*', dict(self.longside.info)['info']['setup'], dict(self.longside.info)['info']['direction'])
                            self.cancel(self.longside)
                            # print('*CANCELING*', dict(self.long_lowside.info)['info']['setup'], dict(self.long_lowside.info)['info']['direction'])
                            self.cancel(self.long_lowside)
                            # print('*CANCELING*', dict(self.long_highside.info)['info']['setup'], dict(self.long_highside.info)['info']['direction'])
                            self.cancel(self.long_highside)
    

    Wouldn't you be better off placing the 3 orders in a list and then cancelling the 3 in a single statement (like a list comprehension) ? A lot more readable.

    Good you figured it out!



  • @backtrader said in Opening bracket limit buy and bracket limit sell order at same time:

    Yes, but when one of brackets is active (because its middle order has executed) the other is still in the system awaiting activation. You probably want to manage that.

    I'm pretty sure this is my current problem. Could just be 3:20am brain, but I can't figure out how to manage it. I thought that by checking in notify_order if an order is (for example) a buy, and then checking if it's an "open" order, then canceling the other (sell) bracket, I would be avoiding this issue?


  • administrators

    You probably want to check the order reference of the main order of a bracket as the key for a dictionary entry which contains a list with the orders of the other bracket (you would only need to cancel the main opposite order though)



  • @backtrader said in Opening bracket limit buy and bracket limit sell order at same time:

    You probably want to check the order reference of the main order of a bracket as the key for a dictionary entry which contains a list with the orders of the other bracket (you would only need to cancel the main opposite order though)

    Here's what I have so far:

    In my next, after all of the code to create the brackets:

    self.shortside.addinfo(bracket_orders = {self.shortside.ref: self.longside, self.longside.ref: self.shortside})
    self.longside.addinfo(bracket_orders = {self.shortside.ref: self.longside, self.longside.ref: self.shortside})
    

    And then in notify_order:

            if order.status in [order.Completed]:
                print(dict(order.info)['info']['bracket_orders'][order.ref])
                self.cancel(dict(order.info)['info']['bracket_orders'][order.ref])
    

    This returns the error KeyError: 'bracket_orders'.

    I'm attempting to use this method I found in the Orders docs:

    info: custom information passed over method addinfo(). It is kept in the form of an OrderedDict which has been subclassed, so that keys can also be specified using ‘.’ notation

    But I haven't yet figured out how to use it, which I'm hoping allows you to add information to an order object.

    Even then, I think I could be a ways off, as "self.shortside" will be overwritten every time a new order comes into the pipeline


  • administrators

    @tw00000 said in Opening bracket limit buy and bracket limit sell order at same time:

    This returns the error KeyError: 'bracket_orders'

    For starters you have 6 orders and you only add the information to 2 of them. From these 2 only 1 will execute, with the other executed one being one of the remaining. So something is bound to break.

    @tw00000 said in Opening bracket limit buy and bracket limit sell order at same time:

    print(dict(order.info)['info']['bracket_orders'][order.ref])
    

    This seems some cumbersome syntax in which info is being addressed inside of itself. Don't know what your expectation is here, but I guess this is going to fail each and every time.

    @tw00000 said in Opening bracket limit buy and bracket limit sell order at same time:

    But I haven't yet figured out how to use it, which I'm hoping allows you to add information to an order object.

    Well, you call addinfo with named arguments and things are available inside info which is a dictionary. (In that part of the docs you quotes, info is listed as an attribute)

    @tw00000 said in Opening bracket limit buy and bracket limit sell order at same time:

    Member Attributes:

    • info: custom information passed over method addinfo(). It is kept in the form of an OrderedDict which has been subclassed, so that keys can also be specified using ‘.’ notation


  • Another version I'm attempting (also not working):

    In my init:

    self.bracket_orders = {}
    

    After my bracket orders in next:

    self.bracket_orders[self.shortside.ref] = self.longside
    self.bracket_orders[self.longside.ref] = self.shortside
    

    In my notify_order:

    if order.status in [order.Completed]:
        self.cancel(self.bracket_orders[order.ref])
    

    Trying to get creative here!

    This one ends up with a strange dance:

    *ACCEPTED* 144.67 open long trade 2017-09-06
    *ACCEPTED* 143.22 close long trade SL 2017-09-06
    *ACCEPTED* 147.2 close long trade TP 2017-09-06
    *ACCEPTED* 144.21 open short trade 2017-09-06
    *ACCEPTED* 141.69 close short trade TP 2017-09-06
    *ACCEPTED* 145.65 close short trade SL  2017-09-06
    *COMPLETED* 144.67 open long trade 2017-09-06
    *CANCELING* open short trade SHORT
    *COMPLETED* 144.21 open short trade 2017-09-06
    *CANCELING* open long trade LONG
    

    Where you can see that both orders end up canceled for some reason



  • @backtrader said in Opening bracket limit buy and bracket limit sell order at same time:

    Well, you call addinfo with named arguments

    Could you show a quick example?



  • I figured it out, I think!

    Since I was placing limit orders in both directions, and limit orders executed at your defined price or better, both long and short brackets were trying to execute simultaneously. I changed these to 'Stop' orders (which is kind of like market-buy-if-touched, for this purpose, right?) I get normal cancellation behavior, or like I would expect:

    *SUBMITTED* 185.56 LL_style open long trade (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *SUBMITTED* 182.78 LL_style close long trade SL (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *SUBMITTED* 186.95 LL_style close long trade TP (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *SUBMITTED* 184.01 LL_style open short trade (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *SUBMITTED* 182.63 LL_style close short trade TP (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *SUBMITTED* 186.77 LL_style close short trade SL  (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *ACCEPTED* 185.56 LL_style open long trade (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *ACCEPTED* 182.78 LL_style close long trade SL (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *ACCEPTED* 186.95 LL_style close long trade TP (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *ACCEPTED* 184.01 LL_style open short trade (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *ACCEPTED* 182.63 LL_style close short trade TP (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *ACCEPTED* 186.77 LL_style close short trade SL  (184.62, 184.95) 2018-09-14 2018-09-14 14:45:00
    *COMPLETED* 184.01 LL_style open short trade (184.62, 184.95) 2018-09-14 2018-09-14 16:00:00
    *CANCELING* LL_style open long trade LONG 2018-09-14 16:00:00
    *COMPLETED* 183.27 LL_style open short trade (184.05, 184.45) 2018-09-13 2018-09-14 17:00:00
    

    Until I find out later that something else mysteriously is going wrong, I think that solves it :) Hopefully this trail of tears helps someone in the future


Log in to reply
 

});