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/

    Custom indicator, how datas work in init

    Indicators/Strategies/Analyzers
    2
    5
    619
    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
      marsario last edited by

      Hi,

      I'm trying to understand how to build a custom indicator. Let's see this one:

      import backtrader as bt
      class DummyDifferenceDivider(bt.Indicator):
          lines = ('diffdiv',)  # output line (array)
          params = (
              ('period', 1),  # distance to previous data point
              ('divfactor', 2.0),  # factor to use for the division
          )
          def __init__(self):
              diff = self.data(0) - self.data(-self.p.period)
              diffdiv = diff / self.p.divfactor
              self.lines.diffdiv = diffdiv
      

      (source: https://medium.com/@danjrod/custom-indicator-development-in-python-with-backtrader-bc775552dc3e by Daniel Rodriguez)

      What does this line do?

              diff = self.data(0) - self.data(-self.p.period)
      

      Daniel writes it should be self-explanatory, but i don't understand it.

      It seems to me that he's subtracting two datafeeds (but this is impossible as I tried to run the code with only one datafeed and it runs). At first, I thought that it was subtracting the closing value of today and today-period, but I don't understand why it's not written as:

              diff = self.datas[0].close[0] - self.datas[0].close[-self.p.period]
      

      Thanks for any insights...

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

        @marsario
        When doing calculations on the whole line at once, you use round brackets and typically use the formula in the init method. In the line below you are moving the whole line back -self.p.period and getting in return diff which is a whole new line.

         diff = self.data(0) - self.data(-self.p.period)
        

        In the statement below we use an individual line from the dataline, in this case close and we get once piece of data in return, typically used in next. So in this case, you are subtracting: the value for self.datas[9].close[0] at the current bar and less self.datas[0].close[-self.p.period] -self.p.period's ago. This will yield one number, a scalar, and can be used in the current bar.

        diff = self.datas[0].close[0] - self.datas[0].close[-self.p.period]
        

        RunBacktest.com

        M 1 Reply Last reply Reply Quote 1
        • M
          marsario @run-out last edited by

          @run-out

          Wow, it's really difficult for me to understand...

          I tried a different test. This is the indicator's code:

          class hasGrown(bt.Indicator):
              lines = ('hasgrown',)  # output line (array)
              
              def __init__(self):
                  self.lines.hasgrown = self.data(0) > self.data(-1)
          

          and this is the strategy:

          class buyifgrewStrategy(bt.Strategy):   
              def __init__(self):
                  print("Starting strategy")
                  self.hasgrown = hasGrown()
                  
              def next(self):
                  if self.hasgrown:
                      if not self.position:
                          calculateSize(self)
                          self.buy(self.data,size=self.size)
                          print("Buying")
                  else:
                      if self.position:
                          self.close(self.data)
                          print("Selling")
                  log(self,f"Today: {self.data.close[0]}. Yesterday: {self.data.close[-1]}")
          

          So, in the indicator I'm checking whether the stock has price has grown yesterday (and if so I buy). If it has decreased I sell.

          The code apperently works, but I don't understand why.

          What am I comparing here? (open prices? close prices? all of them?)

                  self.lines.hasgrown = self.data(0) > self.data(-1)
          

          The print output:

          ...
          2021-02-16, Today: 133.19. Yesterday: 135.37
          2021-02-17, Today: 130.84. Yesterday: 133.19
          2021-02-18, Today: 129.71. Yesterday: 130.84
          Buying
          2021-02-19, Today: 129.87. Yesterday: 129.71
          Selling
          2021-02-22, Today: 126.0. Yesterday: 129.87
          2021-02-23, Today: 125.86. Yesterday: 126.0
          2021-02-24, Today: 125.35. Yesterday: 125.86
          2021-02-25, Today: 120.99. Yesterday: 125.35
          Buying
          2021-02-26, Today: 121.26. Yesterday: 120.99
          2021-03-01, Today: 127.79. Yesterday: 121.26
          Selling
          2021-03-02, Today: 125.12. Yesterday: 127.79
          2021-03-03, Today: 122.06. Yesterday: 125.12
          2021-03-04, Today: 120.13. Yesterday: 122.06
          Buying
          2021-03-05, Today: 121.42. Yesterday: 120.13
          Strategy is over, final cash: 122076.85999999993
          
          run-out 1 Reply Last reply Reply Quote 0
          • run-out
            run-out @marsario last edited by

            @marsario said in Custom indicator, how datas work in init:

            self.lines.hasgrown = self.data(0) > self.data(-1)

            Backtrader has a number of shortcut conventions that make life easier, but also more confusing for the uninitiated. When the line is not indicated a default line will be chosen. In your case above, that would be close.

            Your ohlcv data lines are stored in a list called datas. It's really that simple. So self.datas[0] is the first ohlcv data line in the list, since in python [0] gets you the first in a list. self.datas[1] give you the second ohlcv line in that list, and so on. If you omit the [0] component as in self.data then the first ohlcv data line in the list is selected for you.

            I personally find these very confusing and opt when I code to use the full and formal written version, eg the following in next:

            def next(self):
                self.datas[0].close[0]
            

            Which translates into the first ohlcv data line, closing value, at the current bar. For comparison,

            def next(self):
                self.datas[2].volume[-4]
            

            This would give me the third ohlcv dataline in my list, retuning the volume, 4 bars ago.

            If I wanted to use the whole line in init to create new indicator lines, then I would still use square brackets to select my line, but use round brackes to identify bar position. eg:

            def __init__(self):
                self.newindicatorline = self.datas[1].high(0) -self.datas[2].low(-15)
            

            This would create a new line that would have a value at each bar that is the high of ohlcv line 1 - the low 15 periods ago of ohlcv line 2. If you want to use this new indicator in next:

            def next(self):
                new_ind_current_bar = self.newindicatorline[0]
                new_ind_last_bar = self.newindicatorline[-1]
            

            RunBacktest.com

            M 1 Reply Last reply Reply Quote 2
            • M
              marsario @run-out last edited by

              @run-out Amazing! Thanks a lot! All clear!

              1 Reply Last reply Reply Quote 1
              • 1 / 1
              • First post
                Last post
              Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors