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 with dynamically changing data

    Indicators/Strategies/Analyzers
    2
    5
    1598
    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.
    • M
      momentum last edited by

      Hi,

      I am really new to backtrader, so apologies if this is trivial.

      If I create a custom dummy indicator, I can change its data feeds dynamically inside the strategy and if I run the strategy with runonce=False, it works fine (look below for the code). However, if I use an existing indicator instead e.g. bt.indicators.SimpleMovingAverage, the same approach does not work.

      Many thanks for your help.

      class DynamicIndicator(bt.Indicator):
          lines = ('indicator1',)
      
          def next(self):
              self.lines.indicator1[0] = self.datas[0][0]
      
      class TestStrategy(bt.Strategy):
          def __init__(self):
          self.ind = DynamicIndicator(self.datas[1].lines.close)
      
          def next(self):
              if <condition>:
                  self.ind.datas[0] = self.datas[2].lines.close
      
      1 Reply Last reply Reply Quote 0
      • B
        backtrader administrators last edited by

        The indicators use a generic notation to refer to the data with which they operate and don't care about indexing the array.

        In any case I feel you are using the wrong approach imho. Instead of changing the data feeds, you should change what the data feed delivers, which at the end of the day delivers the same result, but is compatible with any way an indicator can be written.

        1 Reply Last reply Reply Quote 0
        • M
          momentum last edited by

          Many thanks for the reply and the help.

          Do you mean instead of changing the data of the indicator, change the data feed itself?

          I tried that below and although the data feed changes, the moving average is not updated.

          from __future__ import (absolute_import, division, print_function,
                                  unicode_literals)
          import backtrader as bt
          import pandas as pd
          import datetime
          
          # Create a Strategy
          class TestStrategy(bt.Strategy):
          
              params = (
                  ('maperiod', 2),
              )
          
              def log(self, txt, dt=None):
                  ''' Logging function for this strategy'''
                  dt = dt or self.datas[0].datetime.date(0)
                  print('%s, %s' % (dt.isoformat(), txt))
          
              def __init__(self):
                  self.ind = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)
          
              def next(self):
                  if self.datas[0].datetime.date(0) == datetime.datetime(2018, 1, 6).date():
                      self.datas[0] = self.datas[1]
                      # del self.datas[0]
                  self.log("{}, {}".format(self.datas[0].lines.close[0], self.ind.lines.sma[0]))
          
          
          if __name__ == '__main__':
              # Create a cerebro entity
              cerebro = bt.Cerebro()
          
              # Add a strategy with parameters
              cerebro.addstrategy(TestStrategy, maperiod=2)
          
              df1 = pd.DataFrame({'Close': list(range(1,11))}, index=pd.date_range(start='1/1/2018', periods=10))
              df2 = pd.DataFrame({'Close': list(range(2,12))}, index=pd.date_range(start='1/1/2018', periods=10))
              cerebro.adddata(bt.feeds.PandasData(dataname=df1, nocase=True))
              cerebro.adddata(bt.feeds.PandasData(dataname=df2, nocase=True))
          
              # Set our desired cash start
              cerebro.broker.setcash(100000.0)
          
              # Print out the starting conditions
              print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
          
              # Run over everything
              cerebro.run(runonce=False, preload=False)
          
              # Print out the final result
              print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
          
          Starting Portfolio Value: 100000.00
          2018-01-02, 2.0, 1.5
          2018-01-03, 3.0, 2.5
          2018-01-04, 4.0, 3.5
          2018-01-05, 5.0, 4.5
          2018-01-06, 7.0, 5.5
          2018-01-07, 8.0, 6.5
          2018-01-08, 9.0, 7.5
          2018-01-09, 10.0, 8.5
          2018-01-10, 11.0, 9.5
          Final Portfolio Value: 100000.00
          
          B 1 Reply Last reply Reply Quote 0
          • B
            backtrader administrators @momentum last edited by

            @momentum said in indicator with dynamically changing data:

            Do you mean instead of changing the data of the indicator, change the data feed itself?

            No, I said that you create a data feed which dynamically changes the data it serves.

            You fail to understand that the self.datas array in each object is a different one. This is obvious if you consider that you can do this

            ma_on_ma = bt.ind.SMA(bt.ind.SMA(self.data, period=5), period=5)
            

            The outer SMA has, obviously, only 1 data feed which happens to be another SMA (the inner one)

            Futhermore, you expect things to use the array self.datas and you manipulate it with that expectation. As noted above, this is not supported, given that other notations and references are used in the internals.

            1 Reply Last reply Reply Quote 0
            • M
              momentum last edited by

              I am trying to generate a dynamically changing data feed based on the RollOver class but it does not work always correctly when trying to update the history at the same time (self.params.update_history_upon_roll set to True). For example when moving to a data feed with longer history it gives wrong results. Any ideas?

              Thanks

                  def _roll(self):
                      if self._ds:
                          clock1 = self.lines[0].lencount
                          self._dexp = self._d
                          self._d = self._ds.pop(0)
                          self._dts.pop(0)
                          clock3 = self._d.lines[0].lencount
                          if self.params.update_history_upon_roll:
                              if clock3 < clock1:
                                  self._d.lines.rewind()
                              for i in range(len(self.lines.lines)):
                                  self.lines[i].array = self._d.lines[i].array[:]
                                  self.lines[i].lencount = self._d.lines[i].lencount
                                  self.lines[i].idx = self._d.lines[i].idx
                              if clock3 < clock1:
                                  self.lines.advance()
                          return True
                      else:
                          return False
              
                  def _load(self):
                      while self._d is not None:
                          if self._doroll:
                              self._roll()
                              self._doroll = False
                          _next = self._d.next()
                          if _next is None:  # no values yet, more will come
                              continue
                          if _next is False:  # no values from current data src
                              if self._ds:
                                  self._d = self._ds.pop(0)
                                  self._dts.pop(0)
                              else:
                                  self._d = None
                              continue
              
                          dt0 = self._d.datetime.datetime()  # current dt for active data
              
                          # Synchronize other datas using dt0
                          for i, d_dt in enumerate(zip(self._ds, self._dts)):
                              d, dt = d_dt
                              while dt < dt0:
                                  if d.next() is None:
                                      continue
                                  self._dts[i] = dt = d.datetime.datetime()
              
                          # Move expired future as much as needed
                          while self._dexp is not None:
                              if not self._dexp.next():
                                  self._dexp = None
                                  break
              
                              if self._dexp.datetime.datetime() < dt0:
                                  continue
              
                          if self._dexp is None and self._checkdate(dt0, self._d):
                              # rule has been met ... check other factors only if 2 datas
                              # still there
                              if self._ds and self._checkcondition(self._d, self._ds[0]):
                                  if self.params.upon_condition:
                                      self._roll()
                                  else:
                                      self._doroll = True
              
                          # Fill the line and tell we die
                          for i in range(len(self.lines.lines)):
                              self.lines[i][0] = self._d.lines[i][0]
              
                          return True
              
                      # Out of the loop -> self._d is None, no data feed to return from
                      return False
              
              1 Reply Last reply Reply Quote 0
              • 1 / 1
              • First post
                Last post
              Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors