Technical indicators Force Index (FI)


Background

The force index (FI) is a technical indicator to illustrate how strong the actual buying or selling pressure is. High positive values mean there is a strong rising trend, and low values signify a strong downward trend.

The FI is calculated by multiplying the difference between the last and previous closing prices by the volume of the security, yielding a momentum scaled by the volume. The strength of the force is determined by a larger price change or by a larger volume.

The index is an oscillator, fluctuating between positive and negative territory, therefore it is used for trend and breakout confirmation, as well as spotting potential turning points by looking for divergences.

The index is a lagging indicator whch is using prior price and volume data, and then that data is used to calculate an average (EMA), it could then sometimes be slow to provide trade signals. For example, it may take a couple of periods for the force index to start rallying after an upside breakout, but by this time the price may have already moved significantly beyond the breakout point and may thus no longer justify an entry.

Python Implementation

import pandas as pd
import sqlite3

# load S&P500 data from stored database
sp500_db = sqlite3.connect(database="sp500_data.sqlite")

df = pd.read_sql_query(sql="SELECT * FROM SP500",
                       con=sp500_db,
                       parse_dates={"Date"})
df.head()

indexDateTickerAdj CloseCloseHighLowOpenVolumegarmin_klass_vol...macd signalbb_lowbb_midbb_highsmaemaadobvemvefi
002014-04-29A35.04953438.12589338.30472237.17453438.1187404688612.0-0.002274...NaNNaNNaNNaNNaNNaN3.204858e+064688612.0NaNNaN
112014-04-29AAL33.47674235.50999835.65000234.97000135.2000018994200.0-0.000788...NaNNaNNaNNaNNaNNaN5.290623e+068994200.0NaNNaN
222014-04-29AAPL18.63333321.15464221.28500021.05392821.205000337377600.0-0.006397...NaNNaNNaNNaNNaNNaN-4.328196e+07337377600.0NaNNaN
332014-04-29ABBV34.03498551.36999951.52999950.75999850.9399995601300.0-0.062705...NaNNaNNaNNaNNaNNaN3.273491e+065601300.0NaNNaN
442014-04-29ABT31.83151838.54000138.72000138.25999838.3699994415600.0-0.013411...NaNNaNNaNNaNNaNNaN9.599290e+054415600.0NaNNaN

5 rows × 23 columns

# remove irrelevant columns
df = df.drop('index', axis=1)
#df = df.drop('level_0', axis=1)
df.head()

DateTickerAdj CloseCloseHighLowOpenVolumegarmin_klass_volrsi...macd signalbb_lowbb_midbb_highsmaemaadobvemvefi
02014-04-29A35.04953438.12589338.30472237.17453438.1187404688612.0-0.002274NaN...NaNNaNNaNNaNNaNNaN3.204858e+064688612.0NaNNaN
12014-04-29AAL33.47674235.50999835.65000234.97000135.2000018994200.0-0.000788NaN...NaNNaNNaNNaNNaNNaN5.290623e+068994200.0NaNNaN
22014-04-29AAPL18.63333321.15464221.28500021.05392821.205000337377600.0-0.006397NaN...NaNNaNNaNNaNNaNNaN-4.328196e+07337377600.0NaNNaN
32014-04-29ABBV34.03498551.36999951.52999950.75999850.9399995601300.0-0.062705NaN...NaNNaNNaNNaNNaNNaN3.273491e+065601300.0NaNNaN
42014-04-29ABT31.83151838.54000138.72000138.25999838.3699994415600.0-0.013411NaN...NaNNaNNaNNaNNaNNaN9.599290e+054415600.0NaNNaN

5 rows × 22 columns

import pandas_ta

df = df.set_index(['Date','Ticker'])

# compute FI

def compute_efi(stock_data):
    efi = pandas_ta.efi(close=stock_data['Close'],
                        volume=stock_data['Volume'],
                        length=13)
    return efi

df['efi'] = df.groupby(level=1, group_keys=False).apply(compute_efi)
df.tail()

Adj CloseCloseHighLowOpenVolumegarmin_klass_volrsiatrmacdmacd signalbb_lowbb_midbb_highsmaemaadobvemvefi
DateTicker
2024-04-25XYL130.610001130.610001131.199997128.100006129.619995963600.00.00026461.4827450.6653060.3551460.2552764.8455914.8635024.881413128.482000128.5826951.381747e+0842359600.0-1.2756133.009081e+05
YUM141.559998141.559998142.169998140.389999141.9799961693100.00.00007665.6681630.3225660.5508300.2857844.9134824.9379684.962454138.497000138.6585638.902989e+07253995207.047.0148787.056074e+05
ZBH119.750000119.750000121.349998118.769997120.7099991078800.00.00020635.636078-0.350196-0.889200-0.6464804.7728464.8358884.898931125.012999123.5655697.870077e+07-62096220.0-114.852947-5.940560e+05
ZBRA292.529999292.529999293.290009271.630005274.359985674700.00.00135556.1722720.500501-0.391439-0.2424565.5887305.6663875.744044288.205502284.2623625.349014e+0737474700.0-2359.3220921.337205e+06
ZTS153.360001153.360001153.589996150.039993150.9700014567200.00.00017839.8096191.374957-3.202379-3.5843534.9674085.0650655.162723157.579352157.0035212.636284e+08255609600.0-95.499616-4.281032e+06
# update the database
df = df.reset_index()
df.to_sql(name="SP500",
          con=sp500_db,
          if_exists="replace",
          index=True)
sp500_db.close()
import matplotlib.pyplot as plt
from datetime import datetime

# select AAPL
aapl = df[df['Ticker'] == 'AAPL'].set_index('Date')

# only select the data from 2022-01-01
aapl_new = aapl[aapl.index > datetime(2022,1,1)]

# set the theme of the chart
plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (20,16)

# create two charts on the same figure
ax1 = plt.subplot2grid((10,1),(0,0), rowspan=4, colspan=1)
ax2 = plt.subplot2grid((10,1),(5,0), rowspan=4, colspan=1)

# plot the closing price on the first chart
ax1.plot(aapl_new['Adj Close'])
ax1.set_title('AAPL Adjust Close Price')

# plot the EFI on the second chart
ax2.plot(aapl_new['efi'], color='orange', linewidth=1)
ax2.set_title("AAPL Elder's Force Index")
Text(0.5, 1.0, "AAPL Elder's Force Index")

AAPL Elder's Force Index

Reference

Force Index: Overview, Formulas, Limitations by Cory Mitchell on Investopedia


Author: wenvenn
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source wenvenn !