Developed by veteran technician John Bollinger, Bollinger Bands are a highly popular and versatile tool used to analyze price and volatility. They consist of a set of three lines drawn in relation to an asset’s price, forming a dynamic channel that expands and contracts with market volatility.
Theory: The construction of Bollinger Bands is straightforward yet powerful:
Middle Band: This is typically an N-period
Simple Moving Average (SMA) of the price. While SMA is standard, an
Exponential Moving Average (EMA) can also be used. The common period (N)
for the Middle Band is 20 days.
Upper Band: This band is plotted K standard
deviations above the Middle Band. The standard deviation is also
calculated over N periods.
Lower Band: Correspondingly, this band is
plotted K standard deviations below the Middle Band.
The parameter ‘K’ (the number of standard deviations) is typically set to 2. A crucial feature of Bollinger Bands is their dynamic nature: * When market volatility increases, the N-period standard deviation rises, causing the bands to widen. * When market volatility decreases, the standard deviation falls, leading the bands to narrow or “squeeze.”
This adaptability makes Bollinger Bands a direct visual measure of current market volatility.
Usage: Bollinger Bands offer a multifaceted approach to market analysis:
TA-Lib Function: The Technical Analysis Library (TA-Lib) provides a convenient function for calculating Bollinger Bands:
talib.BBANDS(close_prices, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
close_prices
: An array or series of closing
prices.timeperiod
: The lookback period for the SMA and
standard deviation (default is 20).nbdevup
: The number of standard deviations for the
Upper Band (default is 2).nbdevdn
: The number of standard deviations for the
Lower Band (default is 2).matype
: The type of Moving Average to use for the
middle band. matype=0
(or talib.MA_Type.SMA
)
specifies a Simple Moving Average, which is standard.Code Example (Calculation & Plot with yfinance Data):
The following Python code demonstrates how to fetch stock data using
yfinance
, calculate Bollinger Bands, and plot them
overlapping the price chart.
import yfinance as yf
import talib
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# --- 1. Data Fetching using yfinance ---
= "AAPL" # Example: Apple Inc.
ticker_symbol = "2023-01-01"
data_start_date = "2024-05-01"
data_end_date
try:
# Complying with user preference for yfinance download
= yf.download(ticker_symbol, start=data_start_date, end=data_end_date, auto_adjust=False, progress=False)
data if data.empty:
raise ValueError("No data downloaded")
# Complying with user preference for droplevel
if isinstance(data.columns, pd.MultiIndex) and data.columns.nlevels > 1:
= data.columns.droplevel(level=1)
data.columns except Exception as e:
print(f"Error downloading data for {ticker_symbol}: {e}")
= pd.DataFrame() # Ensure data is an empty DataFrame on error
data
if data.empty or 'Close' not in data.columns or data['Close'].isnull().all():
print(f"Insufficient Close price data found for {ticker_symbol}. Exiting.")
else:
= data['Close'].dropna()
close_prices = close_prices.index
date_index
# --- 2. Bollinger Bands (BBANDS) Calculation ---
= 20 # Standard period for Bollinger Bands
time_period_bb = 2 # Standard deviations for upper/lower bands
nbdev_std_bb
# BBANDS need a minimum number of data points equal to the timeperiod for calculation.
# TA-Lib will output NaNs for the initial period.
if len(close_prices) >= time_period_bb:
= talib.BBANDS(
upper_band, middle_band, lower_band
close_prices,=time_period_bb,
timeperiod=nbdev_std_bb,
nbdevup=nbdev_std_bb,
nbdevdn=0 # 0 = SMA for the middle band
matype
)
print(f"\n--- Bollinger Bands (BBANDS) for {ticker_symbol} ({time_period_bb}-day SMA, {nbdev_std_bb} StdDev) ---")
# Print last 5 valid values for each band
= np.where(~np.isnan(middle_band))[0]
last_valid_idx if len(last_valid_idx) > 0:
= min(5, len(last_valid_idx))
last_idx_to_print print(f"Output Upper Band (last {last_idx_to_print} valid): {upper_band[~np.isnan(upper_band)][-last_idx_to_print:].round(2)}")
print(f"Output Middle Band (last {last_idx_to_print} valid): {middle_band[~np.isnan(middle_band)][-last_idx_to_print:].round(2)}")
print(f"Output Lower Band (last {last_idx_to_print} valid): {lower_band[~np.isnan(lower_band)][-last_idx_to_print:].round(2)}")
else:
print("No valid Bollinger Band values to display.")
# --- 3. Plotting ---
=(14, 7))
plt.figure(figsize=f'{ticker_symbol} Close Price', color='blue', alpha=0.9, linewidth=1.5)
plt.plot(date_index, close_prices, label=f'Upper Band ({nbdev_std_bb}σ)', color='red', linestyle='--', alpha=0.7, linewidth=1)
plt.plot(date_index, upper_band, label=f'Middle Band (SMA {time_period_bb})', color='orange', linestyle='-', alpha=0.7, linewidth=1.2)
plt.plot(date_index, middle_band, label=f'Lower Band ({nbdev_std_bb}σ)', color='green', linestyle='--', alpha=0.7, linewidth=1)
plt.plot(date_index, lower_band, label
# Fill between the bands for visual appeal
='grey', alpha=0.15, label='Bollinger Channel')
plt.fill_between(date_index, upper_band, lower_band, color
f'{ticker_symbol} Price with Bollinger Bands')
plt.title('Date')
plt.xlabel('Price')
plt.ylabel(
plt.legend()True, linestyle=':', alpha=0.5)
plt.grid(
plt.show()
else:
print(f"\nSkipping Bollinger Bands plot for {ticker_symbol}: Insufficient data (need >= {time_period_bb} points).")
if not close_prices.empty:
print(f"Available data points after cleaning: {len(close_prices)}.")
Explanation of the Code:
yfinance
,
talib
, numpy
, matplotlib.pyplot
,
and pandas
.yf.download()
fetches historical data. Your preferences
for auto_adjust=False
and droplevel
are
applied. Basic error handling for the download is included.close_prices
are extracted and any NaN
values are dropped.time_period_bb
(e.g., 20) and nbdev_std_bb
(e.g., 2) are set.if len(close_prices) >= time_period_bb:
ensures enough data for TA-Lib.talib.BBANDS()
calculates the upper, middle, and lower
bands. matype=0
ensures the middle band is an SMA.plt.figure()
creates the plot.plt.fill_between()
is used to shade the area between
the upper and lower bands, visually highlighting the volatility
channel.plt.show()
displays the chart.Bollinger Bands provide a dynamic and insightful way to frame price action, helping traders identify periods of high and low volatility, potential overextensions, and breakout opportunities.