For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

BollingerBands Squeeze



  • If anyone needs this, here is a BBSqueeze indicator.

    implementation is based on this: https://www.netpicks.com/squeeze-out-the-chop/

    BBSqueeze

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    import backtrader as bt
    
    from . import KeltnerChannel
    
    
    class BBSqueeze(bt.Indicator):
    
        '''
        https://www.netpicks.com/squeeze-out-the-chop/
    
        Both indicators are symmetrical, meaning that the upper and lower bands or channel lines are the same distance from the moving average. That means that we can focus on only one side in developing our indicator. In our case, we’ll just consider the upper lines.
    
        The basic formulas we need are:
    
            Bollinger Band = Moving Average + (Number of standard deviations X Standard Deviation)
            Keltner Channel = Moving Average + (Number of ATR’s X ATR)
    
        Or if we translate this into pseudo-code:
    
            BBUpper = Avg(close, period) + (BBDevs X StdDev(close, period))
            KCUpper = Avg(close, period) + (KCDevs X ATR(period))
    
        The squeeze is calculated by taking the difference between these two values:
    
            Squeeze = BBUpper – KCUpper
    
        Which simplifies down to this:
    
            Squeeze = (BBDevs X StdDev(close, period)) – (KCDevs X ATR(period))
        '''
    
        lines = ('squeeze',)
        params = (('period', 20), ('devfactor', 2.0), ('movav', bt.ind.MovAv.Simple),)
    
        plotinfo = dict(subplot=True)
    
        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)
            kc = KeltnerChannel(
                period=self.p.period, devfactor=self.p.devfactor, movav=self.p.movav)
            self.lines.squeeze = bb.top - kc.top
    

    KeltnerChannel

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    import backtrader as bt
    
    
    class KeltnerChannel(bt.Indicator):
    
        lines = ('mid', 'top', 'bot',)
        params = (('period', 20), ('devfactor', 1.5),
                  ('movav', bt.ind.MovAv.Simple),)
    
        plotinfo = dict(subplot=False)
        plotlines = dict(
            mid=dict(ls='--'),
            top=dict(_samecolor=True),
            bot=dict(_samecolor=True),
        )
    
        def _plotlabel(self):
            plabels = [self.p.period, self.p.devfactor]
            plabels += [self.p.movav] * self.p.notdefault('movav')
            return plabels
    
        def __init__(self):
            self.lines.mid = ma = self.p.movav(self.data, period=self.p.period)
            atr = self.p.devfactor * bt.ind.ATR(self.data, period=self.p.period)
            self.lines.top = ma + atr
            self.lines.bot = ma - atr
    


  • BBSqueeze with configureable multiplicators for bb and keltner

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    
    import backtrader as bt
    
    from . import KeltnerChannel
    
    
    class BBSqueeze(bt.Indicator):
    
        '''
        https://www.netpicks.com/squeeze-out-the-chop/
    
        Both indicators are symmetrical, meaning that the upper and lower bands or channel lines are the same distance from the moving average. That means that we can focus on only one side in developing our indicator. In our case, we’ll just consider the upper lines.
    
        The basic formulas we need are:
    
            Bollinger Band = Moving Average + (Number of standard deviations X Standard Deviation)
            Keltner Channel = Moving Average + (Number of ATR’s X ATR)
    
        Or if we translate this into pseudo-code:
    
            BBUpper = Avg(close, period) + (BBDevs X StdDev(close, period))
            KCUpper = Avg(close, period) + (KCDevs X ATR(period))
    
        The squeeze is calculated by taking the difference between these two values:
    
            Squeeze = BBUpper – KCUpper
    
        Which simplifies down to this:
    
            Squeeze = (BBDevs X StdDev(close, period)) – (KCDevs X ATR(period))
        '''
    
        lines = ('squeeze',)
        params = (('period', 20), ('bbdevs', 2.0), ('kcdevs', 1.5), ('movav', bt.ind.MovAv.Simple),)
    
        plotinfo = dict(subplot=True)
    
        def _plotlabel(self):
            plabels = [self.p.period, self.p.bbdevs, self.p.kcdevs]
            plabels += [self.p.movav] * self.p.notdefault('movav')
            return plabels
    
        def __init__(self):
            bb = bt.ind.BollingerBands(
                period=self.p.period, devfactor=self.p.bbdevs, movav=self.p.movav)
            kc = KeltnerChannel(
                period=self.p.period, devfactor=self.p.kcdevs, movav=self.p.movav)
            self.lines.squeeze = bb.top - kc.top
    
    


  • Thanks for sharing this @dasch.

    Flipping positions between BBands is a part of my primary strategy whilst waiting for breakouts to occur. I've been using price action relative to dynamic highs and lows to identify breakouts from choppy zones.

    I'll see how introducing the Keltner Channel performs.

    M


Log in to reply
 

});