The Money Flow Index (MFI) is a technical oscillator that combines both price and volume data to measure buying and selling pressure. It is often referred to as the volume-weighted Relative Strength Index (RSI) because, while its calculation and interpretation are similar to the RSI, MFI incorporates volume, providing an arguably deeper insight into the strength behind price movements.
Theory: The MFI is calculated through a series of steps that transform raw price and volume data into a bounded oscillator (0-100):
Calculate Typical Price (TP) for each period:
The Typical Price is an average of the high, low, and closing prices for
a given period.
Calculate Raw Money Flow (RMF): This is the
Typical Price multiplied by the volume for that period. It represents
the total value of transactions for the period, adjusted by its typical
price.
Determine Positive and Negative Money Flow: Over a specified lookback period (N, typically 14 periods):
Calculate the Money Flow Ratio (MFR): This is
the ratio of the N-period sum of Positive Money Flow to the N-period sum
of Negative Money Flow.
Calculate the Money Flow Index (MFI): The MFI is
then scaled to oscillate between 0 and 100 using a formula analogous to
the RSI: If the
N-period sum of Negative Money Flow is zero, MFR is considered infinite,
and MFI is 100. If the N-period sum of Positive Money Flow is zero, MFR
is zero, and MFI is 0.
Usage: The Money Flow Index is interpreted in a manner similar to the RSI, but its inclusion of volume can sometimes offer more robust signals.
TA-Lib Function: The Technical Analysis Library (TA-Lib) provides a function for calculating the Money Flow Index:
talib.MFI(high_prices, low_prices, close_prices, volume, timeperiod=14)
high_prices
: An array or series of high prices.low_prices
: An array or series of low prices.close_prices
: An array or series of closing
prices.volume
: An array or series of volume data.timeperiod
: The lookback period for the MFI
calculation, with 14 being a common default.Code Example (Calculation & Plot with yfinance Data):
The Python code below demonstrates fetching HLCV data using
yfinance
, calculating the MFI, and plotting the price
alongside the MFI indicator, including overbought, oversold, and
midpoint lines.
import yfinance as yf
import talib
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# --- 1. Data Fetching using yfinance ---
= "NVDA" # Example: NVIDIA Corporation
ticker_symbol # Fetching a bit more data initially to ensure enough for indicator calculation start
= "2022-11-01" # Ensure enough data for lookback period
data_start_date = "2023-01-01" # Start plotting from here
plot_start_date = "2024-05-01"
data_end_date
= yf.download(ticker_symbol, start=data_start_date, end=data_end_date, auto_adjust=False, progress=False)
data
if data.empty or not all(col in data.columns for col in ['High', 'Low', 'Close', 'Volume']) or \
any(data[col].isnull().all() for col in ['High', 'Low', 'Close', 'Volume']):
print(f"Insufficient HLCV data found for {ticker_symbol}. Exiting.")
else:
# Complying with user preference for droplevel
if isinstance(data.columns, pd.MultiIndex):
= data.columns.droplevel(level=1)
data.columns
# Prepare data: ensure no NaNs in HLCV for the period used by TA-Lib
= data[['High', 'Low', 'Close', 'Volume']].dropna()
ohlcv_data
= ohlcv_data['High']
high_prices = ohlcv_data['Low']
low_prices = ohlcv_data['Close']
close_prices = ohlcv_data['Volume'].astype(float) # Ensure volume is float
volume_data
# --- 2. Money Flow Index (MFI) Calculation ---
= 14 # Standard period for MFI
time_period_mfi
# MFI requires HLCV data and sufficient lookback period.
# TA-Lib will output NaNs for the initial 'timeperiod' entries.
if len(close_prices) >= time_period_mfi :
= talib.MFI(
mfi_values
high_prices, low_prices, close_prices, volume_data,=time_period_mfi
timeperiod
)= f"MFI({time_period_mfi})"
indicator_name print(f"\n--- {indicator_name} - Money Flow Index for {ticker_symbol} ---")
# For printing, filter out NaNs. For plotting, NaNs will result in gaps.
= mfi_values[~np.isnan(mfi_values)]
valid_mfi if len(valid_mfi) >= 5:
print(f"Output {indicator_name} (last 5 valid): {valid_mfi[-5:].round(2)}")
elif len(valid_mfi) > 0:
print(f"Output {indicator_name} (all valid): {valid_mfi.round(2)}")
else:
print(f"Output {indicator_name}: No valid MFI values calculated (all NaNs).")
# Filter data for the desired plotting period
# Ensure mfi_values (which is a numpy array) is converted to a Pandas Series with the correct index
# before trying to align with plot_ohlcv_data.index
= pd.Series(mfi_values, index=ohlcv_data.index)
mfi_series
= ohlcv_data[ohlcv_data.index >= plot_start_date]
plot_ohlcv_data = mfi_series[plot_ohlcv_data.index] # Align MFI series to the plotting data
plot_mfi_values = plot_ohlcv_data.index
plot_date_index = plot_ohlcv_data['Close']
plot_close_prices
# --- 3. Plotting ---
if not plot_date_index.empty:
= plt.subplots(2, 1, figsize=(14, 10), sharex=True,
fig, axes ={'height_ratios': [3, 1]})
gridspec_kw
# Plot Price
0].plot(plot_date_index, plot_close_prices, label='Close Price', color='blue')
axes[0].set_title(f'{ticker_symbol} Price and {indicator_name}')
axes[0].set_ylabel('Price')
axes[0].legend(loc='upper left')
axes[0].grid(True)
axes[
# Plot Money Flow Index
1].plot(plot_date_index, plot_mfi_values, label=indicator_name, color='orange')
axes[1].axhline(80, color='red', linestyle='--', linewidth=0.8, label='Overbought (80)')
axes[1].axhline(50, color='gray', linestyle=':', linewidth=0.8, label='Midpoint (50)')
axes[1].axhline(20, color='green', linestyle='--', linewidth=0.8, label='Oversold (20)')
axes[1].set_ylim(0, 100) # MFI oscillates between 0 and 100
axes[1].set_ylabel('MFI Value')
axes[1].set_xlabel('Date')
axes[1].legend(loc='center left', bbox_to_anchor=(1, 0.5)) # Place legend outside
axes[1].grid(True)
axes[
=[0, 0, 0.9, 1]) # Adjust layout to make space for external legend
plt.tight_layout(rect
plt.show()else:
print(f"No data available for the plotting period starting {plot_start_date}.")
else:
print(f"\nSkipping MFI plot for {ticker_symbol}: Insufficient data (need >= {time_period_mfi} points after cleaning).")
if not ohlcv_data.empty:
print(f"Cleaned data available for {len(ohlcv_data)} periods. MFI period is {time_period_mfi}.")
Explanation of the Code:
yf.download()
retrieves HLCV data. User preferences
auto_adjust=False
and droplevel
are
applied.ohlcv_data = data[['High', 'Low', 'Close', 'Volume']].dropna()
cleans the data by removing rows with any missing HLCV values.time_period_mfi
is set (typically 14).if len(close_prices) >= time_period_mfi:
ensures enough data for TA-Lib’s MFI calculation (TA-Lib will produce
NaNs for the first timeperiod-1
calculations of money flow,
and one more for the ratio, leading to timeperiod
NaNs in
the MFI output).talib.MFI()
computes the MFI values.plot_start_date
for a cleaner chart. The MFI numpy array is
converted to a Pandas Series with the correct index to allow for this
alignment.plt.subplots(2, 1, ...)
creates two stacked
subplots.axhline()
adds horizontal lines at 80 (overbought), 50
(midpoint), and 20 (oversold) for easy reference.set_ylim(0, 100)
sets the y-axis scale for the
MFI.plt.tight_layout()
adjusts spacing, with
rect
to accommodate the external legend.plt.show()
displays the chart.By integrating volume into a momentum oscillator framework, the Money Flow Index offers traders a more comprehensive tool for gauging market strength and identifying potential turning points that might be missed by price-only indicators.