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 ---
ticker_symbol = "NVDA" # Example: NVIDIA Corporation
# Fetching a bit more data initially to ensure enough for indicator calculation start
data_start_date = "2022-11-01" # Ensure enough data for lookback period
plot_start_date = "2023-01-01" # Start plotting from here
data_end_date = "2024-05-01"
data = yf.download(ticker_symbol, start=data_start_date, end=data_end_date, auto_adjust=False, progress=False)
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 = data.columns.droplevel(level=1)
# Prepare data: ensure no NaNs in HLCV for the period used by TA-Lib
ohlcv_data = data[['High', 'Low', 'Close', 'Volume']].dropna()
high_prices = ohlcv_data['High']
low_prices = ohlcv_data['Low']
close_prices = ohlcv_data['Close']
volume_data = ohlcv_data['Volume'].astype(float) # Ensure volume is float
# --- 2. Money Flow Index (MFI) Calculation ---
time_period_mfi = 14 # Standard period for 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 :
mfi_values = talib.MFI(
high_prices, low_prices, close_prices, volume_data,
timeperiod=time_period_mfi
)
indicator_name = f"MFI({time_period_mfi})"
print(f"\n--- {indicator_name} - Money Flow Index for {ticker_symbol} ---")
# For printing, filter out NaNs. For plotting, NaNs will result in gaps.
valid_mfi = mfi_values[~np.isnan(mfi_values)]
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
mfi_series = pd.Series(mfi_values, index=ohlcv_data.index)
plot_ohlcv_data = ohlcv_data[ohlcv_data.index >= plot_start_date]
plot_mfi_values = mfi_series[plot_ohlcv_data.index] # Align MFI series to the plotting data
plot_date_index = plot_ohlcv_data.index
plot_close_prices = plot_ohlcv_data['Close']
# --- 3. Plotting ---
if not plot_date_index.empty:
fig, axes = plt.subplots(2, 1, figsize=(14, 10), sharex=True,
gridspec_kw={'height_ratios': [3, 1]})
# Plot Price
axes[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)
# Plot Money Flow Index
axes[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)
plt.tight_layout(rect=[0, 0, 0.9, 1]) # Adjust layout to make space for external legend
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.