Calculating Bollinger Band Correctly

  • My bollinger band comes out like the below, which doesn't seem right. Any idea what is wrong with my code for calculating upper and lower bollinber bands?

    I obtained my data from here

    start, end = dt.datetime(1976, 1, 1), dt.datetime(2013, 12, 31)
    sp = web.DataReader('^GSPC','yahoo', start, end) 
    here are my bollinger calculations
    

    calculation for bollinger band

    ave = pd.stats.moments.rolling_mean(self[name], window) 
    std = pd.stats.moments.rolling_std(self[name], window)
    self['upper'] = ave + (2 * std)
    self['lower'] = ave - (2 * std)
    

    enter image description here

    I am not into python but looks like that your average (ave) time series does not look right in relation to "SP", at least ave does not converge with "SP".

    Perhaps try to plot `ave` to see where it is, and perhaps on a different window plot `std` to see its size. Providing a full code might be helpful as well.

    @Bach I second the recommendation on providing more code (or perhaps a simplified version that can reproduce the plot from scratch).

    Seems like the rolling Standard deviation for the $lower$ band is slightly lagging the $SP$ ... Make sure the rolling window is the same for both the upper & lower bands

  • In Pandas 0.19.2++:

    def Bolinger_Bands(stock_price, window_size, num_of_std):
    
        rolling_mean = stock_price.rolling(window=window_size).mean()
        rolling_std  = stock_price.rolling(window=window_size).std()
        upper_band = rolling_mean + (rolling_std*num_of_std)
        lower_band = rolling_mean - (rolling_std*num_of_std)
    
        return rolling_mean, upper_band, lower_band
    
    def main():
    
         price_series = get_data(ticker, dates) # it is a Pandas series...
    
         rolling_avg_price, upper_band, lower_band = Bolinger_Bands(price_series, 20, 2)
    
         do_other_processing(rolling_avg_price, upper_band, lower_band)
         ...
    

    the correct name is Bollinger

  • def bbands(price, length=30, numsd=2):
        """ returns average, upper band, and lower band"""
        ave = pd.stats.moments.rolling_mean(price,length)
        sd = pd.stats.moments.rolling_std(price,length)
        upband = ave + (sd*numsd)
        dnband = ave - (sd*numsd)
        return np.round(ave,3), np.round(upband,3), np.round(dnband,3)
    
    sp['ave'], sp['upper'], sp['lower'] = bbands(sp.Close, length=30, numsd=1)
    sp= sp[-200:]
    sp.plot()
    

    enter image description here

    Pandas stats deprecated. I used ave = price.rolling(window=20, center=False).mean() and sd = price.rolling(window=20,center=False).std()

  • I believe that the answers given here are incorrect as they return the sample standard deviation while the the population measure is the correct calculation for Bollinger Bands. The bands usign the sample calc will be too wide. Pandas does not appear to allow a choice between the sample and population calculations for either solution presented here.

    sd = pd.stats.moments.rolling_std(price,length)
    rolling_std  = stock_price.rolling(window=window_size).std()
    

    Numpy does allow a choice, so it should be used until a proper pandas solution is presented.

    a = np.array([1,2,3,4,5])
    print np.std(a, ddof=1) # sample
    print np.std(a, ddof=0) # population
    >>> 
    1.58113883008
    1.41421356237
    >>> 
    

    https://docs.scipy.org/doc/numpy/reference/generated/numpy.std.html

    John Bollinger, CFA, CMT

    Well, it appears that pandas has caught up with me. This works correctly now.

    b = pd.DataFrame([1,2,3,4,5])
    print b.rolling(window=5).std() # sample
    print b.rolling(window=5).std(ddof=0) # population
    

    This is correct. Should be based on population. Was trying to figure out why my calculation wouldn't match tradingview, and this was why.

  • Try to plot the rolling mean against your quotes for SP and see if it makes sense. Although you line of code to compute the rolling mean is correct, there might be something wrong in the data that you pass as input.

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM