Measuring Overbought And Oversold: Greed And Fear Index (GFI)

█ OVERVIEW

In his article in Oct 2022 issue of Technical Analysis of Stocks and Commodities, “Measuring Overbought And Oversold: Greed And Fear Index” author Howard Wang describes the construction of greed and fear index (GFI).

In simple terms, the GFI helps to detect selling and buying earlier in the trend rather than later in the trend:

  • A high GFI implies that there is overheated buying sentiment and that traders may have overbought. A high GFI may be a good time to sell ahead of the crowd and ahead of a possible reversal.
  • A very low GFI may indicate a good time to buy in advance of the crowd.

█ CALCULATION

As with the BRSI, calculation of the GFI is according to the breakout candlestick. “BOK” stands for “breakout candlestick” and is formed from five original candles:

  • BOKH (BOK candles high) (BOKH is the 5 highest candles)
  • BOKL (BOK candles low) (BOKL is the 5 lowest candles)
  • BOKC (BOK candles close) (BOKC is the 5 last closes)
  • BOKO (BOK candles open) (BOKO is the 5 last opens)

The formula for the GFI is: GFI = B⁄A

  • B = Mean of (BOKC−BOKO) for 5 periods
  • A = Mean of (BOKH−BOKL) for 5 periods

█ EXPLANATION

GFI ranges between +1 and −1:

  • If GFI is close to +1, it indicates there is overheated buying sentiment and traders may have overbought by this point. This may be a good time to sell in advance of the crowd, in order to sell close to a top.
  • If GFI is close to −1, it indicates that stop losses are being hit, indicating selling, and the market may be reaching an oversold condition. This may be a good time to buy in advance of the crowd (that is, buy the dip).
  • If GFI = 0, it indicates we are at an inflection point, which means a change may be about to occur in the current buy sell activity.

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)
1234

Download data

#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)
#CBOE Volatility Index (^VIX) Chicago Options - Chicago Options Delayed Price. Currency in USD

#benchmark_tickers = ['^GSPC', '^DJI', '^IXIC', '^RUT',  'CL=F', 'GC=F', '^TNX']

benchmark_tickers = ['^GSPC', '^VIX']
tickers = benchmark_tickers + ['GSK', 'BST', 'PFE', 'AZN', 'BSX', 'NUVA', 'MDT']
#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(f"{datetime.now()}\t {ticker}\t {hist.shape}\t {hist.index.min()}\t {hist.index.max()}")
    dfs[ticker] = hist
2023-03-08 13:50:01.144419	 ^GSPC	 (5831, 7)	 2000-01-03 00:00:00-05:00	 2023-03-07 00:00:00-05:00
2023-03-08 13:50:02.185163	 ^VIX	 (5832, 7)	 2000-01-03 00:00:00-05:00	 2023-03-08 00:00:00-05:00
2023-03-08 13:50:03.568624	 GSK	 (5831, 7)	 2000-01-03 00:00:00-05:00	 2023-03-07 00:00:00-05:00
2023-03-08 13:50:04.503930	 BST	 (2102, 7)	 2014-10-29 00:00:00-04:00	 2023-03-07 00:00:00-05:00
2023-03-08 13:50:05.765909	 PFE	 (5831, 7)	 2000-01-03 00:00:00-05:00	 2023-03-07 00:00:00-05:00
2023-03-08 13:50:07.133366	 AZN	 (5831, 7)	 2000-01-03 00:00:00-05:00	 2023-03-07 00:00:00-05:00
2023-03-08 13:50:08.237812	 BSX	 (5831, 7)	 2000-01-03 00:00:00-05:00	 2023-03-07 00:00:00-05:00
2023-03-08 13:50:09.634396	 NUVA	 (4736, 7)	 2004-05-13 00:00:00-04:00	 2023-03-07 00:00:00-05:00
2023-03-08 13:50:11.039413	 MDT	 (5831, 7)	 2000-01-03 00:00:00-05:00	 2023-03-07 00:00:00-05:00
ticker = '^GSPC'
dfs[ticker].tail(5)
Open High Low Close Volume Dividends Stock Splits
Date
2023-03-01 00:00:00-05:00 3963.340088 3971.729980 3939.050049 3951.389893 4249480000 0.0 0.0
2023-03-02 00:00:00-05:00 3938.679932 3990.840088 3928.159912 3981.350098 4244900000 0.0 0.0
2023-03-03 00:00:00-05:00 3998.020020 4048.290039 3995.169922 4045.639893 4084730000 0.0 0.0
2023-03-06 00:00:00-05:00 4055.149902 4078.489990 4044.610107 4048.419922 4000870000 0.0 0.0
2023-03-07 00:00:00-05:00 4048.260010 4050.000000 3980.310059 3986.370117 3922500000 0.0 0.0

Define Greed And Fear Index (GFI) calculation function

import sys
sys.path.append(r"/kaggle/input/technical-indicators-core")

#from core.finta import TA
from finta import TA
df = dfs[ticker][['Open', 'High', 'Low', 'Close', 'Volume']]
df = df.round(2)
def cal_gfi(ohlc: pd.DataFrame, 
                  period: int = 5, 
                 ) -> pd.Series:
    """
    // TASC Oct 2022: Measuring Overbought And Oversold: Greed And Fear Index by Howard Wang

    """
 
    ohlc = ohlc.copy(deep=True)
    ohlc.columns = [c.lower() for c in ohlc.columns]
    
    #step 1: get the breakout candlesticks
 
    break_high = ohlc["high"].rolling(window=2).max()
    break_low = ohlc["low"].rolling(window=2).min()
    two_signs = np.sign(ohlc["close"]-ohlc["open"]).rolling(window=2).apply(np.prod)
    break_open = ohlc["close"].shift(1)
    break_open[two_signs>0] = (ohlc["open"].shift(1))[two_signs>0]
    break_close = ohlc["close"]

    df_break = pd.concat([break_open,break_high, break_low,  break_close], axis=1)
    df_break.columns = ['open', 'high', 'low', 'close']
    
    #step 2: calculate the GFI
    
    B = (df_break["close"] - df_break["open"]).rolling(window=period).mean()
    A = (df_break["high"] - df_break["low"]).rolling(window=period).mean()

   

    return pd.Series(B/A, index=ohlc.index, name=f"GFI{period}")   

Calculate Greed And Fear Index (GFI)

df['GFI']=cal_gfi(df)
df['RSI']=TA.RSI(df, period = 14, column="close")
df['EMA50']=TA.EMA(df, period = 50, column="close")
df['EMA200']=TA.EMA(df, period = 200, column="close")
display(df.head(5))
display(df.tail(5))
Open High Low Close Volume GFI RSI EMA50 EMA200
Date
2000-01-03 00:00:00-05:00 1469.25 1478.00 1438.36 1455.22 931800000 NaN NaN 1455.220000 1455.220000
2000-01-04 00:00:00-05:00 1455.22 1455.22 1397.43 1399.42 1009000000 NaN 0.000000 1426.762000 1427.180500
2000-01-05 00:00:00-05:00 1399.42 1413.27 1377.68 1402.11 1085500000 NaN 4.935392 1418.213826 1418.739960
2000-01-06 00:00:00-05:00 1402.11 1411.90 1392.10 1403.45 1092300000 NaN 7.387438 1414.298520 1414.859942
2000-01-07 00:00:00-05:00 1403.45 1441.47 1400.73 1441.47 1225200000 NaN 48.207241 1420.176074 1420.288924
Open High Low Close Volume GFI RSI EMA50 EMA200
Date
2023-03-01 00:00:00-05:00 3963.34 3971.73 3939.05 3951.39 4249480000 -0.235848 40.292240 4004.483349 4007.101141
2023-03-02 00:00:00-05:00 3938.68 3990.84 3928.16 3981.35 4244900000 -0.174370 44.519159 4003.576159 4006.844911
2023-03-03 00:00:00-05:00 3998.02 4048.29 3995.17 4045.64 4084730000 0.268026 52.319603 4005.225721 4007.230932
2023-03-06 00:00:00-05:00 4055.15 4078.49 4044.61 4048.42 4000870000 0.245390 52.629750 4006.919615 4007.640773
2023-03-07 00:00:00-05:00 4048.26 4050.00 3980.31 3986.37 3922500000 0.106727 45.513517 4006.113747 4007.429124
df['GFI'].hist(bins=50)

png

Visualize Greed And Fear Index (GFI)

#from core.visuals import *
from visuals import *
start = -500
end = df.shape[0]
df_sub = df.iloc[start:end]
# df_sub = df[(df.index<='2019-04-01') & (df.index>='2019-01-24')]
#names = {'main_title': f'{ticker}'}
names = {'main_title': f'{ticker} - Greed And Fear Index (GFI)'}

lines0,  ax_cfg0 = plot_overlay_lines(data = df_sub, overlay_columns = ['EMA50', 'EMA200'])

#lines1, ax_cfg1 = plot_macd(data = df_sub, macd= 'DI_PLUS', macd_signal = 'DI_MINUS', panel =1)

lines1, shadows1, ax_cfg1 = plot_add_lines(data = df_sub, line_columns=['RSI', ], 
                                           panel =1, bands = [30, 70])
lines2, shadows2, ax_cfg2 = plot_add_lines(data = df_sub, line_columns=['GFI'], 
                                           panel =2, bands = [-0.75, 0.75])
#b_s_ = plot_buy_sell(data=df_sub, buy_column='DMI_BUY_Close', sell_column='DMI_SELL_Close')
lines_ = dict(**lines0, **lines1)
lines_.update(lines2)
#lines_.update(b_s_)

shadows_ =    shadows1 + shadows2


fig_config_ = dict(figratio=(18,9), volume=False, volume_panel=2,panel_ratios=(4,2,2), tight_layout=True, returnfig=True,)

ax_cfg_ = ax_cfg0
ax_cfg_.update(ax_cfg1)
ax_cfg_.update(ax_cfg2)


names = {'main_title': f'{ticker}'}

aa_, bb_ = make_panels(main_data = df_sub[['Open', 'High', 'Low', 'Close', 'Volume']], 
                       added_plots = lines_,
                       fill_betweens = shadows_, 
                       fig_config = fig_config_, 
                       axes_config = ax_cfg_,  
                       names = names)    

png

Call the function from finta.py

df_list = []
for ticker, df in dfs.items():
    df = df[['Open', 'High', 'Low', 'Close', 'Volume']].round(2)
    #df['GFI']=cal_gfi(df)
    df['GFI']=TA.GFI(df, period = 5)
    df['RSI']=TA.RSI(df, period = 14, column="close")
    df['EMA50']=TA.EMA(df, period = 50, column="close")
    df['EMA200']=TA.EMA(df, period = 200, column="close")
    df['ticker'] = ticker
    
    df_list.append(df)

df_all = pd.concat(df_list)
print(df_all.shape)
del df_list
gc.collect()
(47656, 10)





5507
dd = df_all.index
df_all.index = dd.date
df_all.index.name='Date'
df_all.tail(5)
Open High Low Close Volume GFI RSI EMA50 EMA200 ticker
Date
2023-03-01 82.40 82.53 81.67 82.08 5249100 -0.449129 44.022248 82.552691 86.762242 MDT
2023-03-02 81.49 82.56 81.20 82.26 4985100 -0.268898 44.950352 82.541213 86.717444 MDT
2023-03-03 82.79 83.63 82.44 83.41 4771600 -0.007821 50.587132 82.575283 86.684534 MDT
2023-03-06 83.49 83.83 81.45 81.93 6845500 -0.151832 44.300301 82.549978 86.637225 MDT
2023-03-07 82.09 82.15 79.51 79.74 6876000 -0.349321 36.977231 82.439782 86.568596 MDT
df_all.to_csv('b_055_1_gfi_tasc202210.csv', index=True)