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/

    Crude Swing Indicator help

    General Code/Help
    3
    5
    2425
    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.
    • T
      ThatBlokeDave last edited by ThatBlokeDave

      Hello All,
      I want to start building an algorithm to detect swing highs / lows and plot them on the chart. Having looked through the docs I have a couple of questions which I think may just be due to a lack of programming experience on my part.

      Below is a very crude starting point. My aim below is to get a basic understanding of how to plot on the chart and review multiple periods backwards.

      
      class SwingInd(bt.Indicator):
          lines = ('swingline',)
      
          def __init__(self):
              self.addminperiod(7)
      
          def next(self):
              #crude swing up
              highs = self.data.lines.high
              lows = self.data.lines.low
              if all([highs[-3] > i for i in [highs[-0],highs[-1],highs[-2], highs[-4],highs[-5],highs[-6]]]):
                  self.lines.swingline[-3] = 1
              elif all([lows[-3] < i for i in [lows[-0],lows[-1],lows[-2],lows[-4],lows[-5],lows[-6]]]):
                  self.lines.swingline[-3] = 0
              else:
                  self.lines.swingline[-3] = 0.5
      

      My questions are:

      • Is it possible to slice the lines array? I get an error when I try to do it. I would like to tidy up the long list of individual index references (high[-1], high[-2] etc)
      • Is it possible to plot the indicator as arrows on the main chart? I saw a tutorial regarding how to change the style of the buySell observers arrows but I was not sure if how to apply that to an indicator.
      1 Reply Last reply Reply Quote 0
      • RandyT
        RandyT last edited by RandyT

        @ThatBlokeDave

        A few hints here. Someone else may have some other specifics.

        1. You can probably do most of what you are trying to do in the __init__() phase.
        2. When referencing different indexes while in __init__(), use self.data.high(-3) (note the parens instead of brackets)
        3. There are also a number of basic tools available, for example to get the Highest value over some period. (bt.indicators.Highest(self.data.high, period=self.p.period) )

        In my experience, I can never spend enough time looking at all of the existing examples in the backtrader/indicators directory. I always learn something new.

        1 Reply Last reply Reply Quote 2
        • B
          backtrader administrators last edited by backtrader

          @RandyT is pointing out at the canonical way of writing indicators. Canonical in the sense that those ideas are what motivated backtrader (at the end of the day the user is king)

          Is it possible to slice the lines array? I get an error when I try to do it. I would like to tidy up the long list of individual index references (high[-1], high[-2] etc)

          No. The reason is that [0] is the present, [-1] is the past and [1] is the future (for example for the Ichimoku indicator). See the following example for slicing:

          lasthighs = self.data.high[3:-1]
          

          That's perfectly acceptable in Python but within the realm of backtrader it means ... "give me something starting from the future and ending in the past. And to start with there is nothing in the future (the note about Ichimoku is about projecting values into the future and not because the values are there)

          Can something be done? Yes. To cover those use cases where an array is needed:

          • self.data.high.get(ago=0, size=3)

            • ago indicates where to start (looking backwards) fetching data. 0 is now, -1 is the previous bar, -2 is two bars ago
            • size how many items to return

          Is it possible to plot the indicator as arrows on the main chart? I saw a tutorial regarding how to change the style of the buySell observers arrows but I was not sure if how to apply that to an indicator.

          The declarations for plotting are the same for observers and indicators. The main documentation is here: Docs - Plotting Look for plotinfo and plotlines.

          Some examples to decide how to best plot your lines and on which scale:

          • Source - Parabolic SAR

            Plots on the data.

          • The markers for the BuySell observer can be seen in the source for it and which markers actually plot arrows in the blog post about

            Source - BuySell Observer
            Blog - Arrows for the BuySell Observer

          BUT - Your indicators delivers values of 1.0, 0.5 and 0 and that scale is for sure not going to be compatible with the scale of the data. It doesn't make sense to plot on the data unless you scale the values to properly fit the scale of the data.

          A quick canonical (non-tested and typo-prone) implementation ... but take into account that two values could have the same high or the same low

          (Delivering 1.0, 0 and -1.0 which makes it usable as a signal)

          class SwingInd(bt.Indicators)
              lines = ('swingline',)
          
              params = (('period', 7),)
          
              def __init__(self):
                  hhigh = bt.ind.Highest(self.data.high, period=self.p.period)
                  llow = bt.ind.Lowest(self.data.low, period=self.p.period)
          
                  hswing = hhigh == self.data.high(-self.p.period // 2)
                  lswing = llow == self.data.low(-self.p.period // 2)
          
                  # (period) to shift the result back
                  self.lines.swingline = bt.If(hswing, 1.0, bt.If(lswing, -1.0, 0))(self.p.period // 2) 
          

          bt.If is needed because there is no way to override python if statement.

          Your implementation with some get

          class SwingInd(bt.Indicator):
              lines = ('swingline',)
          
              def __init__(self):
                  self.addminperiod(7)
          
              def next(self):
                  #crude swing up
                  highs = self.data.high.get(size=7)
                  lows = self.data.low.get(size=7)
          
                  if highs.pop(3) > max(highs):
                      self.lines.swingline[-3] = 1
                  elif lows.pop(3) > min(lows):
                      self.lines.swingline[-3] = 0
                  else:
                      self.lines.swingline[-3] = 0.5
          
          1 Reply Last reply Reply Quote 1
          • T
            ThatBlokeDave last edited by

            @RandyT @backtrader

            Amazing... Thanks for the insights and help. Really appreciate the time you have taken to give such comprehensive answers.

            1 Reply Last reply Reply Quote 0
            • B
              backtrader administrators last edited by

              Reconsidering this Indicator, it should probably be called something else like Study, Analysis, Post-Analysis, Forensics ...

              The reason is that the actual produced value is being stored at in the past -3 (or -self.p.period // 2) instead of at 0.

              For an analysis with frozen data the code indicates where the swing points are, but as a practical indicator, it should point out at position 0 that something happened which has to be acted upon ... now.

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