def bollinger_bands(df, bollinger_period_sd_ranges=[(20, 2)]):
    """
    Bollinger Bands (including %B and Bandwidth)

    Parameters
    ----------
    df : pandas.DataFrame, must include columns ['High', 'Low', 'Close]
     Dataframe where the bollinger_bands is extracted from

    bollinger_period_sd_ranges : list, default [(20,2)]
     List of (period, standard_deviation) to be extracted

    Return
    ------
    df : pandas.DataFrame
     Dataframe with Bollinger Bands
    """
    
    df = df.copy()
    df["TypicalPrice"] = ((df["High"] + df["Low"] + df["Close"]) / 3)
    for (period, sd) in bollinger_period_sd_ranges:
        ma_typical_price = df["TypicalPrice"].rolling(window=period).mean()
        sd_typical_price = ma_typical_price.rolling(window=period).std(ddof=1)
        df[f"UpperBollinger{period}"] = ma_typical_price + sd * sd_typical_price
        df[f"LowerBollinger{period}"] = ma_typical_price - sd * sd_typical_price
        df[f"%B{period}"] = ((df["Close"] - df[f"LowerBollinger{period}"]) /
                             (df[f"UpperBollinger{period}"] - df[f"LowerBollinger{period}"]))
        df[f"Bandwidth{period}"] = (df[f"UpperBollinger{period}"] - df[f"LowerBollinger{period}"]) / ma_typical_price
    df = df.drop(columns=[f"TypicalPrice"])

    return df