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/

    Indicator for multiple Datafeeds

    Indicators/Strategies/Analyzers
    2
    9
    46
    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.
    • H
      HexaBraum last edited by

      The Bollinger Bands work for the first Data File, so it´s the only one where Orders are created.
      Additionally the buys and sells dont get displayed, Where is the mistake?
      Thanks for your help :)

      import backtrader as bt
      import backtrader.feeds as btfeed
      from datetime import datetime
      
      class dataFeed(btfeed.GenericCSVData):
          params = (
              ('dtformat', '%Y-%m-%d %H:%M:%S'),
              ('datetime', 0),
              ('open', 1),
              ('high', 2),
              ('low', 3),
              ('close', 4),
              ('volume', 5),
              ('openinterest', -1)
          )
      
      class BollingerBands(bt.Indicator):
          lines = ('topband', 'botband')
          params = (('period', 21), ('devfactor', 2.0), ('movav', bt.ind.MovAv.Simple),)
      
          plotinfo = dict(subplot=False)
      
          def _plotlabel(self):
              plabels = [self.p.period, self.p.devfactor]
              plabels += [self.p.movav] * self.p.notdefault('movav')
              return plabels
      
          def __init__(self):
              bb = bt.ind.BollingerBands(
                  period=self.p.period, devfactor=self.p.devfactor, movav=self.p.movav)
      
      class firstStrategy(bt.Strategy):
          def log(self, txt, dt=None):
              ''' Logging function fot this strategy'''
              dt = dt or self.datas[0].datetime.date(0)
              print('%s, %s' % (dt.isoformat(), txt))
      
          def __init__(self):
              self.dataclose = self.datas[0].close
              bb = bt.ind.BollingerBands(
                  period=21, devfactor=2.0, movav=bt.ind.MovAv.Simple)
              self.lines.topband = bb.top
              self.lines.botband = bb.bot
      
      
          def notify_order(self, order):
              if order.status in [order.Submitted, order.Accepted]:
                  # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                  return
      
              # Check if an order has been completed
              # Attention: broker could reject order if not enough cash
              if order.status in [order.Completed]:
                  if order.isbuy():
                      self.log('BUY EXECUTED, %.2f' % order.executed.price)
                  elif order.issell():
                      self.log('SELL EXECUTED, %.2f' % order.executed.price)
      
                  self.bar_executed = len(self)
      
              elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                  self.log('Order Canceled/Margin/Rejected')
      
              # Write down: no pending order
              #self.order = None
      
          def next(self):
              for d in self.datas:
                  dt, dn = self.datetime.date(), d._name
                  pos = self.getposition(d).size
                  # Simply log the closing price of the series from the reference
                  self.log('Close, %.2f' % d.close[0])
      
              # Check if an order is pending ... if yes, we cannot send a 2nd one
              #if self.order:
              #   return
      
              # Check if we are in the market
              if not self.position:
                  if self.dataclose[0] < self.lines.botband[0]:
                      # current close less than previous close
      
                      # if self.dataclose[-1] < self.dataclose[-2]:
                      # previous close less than the previous close
      
                      # BUY, BUY, BUY!!! (with default parameters)
                      self.log('BUY CREATE, %.2f' % self.dataclose[0])
      
                      # Keep track of the created order to avoid a 2nd order
                      self.order = self.buy()
      
                  # Already in the market ... we might sell
                  if self.dataclose[0] > self.lines.topband[0]:
                      # SELL, SELL, SELL!!! (with all possible default parameters)
                      self.log('SELL CREATE, %.2f' % self.dataclose[0])
      
                      # Keep track of the created order to avoid a 2nd order
                      self.order = self.sell()
      
      
      if __name__ == '__main__':
          # Create a cerebro entity
          cerebro = bt.Cerebro()
          startcash = 10000
      
          # Add a strategy
          cerebro.addstrategy(firstStrategy)
          cerebro.addindicator(BollingerBands)
      
          datalist = [("AM.ATVI.csv"), ("AM.MO.csv"), ("AM.GM.csv"), ("AM.CL.csv")]
          """  , ("AM.FDX.csv"), ("AM.NAKD.csv"), ("AM.NVDA.csv"), ("AM.OCGN.csv"), ("AM.ON.csv")
              , ("AM.PDD.csv"), ("AM.PLUG.csv"), ("AM.QCOM.csv"), ("AM.MDLZ.csv"), ("AM.ADP.csv")
              , ("AM.C.csv"), ("AM.AZN.csv"), ("AM.PEP.csv"), ("AM.ORCL.csv"), ("AM.QTT.csv")
              , ("AM.RUN.csv"), ("AM.SABR.csv"), ("AM.SNDL.csv"), ("AM.TSLA.csv"), ("AM.UAL.csv")
              , ("AM.UXIN.csv"), ("AM.WEN.csv"), ("AM.WFC.csv"), ("AM.YY.csv"), ("AM.ZNGA.csv")
              , ("AM.MSFT.csv"), ("AM.AAPL.csv"), ("AM.FB.csv"), ("AM.BABA.csv"), ("AM.TSM.csv")
              , ("AM.V.csv"), ("AM.JPM.csv"), ("AM.JNJ.csv")
              , ("AM.PG.csv"), ("AM.BAC.csv"), ("AM.INTC.csv"), ("AM.VZ.csv"), ("AM.NKE.csv")
              , ("AM.XOM.csv"), ("AM.KO.csv"), ("AM.T.csv"), ("AM.PFE.csv")
              , ("AM.MRK.csv"), ("AM.MS.csv"), ("AM.AAL.csv"), ("AM.GT.csv"), ("AM.UBER.csv")
              , ("AM.AMD.csv"), ("AM.PDD.csv"), ("AM.CVX.csv")]"""
      #the files are located in my python project
          for i in range(len(datalist)):
              data = dataFeed(dataname=datalist[i], timeframe=bt.TimeFrame.Minutes, compression=60)
              cerebro.adddata(data, name=datalist[i])
      
          # Set our desired cash start
          cerebro.broker.setcash(startcash)
      
          # Set the commission
          #cerebro.broker.setcommission(commission=0.0005)
      
          # Add a sizer
          #cerebro.addsizer(bt.sizers.PercentSizer, percents=50)
      
          cerebro.run()
          # Print out the starting conditions
          print('Starting Portfolio Value: %.2f' % startcash)
      
          # Get final portfolio Value
          portvalue = cerebro.broker.getvalue()
          pnl = portvalue - startcash
      
          # Print out the final result
          print('Final Portfolio Value: ${}'.format(portvalue))
          print('P/L: ${}'.format(pnl))
          cerebro.plot()
      
      run-out 1 Reply Last reply Reply Quote 0
      • run-out
        run-out @HexaBraum last edited by

        @hexabraum I think the answer depends on what you are trying to do. Are you trying to test each security independently, eg four securities give you four backtest and four results, or are you trying to test all teh securities at once, in the same portfolio, so four securities equal one backtest.

        Could you try to describe your situation more fully? Thanks.

        H 1 Reply Last reply Reply Quote 1
        • H
          HexaBraum @run-out last edited by

          @run-out 4 securities equal one backtest, however each datafeed gets a technical analysis and therefore buy or sell signals for this exact security. I want the backtrader to backtest my strategy on multiple datafeeds. I hope that i got the point :D

          run-out 1 Reply Last reply Reply Quote 0
          • run-out
            run-out @HexaBraum last edited by

            @hexabraum

            Check out these two links:

            https://backtest-rookies.com/2020/05/09/backtrader-portfolio-rebalancing-with-alpha-vantage/

            https://community.backtrader.com/topic/152/multi-asset-ranking-and-rebalancing/2

            H 1 Reply Last reply Reply Quote 0
            • H
              HexaBraum @run-out last edited by

              @run-out Thanks but that doesn´t really help me. My problem is that the Bollinger Bands only work for my first data feed and also only get plotted for my first datafeed. Therefore the other securities are not in consideration to be bought or sold. Plot.PNG

              1 Reply Last reply Reply Quote 0
              • run-out
                run-out last edited by

                I made some modifications to your code.

                • Removed bollinger band class since it appears you are just using the standard class. You can re-introduce this if I'm mistaken.
                • Removed self.dataclose = self.datas[0].close from init as this is not needed when using multi data.
                • Added in the following loop to create a bollinger band for each data in a dictionary.
                  self.bb_inds = dict()
                        for d in self.datas:
                            bb = bt.ind.BollingerBands(d, period=21, devfactor=2.0,
                                                       movav=bt.ind.MovAv.Simple)
                            self.bb_inds[d] = dict()
                            self.bb_inds[d]["bb_top"] = bb.top
                            self.bb_inds[d]["bb_bot"] = bb.bot
                
                • In next used d in your datas loop to retrieve the appropriate bollinger bands for each data. Changed your data references to d.close[0] where appropriate.
                if not self.position:
                    if d.close[0] < self.bb_inds[d]["bb_bot"][0]:
                
                • Used yahoo daily data for convenience.
                  bollinger.png

                Here's the entire code:

                import backtrader as bt
                import backtrader.feeds as btfeed
                import datetime
                # from datetime import datetime
                
                class dataFeed(btfeed.GenericCSVData):
                    params = (
                        ('dtformat', '%Y-%m-%d %H:%M:%S'),
                        ('datetime', 0),
                        ('open', 1),
                        ('high', 2),
                        ('low', 3),
                        ('close', 4),
                        ('volume', 5),
                        ('openinterest', -1)
                    )
                
                # class BollingerBands(bt.Indicator):
                #     lines = ('topband', 'botband')
                #     params = (('period', 21), ('devfactor', 2.0), ('movav', bt.ind.MovAv.Simple),)
                # 
                #     plotinfo = dict(subplot=False)
                # 
                #     def _plotlabel(self):
                #         plabels = [self.p.period, self.p.devfactor]
                #         plabels += [self.p.movav] * self.p.notdefault('movav')
                #         return plabels
                # 
                #     def __init__(self):
                #         bb = bt.ind.BollingerBands(
                #             period=self.p.period, devfactor=self.p.devfactor, movav=self.p.movav)
                # 
                #         bb.lines.top_band = bb.top
                #         bb.lines.botband = bb.bot
                
                class firstStrategy(bt.Strategy):
                    def log(self, txt, dt=None):
                        ''' Logging function fot this strategy'''
                        dt = dt or self.datas[0].datetime.date(0)
                        print('%s, %s' % (dt.isoformat(), txt))
                
                    def __init__(self):
                
                        # self.dataclose = self.datas[0].close
                
                        self.bb_inds = dict()
                        for d in self.datas:
                            bb = bt.ind.BollingerBands(d, period=21, devfactor=2.0,
                                                       movav=bt.ind.MovAv.Simple)
                            self.bb_inds[d] = dict()
                            self.bb_inds[d]["bb_top"] = bb.top
                            self.bb_inds[d]["bb_bot"] = bb.bot
                
                
                    def notify_order(self, order):
                        if order.status in [order.Submitted, order.Accepted]:
                            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                            return
                
                        # Check if an order has been completed
                        # Attention: broker could reject order if not enough cash
                        if order.status in [order.Completed]:
                            if order.isbuy():
                                self.log('BUY EXECUTED, %.2f' % order.executed.price)
                            elif order.issell():
                                self.log('SELL EXECUTED, %.2f' % order.executed.price)
                
                            self.bar_executed = len(self)
                
                        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                            self.log('Order Canceled/Margin/Rejected')
                
                        # Write down: no pending order
                        #self.order = None
                
                    def next(self):
                        for d in self.datas:
                            dt, dn = self.datetime.date(), d._name
                            pos = self.getposition(d).size
                            # Simply log the closing price of the series from the reference
                            self.log('Close, %.2f' % d.close[0])
                
                        # Check if an order is pending ... if yes, we cannot send a 2nd one
                        #if self.order:
                        #   return
                
                        # Check if we are in the market
                        if not self.position:
                            if d.close[0] < self.bb_inds[d]["bb_bot"][0]:
                                # current close less than previous close
                
                                # if self.dataclose[-1] < self.dataclose[-2]:
                                # previous close less than the previous close
                
                                # BUY, BUY, BUY!!! (with default parameters)
                                self.log('BUY CREATE, %.2f' % d.close[0])
                
                                # Keep track of the created order to avoid a 2nd order
                                self.order = self.buy()
                
                            # Already in the market ... we might sell
                            if d.close[0] > self.bb_inds[d]["bb_top"][0]:
                                # SELL, SELL, SELL!!! (with all possible default parameters)
                                self.log('SELL CREATE, %.2f' % d.close[0])
                
                                # Keep track of the created order to avoid a 2nd order
                                self.order = self.sell()
                
                
                if __name__ == '__main__':
                    # Create a cerebro entity
                    cerebro = bt.Cerebro()
                    startcash = 10000
                
                    # Add a strategy
                    cerebro.addstrategy(firstStrategy)
                    cerebro.addindicator(BollingerBands)
                
                    # datalist = [("AM.ATVI.csv"), ("AM.MO.csv"), ("AM.GM.csv"), ("AM.CL.csv")]
                    # """  , ("AM.FDX.csv"), ("AM.NAKD.csv"), ("AM.NVDA.csv"), ("AM.OCGN.csv"), ("AM.ON.csv")
                    #     , ("AM.PDD.csv"), ("AM.PLUG.csv"), ("AM.QCOM.csv"), ("AM.MDLZ.csv"), ("AM.ADP.csv")
                    #     , ("AM.C.csv"), ("AM.AZN.csv"), ("AM.PEP.csv"), ("AM.ORCL.csv"), ("AM.QTT.csv")
                    #     , ("AM.RUN.csv"), ("AM.SABR.csv"), ("AM.SNDL.csv"), ("AM.TSLA.csv"), ("AM.UAL.csv")
                    #     , ("AM.UXIN.csv"), ("AM.WEN.csv"), ("AM.WFC.csv"), ("AM.YY.csv"), ("AM.ZNGA.csv")
                    #     , ("AM.MSFT.csv"), ("AM.AAPL.csv"), ("AM.FB.csv"), ("AM.BABA.csv"), ("AM.TSM.csv")
                    #     , ("AM.V.csv"), ("AM.JPM.csv"), ("AM.JNJ.csv")
                    #     , ("AM.PG.csv"), ("AM.BAC.csv"), ("AM.INTC.csv"), ("AM.VZ.csv"), ("AM.NKE.csv")
                    #     , ("AM.XOM.csv"), ("AM.KO.csv"), ("AM.T.csv"), ("AM.PFE.csv")
                    #     , ("AM.MRK.csv"), ("AM.MS.csv"), ("AM.AAL.csv"), ("AM.GT.csv"), ("AM.UBER.csv")
                    #     , ("AM.AMD.csv"), ("AM.PDD.csv"), ("AM.CVX.csv")]"""
                    # #the files are located in my python project
                    # for i in range(len(datalist)):
                    #     data = dataFeed(dataname=datalist[i], timeframe=bt.TimeFrame.Minutes, compression=60)
                    #     cerebro.adddata(data, name=datalist[i])
                
                
                    for ticker in [
                        "TSLA",
                        "AAPL",
                        "AMZN",
                      
                    ]:
                        data = bt.feeds.YahooFinanceData(
                            dataname=ticker,
                            timeframe=bt.TimeFrame.Days,
                            fromdate=datetime.date(2020, 1, 1),
                            todate=datetime.date(2020, 12, 31),
                            reverse=False,
                        )
                        cerebro.adddata(data)
                
                    # Set our desired cash start
                    cerebro.broker.setcash(startcash)
                
                    # Set the commission
                    #cerebro.broker.setcommission(commission=0.0005)
                
                    # Add a sizer
                    #cerebro.addsizer(bt.sizers.PercentSizer, percents=50)
                
                    cerebro.run()
                    # Print out the starting conditions
                    print('Starting Portfolio Value: %.2f' % startcash)
                
                    # Get final portfolio Value
                    portvalue = cerebro.broker.getvalue()
                    pnl = portvalue - startcash
                
                    # Print out the final result
                    print('Final Portfolio Value: ${}'.format(portvalue))
                    print('P/L: ${}'.format(pnl))
                    cerebro.plot()
                
                H 1 Reply Last reply Reply Quote 2
                • H
                  HexaBraum @run-out last edited by

                  @run-out Thank you very much, that helps a lot. Now there is still the problem that the buy and sell signals dont really work. It seems like the order is created for stock1 but executed for stock2. When I use your code from above with my CSV Datafiles and i dont log the closing price but only the orders this is my output:

                  2021-02-18, BUY CREATE, 43.76
                  2021-02-18, BUY EXECUTED, 101.03
                  Starting Portfolio Value: 10000.00
                  Final Portfolio Value: $10001.244999999999
                  P/L: $1.2449999999989814

                  Furthermore the buys and sells dont get displayed in the plot. Unfortunately I can´t find the mistake/s.

                  H 1 Reply Last reply Reply Quote 0
                  • H
                    HexaBraum @HexaBraum last edited by

                    @hexabraum I just tested it with more than 2 Stocks and it seems like only the very first stock in the ticker list gets bought or sold even though the order was created for another stock.

                    run-out 1 Reply Last reply Reply Quote 0
                    • run-out
                      run-out @HexaBraum last edited by

                      @hexabraum

                      It seems like the order is created for stock1 but executed for stock2.

                      That can't happen. Orders have a dataline when created and will only execute on that stock.

                      To be honest I didn't look at your order management that closely. What steps have you taken to debug your code?

                      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(); }); }