Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    Multi Assets on IB Not Responding

    General Code/Help
    2
    6
    49
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Adham Suliman
      Adham Suliman last edited by

      Hello,

      I'm having trouble executing one strategy on two live data feeds on Interactive Brokers. It seems like the connection is being made, but the strategy doesn't seem to be making any trades. When I reduce the data feeds to one data feed, the program works.

      The first set of code is used to make the connection to interactive brokers. The second section of code is the strategy. Finally, the third is the output from python. I can see that the connection is being made, but again, no trades are being made. Any help would be highly appreciated.

      Python code to add data feeds and connect to IB

      import sys, argparse, os
      from datetime import datetime 
      
      import backtrader as bt
      import backtrader.stores.ibstore as ibstore
      from strategy.long_copy import EmaCross_long
      
      def run():
          cerebro = bt.Cerebro(stdstats=False)
          ibstore = bt.stores.IBStore(host='127.0.0.1', port=4002, clientId=0)
      
      
          stockkwargs = dict(
              timeframe=bt.TimeFrame.Seconds,
              rtbar=True,  # use RealTime 5 seconds bars
              historical = False,
              qcheck=100,  # timeout in seconds (float) to check for events
              latethrough=False)
      #       tradename=None,  # use a different asset as order target
      #       fromdate = datetime.today())
      
          contracts = ['NQ-202103-GLOBEX-USD','RTY-202103-GLOBEX-USD'] #
          c_name = ['NQ','RTY']
          for d,c in zip(contracts,c_name):
              data = ibstore.getdata(dataname=d,**stockkwargs)  
              cerebro.resampledata(data, name=c, timeframe=bt.TimeFrame.Seconds, compression=5)
      
          cerebro.broker = ibstore.getbroker()
          cerebro.addstrategy(EmaCross_long)
          cerebro.run()
      
      if __name__ == '__main__':
          run()
      

      Python code for bt.strategy

      
      
      import pandas as pd
      import backtrader as bt
      from datetime import datetime, timedelta
      import time
      import math
      import sqlalchemy as db
      
      
      class EmaCross_long(bt.Strategy):
          params = {('order_percentage', .95)}
          def log(self, txt, dt=None):
              ''' Logging function fot this strategy'''
              dt = dt or self.datas[0].datetime.datetime(0)
              print('%s, %s' % (dt.isoformat(), txt))
      
          def __init__(self):
              self.orderid = list()
              self.inds = dict()
              for i,d in enumerate(self.datas):
                  self.inds[d] = dict()
                  self.inds[d]['dataopen'] = d.open
                  self.inds[d]['dataclose'] = d.close
                  self.inds[d]['datahigh'] = d.high
                  self.inds[d]['datalow'] = d.low
                  
                  # Define average true range
                  self.inds[d]['atr'] = bt.indicators.ATR(d, period =14)
                  self.inds[d]['ema_s'] = bt.indicators.EMA(d,period=6,plotname='ema_s (6)')
                  self.inds[d]['ema_l'] = bt.indicators.EMA(d, period=17, plotname='ema_l (17)')
      
              # Define self order
              self.order = None
              self.trail_stop = None
              self.cross_down = None
              self.sell_type = 'atr_cross_sell'
      
          def notify_store(self, msg, *args, **kwargs):
              print(f'message: {msg}')
              error = getattr(msg,'errorCode',False)
              if error:
                  print(error)
      
          def notify_order(self, order):
              # https://www.backtrader.com/docu/order-creation-execution/order-creation-execution/
              # Check the order status.
              print('{}: Order ref: {} / Type {} / Status {}'.format(
                      self.data.datetime.datetime(0),
                      order.ref, 'Buy' * order.isbuy() or 'Sell',
                      order.getstatusname()))
      
              # If it is submitted/accepted, leave the fucntion
              if order.status in [order.Submitted, order.Accepted]:
                  #self.log(f"Order submitted/accepted but not complete")
                  return
      
              if order.status in [order.Completed]:
                  if order.isbuy():
                      # Keep track of executred price and stop loss
                      self.log(f"Buy Executed at {order.executed.price}")
                  elif order.issell():
                      # Keep track of sell price
                      self.log(f"Sell Executed at {order.executed.price} and order {order}")
                      #print(f'position: \n {self.position} and order: \n {order}')
                  self.order = None
                  self.trail_stop = None
                  self.cross_down = None
      
              if order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log(f"Order Rejected: {order.Rejected}")
                  return
      
              if order.status == order.Canceled:
                  self.log(f'Order Canceled {order.Canceled}')
                  return
      
          def next(self):
              if self.order:
                  # An order is pending ... nothing can be done
                  return
      
              # if there is no current postion
              for i, d in enumerate(self.datas):
                  if self.getposition(d).size <= 0:
                      try:
                          self.cancel(self.cross_down)
                      except:
                          pass
                      if (self.inds[d].dataclose[0] > self.inds[d].ema_s[0] and
                      self.inds[d].ema_s[0] > self.inds[d].ema_l[0] and
                      (self.inds[d].ema_s[-1] < self.ema_l[-1] or
                      self.inds[d].ema_s[-2] < self.inds[d].ema_l[-2])):
                      # amount to invest
                      #amount_to_invest = self.params.order_percentage * self.broker.cash
                      #self.size = math.floor(amount_to_invest/self.dataclose)
                          self.size = 1
                          self.log('BUY CREATE, %.2f' % self.dataclose[0])
                          self.order = self.buy(size = self.size,
                                                data = d)
      
                  #elif self.getposition(d) > 0:
                  #    if (self.trail_stop is None and self.cross_down is None and self.sell_type in ['atr_sell','atr_cross_sell']):
                  #        # cancel any prior cross_down
                  #        try:
                  #            self.cancel(self.cross_down)
                  #        except:
                  #            pass
                  #        # sell on trailing stop
                  #        self.trail_stop = self.close(data = d,
                  #                                    exectype=bt.Order.StopTrail,
                  #                                    trailamount = 3 * self.atr[0],
                  #                                    size = self.getposition(d), 
                  #                                    ref = '999')
                  #                                    #valid = datetime.now() + timedelta(hours = 12))
      
                      elif (self.getposition(d) > 0 and self.ema_s[0] < self.ema_l[0]
                          and (self.inds[d].ema_s[-1] > self.inds[d].ema_l[-1]
                          or self.inds[d].ema_s[-2] > self.inds[d].ema_l[-2])):
                          # cancel trailing stop
                          self.cancel(self.trail_stop)
                          # sell on the cros down
                          self.cross_down = self.close(data = d)
      

      IB is connecting, but bt isn't performing any trades.

      Server Version: 76
      TWS Time at connection:20210113 11:47:50 EST
      message: <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:usfuture.nj>
      2104
      message: <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:usfarm.nj>
      2104
      message: <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:usfuture>
      2104
      message: <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:cashfarm>
      2104
      message: <error id=-1, errorCode=2104, errorMsg=Market data farm connection is OK:usfarm>
      2104
      message: <error id=-1, errorCode=2106, errorMsg=HMDS data farm connection is OK:euhmds>
      2106
      message: <error id=-1, errorCode=2106, errorMsg=HMDS data farm connection is OK:fundfarm>
      2106
      message: <error id=-1, errorCode=2106, errorMsg=HMDS data farm connection is OK:ushmds>
      2106
      message: <error id=-1, errorCode=2158, errorMsg=Sec-def data farm connection is OK:secdefil>
      2158
      
      vladisld 1 Reply Last reply Reply Quote 0
      • Adham Suliman
        Adham Suliman last edited by

        I realized some of the code within the bt.strategy script wasn't correctly calling the datas feed within next(). I updated the script, but I'm still having the respective issues above. Below is the updated script for bt.strategy.

        Python code for bt.strategy

        import backtrader as bt
        from datetime import datetime, timedelta
        import time
        import math
        
        
        class EmaCross_long(bt.Strategy):
            params = {('order_percentage', .95)}
            def log(self, txt, dt=None):
                ''' Logging function fot this strategy'''
                dt = dt or self.datas[0].datetime.datetime(0)
                print('%s, %s' % (dt.isoformat(), txt))
        
            def __init__(self):
                self.orderid = list()
                self.inds = dict()
                for i, d in enumerate(self.datas):
                    self.inds[d] = dict()
                    self.inds[d]['dataopen'] = d.open
                    self.inds[d]['dataclose'] = d.close
                    self.inds[d]['datahigh'] = d.high
                    self.inds[d]['datalow'] = d.low
                    
                    # Define average true range
                    self.inds[d]['atr'] = bt.indicators.ATR(d, period =14)
                    self.inds[d]['ema_s'] = bt.indicators.EMA(d,period=6,plotname='ema_s (6)')
                    self.inds[d]['ema_l'] = bt.indicators.EMA(d, period=17, plotname='ema_l (17)')
        
                # Define self order
                self.order = None
                self.trail_stop = None
                self.cross_down = None
                self.sell_type = 'atr_cross_sell'
        
            def notify_store(self, msg, *args, **kwargs):
                print(f'message: {msg}')
                error = getattr(msg,'errorCode',False)
                if error:
                    print(error)
        
            def notify_order(self, order):
                # https://www.backtrader.com/docu/order-creation-execution/order-creation-execution/
                # Check the order status.
                print('{}: Order ref: {} / Type {} / Status {}'.format(
                        self.data.datetime.datetime(0),
                        order.ref, 'Buy' * order.isbuy() or 'Sell',
                        order.getstatusname()))
        
                # If it is submitted/accepted, leave the fucntion
                if order.status in [order.Submitted, order.Accepted]:
                    #self.log(f"Order submitted/accepted but not complete")
                    return
        
                if order.status in [order.Completed]:
                    if order.isbuy():
                        # Keep track of executred price and stop loss
                        self.log(f"Buy Executed at {order.executed.price}")
                    elif order.issell():
                        # Keep track of sell price
                        self.log(f"Sell Executed at {order.executed.price} and order {order}")
                        #print(f'position: \n {self.position} and order: \n {order}')
                    self.order = None
                    self.trail_stop = None
                    self.cross_down = None
        
                if order.status in [order.Canceled, order.Margin, order.Rejected]:
                    self.log(f"Order Rejected: {order.Rejected}")
                    return
        
                if order.status == order.Canceled:
                    self.log(f'Order Canceled {order.Canceled}')
                    return
        
            def next(self):
                if self.order:
                    # An order is pending ... nothing can be done
                    return
        
                # if there is no current postion
                for i, d in enumerate(self.datas):
                    if self.getposition(d).size <= 0:
                        if (self.inds[d].dataclose[0] > self.inds[d].ema_s[0] and
                        self.inds[d].ema_s[0] > self.inds[d].ema_l[0] and
                        (self.inds[d].ema_s[-1] < self.inds[d].ema_l[-1] or
                        self.inds[d].ema_s[-2] < self.inds[d].ema_l[-2])):
                        # amount to invest
                        #amount_to_invest = self.params.order_percentage * self.broker.cash
                        #self.size = math.floor(amount_to_invest/self.dataclose)
                            self.size = 1
                            self.log('BUY CREATE, %.2f' % self.dataclose[0])
                            self.order = self.buy(size = self.size,
                                                  data = d)
        
                    #elif self.getposition(d) > 0:
                    #    if (self.trail_stop is None and self.cross_down is None and self.sell_type in ['atr_sell','atr_cross_sell']):
                    #        # cancel any prior cross_down
                    #        try:
                    #            self.cancel(self.cross_down)
                    #        except:
                    #            pass
                    #        # sell on trailing stop
                    #        self.trail_stop = self.close(data = d,
                    #                                    exectype=bt.Order.StopTrail,
                    #                                    trailamount = 3 * self.atr[0],
                    #                                    size = self.getposition(d), 
                    #                                    ref = '999')
                    #                                    #valid = datetime.now() + timedelta(hours = 12))
        
                        elif (self.getposition(d) > 0 and self.inds[d].ema_s[0] < self.inds[d].ema_l[0]
                            and (self.inds[d].ema_s[-1] > self.inds[d].ema_l[-1]
                            or self.inds[d].ema_s[-2] > self.inds[d].ema_l[-2])):
                            # sell on the cros down
                            self.cross_down = self.close(data = d)
        
        1 Reply Last reply Reply Quote 0
        • vladisld
          vladisld @Adham Suliman last edited by

          @Adham-Suliman said in Multi Assets on IB Not Responding:

          cerebro.resampledata(data, name=c, timeframe=bt.TimeFrame.Seconds, compression=5)

          Why do you need to resample the 5 sec bars to the 5 sec bars ? I'm also using the RT bars - and it works pretty reliably without resampling.

          So instead of resampling I'd recommend to just specify the compression = 5 do the data arguments.

          Adham Suliman 1 Reply Last reply Reply Quote 2
          • Adham Suliman
            Adham Suliman @vladisld last edited by

            @vladisld

            You're correct. After reading the documentation in more detail, there is no need to resample the data. I am now using cerebro.adddata(). I'm still getting the same output as before where a connection is made, but bt isn't performing any trades. I've cleaned up my main file, and have written it below.

            Python code to add data feeds and connect to IB

            # import modules 
            import sys, argparse, os
            from datetime import datetime 
            
            import backtrader as bt
            import backtrader.stores.ibstore as ibstore
            from strategy.long_copy import EmaCross_long
            
            def run():
                cerebro = bt.Cerebro(stdstats=False)
                ibstore = bt.stores.IBStore(host='127.0.0.1', port=4002, clientId=0)
            
                # Adding two data feeds
                contracts = ['NQ-202103-GLOBEX-USD','RTY-202103-GLOBEX-USD'] #
                c_name = ['NQ','RTY']
                for d,c in zip(contracts,c_name):
                    data = ibstore.getdata(dataname=d, 
                                            timeframe=bt.TimeFrame.Seconds,
                                            compression=5,
                                            rtbar=True,  # use RealTime 5 seconds bars
                                            historical = False,
                                            qcheck=100,  # timeout in seconds (float) to check for events
                                            latethrough=False)
                    cerebro.adddata(data, name=c)
            
                cerebro.broker = ibstore.getbroker()
                cerebro.addstrategy(EmaCross_long)
                cerebro.run()
            
            if __name__ == '__main__':
                run()
            

            Are there any places you would suggest setting breakpoints within the bt.strategy script that would help us figure out why bt isn't placing any trades?

            Your contributions to the community are highly appreciated @vladisld. I've benefited immensely from your commentary on this forum, and I can't thank you enough!

            1 Reply Last reply Reply Quote 0
            • vladisld
              vladisld last edited by

              If it could be one thing that seems strange to me (without running your code yet) it would be a quite large value used for qcheck.

              Setting this value too high may hurt in multi-data scenario: let's say one symbol (call it 'A') produces no new bars for some reason and the other one ('B') is very active. So the code using the qcheck==100 will wait for more than a minute until new bar will be received for symbol 'A', while not reacting on multiple bars already available for symbol 'B'.

              I would suggest to lower this timeout to 0.5

              1 Reply Last reply Reply Quote 0
              • Adham Suliman
                Adham Suliman last edited by

                For anyone looking to use this in the future, in the next method, I was trying to access a dictionary using inds[d].field[0]. The proper syntax to access a dictionary is inds[d]['field'][0]. I also lowered my qcheck to .05 as @vladisld suggested. If someone has a link as to how to handle multiple orders with multiple data feeds, I would highly appreciate it if you posted the link below.

                Thanks

                1 Reply Last reply Reply Quote 0
                • 1 / 1
                • First post
                  Last post
                Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
                $(document).ready(function () { app.coldLoad(); }); }