BBI
References
Definition
BBI (Bull and Bear Index) is an indicator aims on measuring the general short/mid-term (< 1 month) trend and sentiment of the stock/market. It used an average of 4 SMAs (3, 6, 12, 24) as a cut-off of a bullish / bearish trend .
BBI Bollinger Bands uses BBI as “basis” and calculates variations (Stdev) of BBI during the past several days. In general, BBI Boll band is more volatile than the traditional Boll Band.
Read the indicator
- BUY: close> BBI
- SELL: close < BBI
Load basic packages
import pandas as pd
import numpy as np
import os
import gc
import copy
from pathlib import Path
from datetime import datetime, timedelta, time, date
#this package is to download equity price data from yahoo finance
#the source code of this package can be found here: https://github.com/ranaroussi/yfinance/blob/main
import yfinance as yf
pd.options.display.max_rows = 100
pd.options.display.max_columns = 100
import warnings
warnings.filterwarnings("ignore")
import pytorch_lightning as pl
random_seed=1234
pl.seed_everything(random_seed)
Global seed set to 1234
1234
#S&P 500 (^GSPC), Dow Jones Industrial Average (^DJI), NASDAQ Composite (^IXIC)
#Russell 2000 (^RUT), Crude Oil Nov 21 (CL=F), Gold Dec 21 (GC=F)
#Treasury Yield 10 Years (^TNX)
#benchmark_tickers = ['^GSPC', '^DJI', '^IXIC', '^RUT', 'CL=F', 'GC=F', '^TNX']
benchmark_tickers = ['^GSPC']
tickers = benchmark_tickers + ['GSK', 'NVO', 'AROC']
#https://github.com/ranaroussi/yfinance/blob/main/yfinance/base.py
# def history(self, period="1mo", interval="1d",
# start=None, end=None, prepost=False, actions=True,
# auto_adjust=True, back_adjust=False,
# proxy=None, rounding=False, tz=None, timeout=None, **kwargs):
dfs = {}
for ticker in tickers:
cur_data = yf.Ticker(ticker)
hist = cur_data.history(period="max", start='2000-01-01')
print(datetime.now(), ticker, hist.shape, hist.index.min(), hist.index.max())
dfs[ticker] = hist
2022-09-10 21:34:31.097535 ^GSPC (5710, 7) 1999-12-31 00:00:00 2022-09-09 00:00:00
2022-09-10 21:34:31.431293 GSK (5710, 7) 1999-12-31 00:00:00 2022-09-09 00:00:00
2022-09-10 21:34:31.831783 NVO (5710, 7) 1999-12-31 00:00:00 2022-09-09 00:00:00
2022-09-10 21:34:32.114387 AROC (3791, 7) 2007-08-21 00:00:00 2022-09-09 00:00:00
ticker = 'GSK'
dfs[ticker].tail(5)
| Open | High | Low | Close | Volume | Dividends | Stock Splits | |
|---|---|---|---|---|---|---|---|
| Date | |||||||
| 2022-09-02 | 31.600000 | 31.969999 | 31.469999 | 31.850000 | 8152600 | 0.0 | 0.0 |
| 2022-09-06 | 31.650000 | 31.760000 | 31.370001 | 31.469999 | 5613900 | 0.0 | 0.0 |
| 2022-09-07 | 31.209999 | 31.590000 | 31.160000 | 31.490000 | 4822000 | 0.0 | 0.0 |
| 2022-09-08 | 30.910000 | 31.540001 | 30.830000 | 31.510000 | 6620900 | 0.0 | 0.0 |
| 2022-09-09 | 31.950001 | 31.969999 | 31.730000 | 31.889999 | 3556800 | 0.0 | 0.0 |
Define BBI calculation function
def BBI(CLOSE,M1=3,M2=6,M3=12,M4=20): #BBI多空指标
return (MA(CLOSE,M1)+MA(CLOSE,M2)+MA(CLOSE,M3)+MA(CLOSE,M4))/4
def cal_bbi(ohlc: pd.DataFrame,
m1_period: int = 3,
m2_period: int = 6,
m3_period: int = 12,
m4_period: int = 20,
column: str = "close") -> pd.Series:
"""
BBI (Bull and Bear Index) is an indicator aims on measuring the general short/mid-term (< 1 month) trend
and sentiment of the stock/market.
It used an average of 4 SMAs (3, 6, 12, 24) as a cut-off of a bullish / bearish trend .
BUY: close> BBI
SELL: close<BBI
"""
c = ohlc[column]
m1 = c.rolling(m1_period).mean()
m2 = c.rolling(m2_period).mean()
m3 = c.rolling(m3_period).mean()
m4 = c.rolling(m4_period).mean()
return pd.Series((m1+m2+m3+m4)/4, name='BBI')
Calculate BBI
df = dfs[ticker][['Open', 'High', 'Low', 'Close', 'Volume']]
df = df.round(2)
help(cal_bbi)
Help on function cal_bbi in module __main__:
cal_bbi(ohlc: pandas.core.frame.DataFrame, m1_period: int = 3, m2_period: int = 6, m3_period: int = 12, m4_period: int = 20, column: str = 'close') -> pandas.core.series.Series
BBI (Bull and Bear Index) is an indicator aims on measuring the general short/mid-term (< 1 month) trend
and sentiment of the stock/market.
It used an average of 4 SMAs (3, 6, 12, 24) as a cut-off of a bullish / bearish trend .
BUY: close> BBI
SELL: close<BBI
df_ta = cal_bbi(df, column="Close")
df = df.merge(df_ta, left_index = True, right_index = True, how='inner' )
del df_ta
gc.collect()
80
from core.finta import TA
help(TA.BBANDS)
Help on function BBANDS in module core.finta:
BBANDS(ohlc: pandas.core.frame.DataFrame, period: int = 20, MA: pandas.core.series.Series = None, column: str = 'close', std_multiplier: float = 2) -> pandas.core.frame.DataFrame
Developed by John Bollinger, Bollinger Bands® are volatility bands placed above and below a moving average.
Volatility is based on the standard deviation, which changes as volatility increases and decreases.
The bands automatically widen when volatility increases and narrow when volatility decreases.
This method allows input of some other form of moving average like EMA or KAMA around which BBAND will be formed.
Pass desired moving average as <MA> argument. For example BBANDS(MA=TA.KAMA(20)).
"MOBO bands are based on a zone of 0.80 standard deviation with a 10 period look-back"
If the price breaks out of the MOBO band it can signify a trend move or price spike
Contains 42% of price movements(noise) within bands.
edit on 2022-09-09: remove MOBO function; add BBWIDTH and PERCENT_B to output
df_ta = TA.BBANDS(df, MA=df['BBI'], period = 20, column="close", std_multiplier=1.95)
df = df.merge(df_ta, left_index = True, right_index = True, how='inner' )
del df_ta
gc.collect()
21
# BUY: close> BBI
# SELL: close<BBI
df['B'] = (df["BBI"]<df["Close"]).astype(int)*(df['High']+df['Low'])/2
df['S'] = (df["BBI"]>df["Close"]).astype(int)*(df['High']+df['Low'])/2
display(df.head(5))
display(df.tail(5))
| Open | High | Low | Close | Volume | BBI | BB_UPPER | BB_MIDDLE | BB_LOWER | BBWIDTH | PERCENT_B | B | S | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | |||||||||||||
| 1999-12-31 | 19.60 | 19.67 | 19.52 | 19.56 | 139400 | NaN | NaN | NaN | NaN | NaN | NaN | 0.0 | 0.0 |
| 2000-01-03 | 19.58 | 19.71 | 19.25 | 19.45 | 556100 | NaN | NaN | NaN | NaN | NaN | NaN | 0.0 | 0.0 |
| 2000-01-04 | 19.45 | 19.45 | 18.90 | 18.95 | 367200 | NaN | NaN | NaN | NaN | NaN | NaN | 0.0 | 0.0 |
| 2000-01-05 | 19.21 | 19.58 | 19.08 | 19.58 | 481700 | NaN | NaN | NaN | NaN | NaN | NaN | 0.0 | 0.0 |
| 2000-01-06 | 19.38 | 19.43 | 18.90 | 19.30 | 853800 | NaN | NaN | NaN | NaN | NaN | NaN | 0.0 | 0.0 |
| Open | High | Low | Close | Volume | BBI | BB_UPPER | BB_MIDDLE | BB_LOWER | BBWIDTH | PERCENT_B | B | S | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | |||||||||||||
| 2022-09-02 | 31.60 | 31.97 | 31.47 | 31.85 | 8152600 | 33.075875 | 37.449735 | 33.075875 | 28.702015 | 0.264474 | 0.359863 | 0.0 | 31.720 |
| 2022-09-06 | 31.65 | 31.76 | 31.37 | 31.47 | 5613900 | 32.757250 | 36.580607 | 32.757250 | 28.933893 | 0.233436 | 0.331660 | 0.0 | 31.565 |
| 2022-09-07 | 31.21 | 31.59 | 31.16 | 31.49 | 4822000 | 32.518417 | 35.576794 | 32.518417 | 29.460039 | 0.188101 | 0.331869 | 0.0 | 31.375 |
| 2022-09-08 | 30.91 | 31.54 | 30.83 | 31.51 | 6620900 | 32.297250 | 34.836249 | 32.297250 | 29.758251 | 0.157227 | 0.344968 | 0.0 | 31.185 |
| 2022-09-09 | 31.95 | 31.97 | 31.73 | 31.89 | 3556800 | 32.226333 | 34.680677 | 32.226333 | 29.771989 | 0.152319 | 0.431482 | 0.0 | 31.850 |
df[['BBI']].hist(bins=50)
array([[<AxesSubplot:title={'center':'BBI'}>]], dtype=object)

#https://github.com/matplotlib/mplfinance
#this package help visualize financial data
import mplfinance as mpf
import matplotlib.colors as mcolors
# all_colors = list(mcolors.CSS4_COLORS.keys())#"CSS Colors"
# all_colors = list(mcolors.TABLEAU_COLORS.keys()) # "Tableau Palette",
# all_colors = list(mcolors.BASE_COLORS.keys()) #"Base Colors",
all_colors = ['dodgerblue', 'firebrick','limegreen','skyblue','lightgreen', 'navy','yellow','plum', 'yellowgreen']
#https://github.com/matplotlib/mplfinance/issues/181#issuecomment-667252575
#list of colors: https://matplotlib.org/stable/gallery/color/named_colors.html
#https://github.com/matplotlib/mplfinance/blob/master/examples/styles.ipynb
def make_3panels2(main_data, add_data, mid_panel=None, chart_type='candle', names=None, figratio=(14,9)):
style = mpf.make_mpf_style(base_mpf_style='yahoo', #charles
base_mpl_style = 'seaborn-whitegrid',
# marketcolors=mpf.make_marketcolors(up="r", down="#0000CC",inherit=True),
gridcolor="whitesmoke",
gridstyle="--", #or None, or - for solid
gridaxis="both",
edgecolor = 'whitesmoke',
facecolor = 'white', #background color within the graph edge
figcolor = 'white', #background color outside of the graph edge
y_on_right = False,
rc = {'legend.fontsize': 'small',#or number
#'figure.figsize': (14, 9),
'axes.labelsize': 'small',
'axes.titlesize':'small',
'xtick.labelsize':'small',#'x-small', 'small','medium','large'
'ytick.labelsize':'small'
},
)
if (chart_type is None) or (chart_type not in ['ohlc', 'line', 'candle', 'hollow_and_filled']):
chart_type = 'candle'
len_dict = {'candle':2, 'ohlc':3, 'line':1, 'hollow_and_filled':2}
kwargs = dict(type=chart_type, figratio=figratio, volume=True, volume_panel=1,
panel_ratios=(4,2), tight_layout=True, style=style, returnfig=True)
if names is None:
names = {'main_title': '', 'sub_tile': ''}
added_plots = {
#'S': mpf.make_addplot(add_data['S'], panel=0, color='blue', type='scatter', marker=r'${S}$' , markersize=100, secondary_y=False),
#'B': mpf.make_addplot(add_data['B'], panel=0, color='blue', type='scatter', marker=r'${B}$' , markersize=100, secondary_y=False),
'BB_UPPER': mpf.make_addplot(add_data['BB_UPPER'], panel=0, color='dodgerblue', secondary_y=False),
'BB_LOWER': mpf.make_addplot(add_data['BB_LOWER'], panel=0, color='tomato', secondary_y=False),
'BBI': mpf.make_addplot(add_data['BBI'], panel=0, color='gray', secondary_y=False),
}
fb_bbands_ = dict(y1=add_data.iloc[:, 0].values,
y2=add_data.iloc[:, 2].values,color="lightskyblue",alpha=0.1,interpolate=True)
fb_bbands_['panel'] = 0
fb_bbands= [fb_bbands_]
if mid_panel is not None:
i = 0
for name_, data_ in mid_panel.iteritems():
added_plots[name_] = mpf.make_addplot(data_, panel=1, color=all_colors[i])
i = i + 1
fb_bbands2_ = dict(y1=np.zeros(mid_panel.shape[0]),
y2=0.8+np.zeros(mid_panel.shape[0]),color="lightskyblue",alpha=0.1,interpolate=True)
fb_bbands2_['panel'] = 1
fb_bbands.append(fb_bbands2_)
fig, axes = mpf.plot(main_data, **kwargs,
addplot=list(added_plots.values()),
fill_between=fb_bbands)
# add a new suptitle
fig.suptitle(names['main_title'], y=1.05, fontsize=12, x=0.1285)
axes[0].legend([None]*7)
handles = axes[0].get_legend().legendHandles
axes[0].legend(handles=handles[2:],labels=['BB_UPPER','BB_LOWER', 'BBI'])
axes[0].set_title(names['sub_tile'], fontsize=10, style='italic', loc='left')
# axes[0].set_ylabel(names['y_tiles'][0])
# axes[2].set_ylabel(names['y_tiles'][1])
return fig, axes
start = -100
end = df.shape[0]
names = {'main_title': f'{ticker}',
'sub_tile': 'BBI: BUY: close> BBI, SELL: close<BBI'}
aa_, bb_ = make_3panels2(df.iloc[start:end][['Open', 'High', 'Low', 'Close', 'Volume']],
df.iloc[start:end][['BB_UPPER', 'BBI','BB_LOWER', 'B', 'S']],
chart_type='hollow_and_filled',names = names)
