Crude Swing Indicator help



  • 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.


  • @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.


  • administrators

    @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:

    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
    


  • @RandyT @backtrader

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


  • administrators

    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.


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.