atreyuxtrading / atreyu-backtrader-api

Atreyu's integration of IB Native API with backtrader
BSD 2-Clause "Simplified" License
91 stars 58 forks source link

Bracket/oco functionality doesn't work with IB #11

Open t3chap opened 1 year ago

t3chap commented 1 year ago

File ~/anaconda3/lib/python3.9/site-packages/atreyu_backtrader_api-0.1.0-py3.9.egg/atreyu_backtrader_api/ibbroker.py:358 in submit order.ocaGroup = self.orderbyid[order.oco.orderId].ocaGroup

AttributeError: 'int' object has no attribute 'orderId'

Exception: 'Execution' object has no attribute 'OrderId' Traceback (most recent call last): File "/home/tim/anaconda3/lib/python3.9/site-packages/atreyu_backtrader_api-0.1.0-py3.9.egg/atreyu_backtrader_api/ibbroker.py", line 520, in push_commissionreport oid = ex.OrderId AttributeError: 'Execution' object has no attribute 'OrderId' ExecId: 0000e1a7.641848b4.01.01, Commission: 0.770000, Currency: USD, RealizedPnL: , Yield: , YieldRedemptionDate: 0

t3chap commented 1 year ago

Bracket orders entered into the IB API don't automatically cancel the unfilled part when the other side of the bracket is filled when using the buy_bracket or sell_bracket. This could be because of a change on IB's side of things, I'm not certain. The orders when viewed from the IBAPI have One Cancel Another group names but just aren't cancelling, leaving unexpected orders working.

t3chap commented 1 year ago

the line in the code seems to work if it is changed to oid = ex.orderId

It appears the api update changed the case of the beginning of the variable. However, I'm still not able to get bracket orders to be placed in the IBAPI with the same OCA grouping, which still leads to orphaned orders.

Ingravido commented 6 months ago

Any update on this? The two main strategies I'm working on are using those handy bracket orders... I guess at least I can rewrite and handle myself the cancellation of the unfilled part of the main order.

t3chap commented 6 months ago

I just made my own versions as methods. Ideally the correct ones would be fixed but to make this possible to trade I did the following, which seems to work just fine:

    def buy_bracket_market(self,size=1, stopprice=None, limitprice=None, ** kwargs):
        '''replace the organic buy_bracket order since no longer integrates
        with IB well. This buys at the market and sets a bracket at the 
        stop price and limit price.
        '''
        try:
            nextid = self.broker.get_high_order_id()
            print('next id is {}' . format(nextid))  
            if stopprice:
                if limitprice:
                    print('buying bracket testing target: {} stop: {} ' . format(limitprice,stopprice))
                    oca_group = (uuid.uuid4())
                    main_order = self.buy(exectype=bt.Order.Market, size = size, transmit = False, ** kwargs)
                    self.broker.get_high_order_id()
                    time.sleep(.1)
                    take_profit_order = self.sell(exectype=bt.Order.Limit, price = limitprice, size = main_order.size,\
                                                  transmit=False,ocaType = 1, ocaGroup = oca_group, ** kwargs)
                    self.broker.get_high_order_id()
                    time.sleep(.1)           
                    stop_loss_order = self.sell(exectype=bt.Order.Stop, price = stopprice, size = main_order.size,\
                                                transmit=True,ocaType = 1, ocaGroup = oca_group, ** kwargs)

                else:
                    print('missing limitprice')
            else:
                print('missing stopprice')
        except: #this might be problemmatic if only part of the previous bracket failed, we'd end up with unintentional duplicates
             nextid = self.broker.get_high_order_id()
             print('next id is {}' . format(nextid))  
             if stopprice:
                 if limitprice:
                     print('selling bracket testing target: {} stop: {} ' . format(limitprice,stopprice))
                     oca_group = (uuid.uuid4())
                     main_order = self.sell(exectype=bt.Order.Market, size = size, transmit = False, ** kwargs)
                     self.broker.get_high_order_id()
                     time.sleep(.1)             
                     take_profit_order = self.buy(exectype=bt.Order.Limit, price = limitprice,  size = main_order.size, \
                                                  transmit=False,ocaType = 1, ocaGroup = oca_group, ** kwargs)
                     self.broker.get_high_order_id()
                     time.sleep(.1)     
                     stop_loss_order = self.buy(exectype=bt.Order.Stop, price = stopprice, size = main_order.size, \
                                                transmit=True,ocaType = 1, ocaGroup = oca_group, ** kwargs)

                     #bracket_order = [main_order,take_profit_order,stop_loss_order]       
                     #return bracket_order
                 else:
                     print('missing limitprice')
             else:
                 print('missing stopprice')            

    def sell_bracket_market(self,size=1, stopprice=None, limitprice=None, ** kwargs):
         '''replace the organic sell_bracket order since no longer integrates
         with IB well. This sellss at the market and sets a bracket at the 
         stop price and limit price.
         '''
         try:
             nextid = self.broker.get_high_order_id()
             print('next id is {}' . format(nextid))  
             if stopprice:
                 if limitprice:
                     print('selling bracket testing target: {} stop: {} ' . format(limitprice,stopprice))
                     oca_group = (uuid.uuid4())
                     main_order = self.sell(exectype=bt.Order.Market, size = size, transmit = False, ** kwargs)
                     self.broker.get_high_order_id()
                     time.sleep(.1)                          

                     take_profit_order = self.buy(exectype=bt.Order.Limit, price = limitprice,  size = main_order.size, \
                                                  transmit=False,ocaType = 1, ocaGroup = oca_group, ** kwargs)
                     self.broker.get_high_order_id()
                     time.sleep(.1)                              

                     stop_loss_order = self.buy(exectype=bt.Order.Stop, price = stopprice, size = main_order.size, \
                                                transmit=True,ocaType = 1, ocaGroup = oca_group, ** kwargs)

                     #bracket_order = [main_order,take_profit_order,stop_loss_order]       
                     #return bracket_order
                 else:
                     print('missing limitprice')
             else:
                 print('missing stopprice')        
         except:
             nextid = self.broker.get_high_order_id()
             print('next id is {}' . format(nextid))  
             if stopprice:
                 if limitprice:
                     print('selling bracket testing target: {} stop: {} ' . format(limitprice,stopprice))
                     oca_group = (uuid.uuid4())
                     main_order = self.sell(exectype=bt.Order.Market, size = size, transmit = False, ** kwargs)
                     self.broker.get_high_order_id()
                     time.sleep(.1)                          

                     take_profit_order = self.buy(exectype=bt.Order.Limit, price = limitprice,  size = main_order.size, \
                                                  transmit=False,ocaType = 1, ocaGroup = oca_group, ** kwargs)
                     self.broker.get_high_order_id()
                     time.sleep(.1)                              

                     stop_loss_order = self.buy(exectype=bt.Order.Stop, price = stopprice, size = main_order.size, \
                                                transmit=True,ocaType = 1, ocaGroup = oca_group, ** kwargs)

                     #bracket_order = [main_order,take_profit_order,stop_loss_order]       
                     #return bracket_order
                 else:
                     print('missing limitprice')
             else:
                 print('missing stopprice')