End-to-end Implementation with buy and sell signals. Indicators include VPT, VROC, CMF, Klinger Oscillator and VROC
In this article, we delve into the practical application of various key volume indicators, providing a complete end-to-end implementation in Python with potential buy and sell signals.
Volume indicators are essential tools in trading, providing crucial insights into market trends. By analyzing trading volume, these indicators help traders understand the strength behind price movements, whether they’re driven by strong trader interest or just surface-level fluctuations.
We will cover a range of essential volume indicators which have become popular for their effectiveness. Each offers a unique view on market activity, aiding traders in building diverse strategies:
- On-Balance Volume (OBV)
- Volume Price Trend (VPT)
- Volume Rate of Change (VROC)
- Chaikin Money Flow (CMF)
- Volume-Weighted Average Price (VWAP)
- Accumulation/Distribution Line
- Money Flow Index (MFI)
- Klinger Oscillator
- Negative Volume Index
1. Understanding Volume Indicators
Volume indicators are essential tools in trading, helping to validate price movements and signal potential market shifts. For example, if a stock price increases with high volume, it indicates strong buyer interest, making the uptrend more credible.
Conversely, if the stock rises on low volume, the move might be less reliable. Similarly, a price drop on high volume could signal strong selling pressure, hinting at a bearish trend.
These indicators not only confirm the strength of a trend but can also flag reversals. High volume with flat price in a downtrend can signal weakening selling pressure, a possible reversal.
Understanding these nuances helps traders make more informed decisions, identifying opportunities for entry and exit in the market.
2. Python Implementation
2.1 On-Balance Volume (OBV)
On-Balance Volume (OBV) is a cumulative indicator that relates volume to price change. OBV theory hinges on a difference between informed big investors and less-informed individual investors.
OBV increases or decreases during each trading day in line with whether the price closes higher or lower from the previous close. If the closing price is higher, the day’s volume is added to the OBV total; if it’s lower, the volume is subtracted.
The formula can be represented as:
Equation 1. OBV Formula: Calculates cumulative volume based on price changes, adding volume on up days and subtracting on down days to indicate buying or selling pressure.
The sign depends on the comparison of closing prices between the current and the previous day.
A key insight not immediately obvious is OBV’s utility in identifying volume-driven price movements ahead of price trend changes. This is because volume changes often precede price changes, making OBV a leading indicator for identifying potential reversals.
For instance, a rising OBV in a downtrending market can signal an impending upward price reversal, as it suggests accumulation by informed investors.
Conversely, a declining OBV in an uptrend may indicate distribution and a possible price reversal to the downside. OBV helps confirm trends and spot hidden clues (divergences) from price alone, giving traders an early edge.
import yfinance as yf
import matplotlib.pyplot as plt
import pandas as pd
def calculate_obv(data):
obv = [0]
for i in range(1, len(data)):
if data['Close'][i] > data['Close'][i-1]:
obv.append(obv[-1] + data['Volume'][i])
elif data['Close'][i] < data['Close'][i-1]:
obv.append(obv[-1] - data['Volume'][i])
else:
obv.append(obv[-1])
return obv
# Retrieve data
ticker = 'AAPL' # Example ticker
data = yf.download(ticker, start="2020-01-01", end="2023-01-01")
# Calculate OBV
data['OBV'] = calculate_obv(data)
data['OBV_EMA'] = data['OBV'].ewm(span=30).mean() # 20-day EMA of OBV
# Generate buy and sell signals
buy_signal = (data['OBV'] > data['OBV_EMA']) & (data['OBV'].shift(1) <= data['OBV_EMA'].shift(1))
sell_signal = (data['OBV'] < data['OBV_EMA']) & (data['OBV'].shift(1) >= data['OBV_EMA'].shift(1))
# Plotting with adjusted subplot sizes
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8), gridspec_kw={'height_ratios': [3, 1]})
# Stock price plot with buy and sell signals
ax1.plot(data['Close'], label='Close Price', alpha=0.5)
ax1.scatter(data.index[buy_signal], data['Close'][buy_signal], label='Buy Signal', marker='^', color='green')
ax1.scatter(data.index[sell_signal], data['Close'][sell_signal], label='Sell Signal', marker='v', color='red')
ax1.set_title(f'{ticker} Stock Price')
ax1.set_ylabel('Price')
ax1.legend()
# OBV subplot
ax2.plot(data['OBV'], label='OBV', color='blue')
ax2.plot(data['OBV_EMA'], label='30-day EMA of OBV', color='orange', alpha=0.6)
ax2.set_title(f'{ticker} On-Balance Volume (OBV)')
ax2.set_ylabel('OBV')
ax2.legend()
plt.tight_layout()
plt.show()
Figure. 1: The chart contrasts stock closing prices with its OBV, including a 30-day EMA of OBV for trend analysis. Buy signals (green markers) occur when OBV crosses above its EMA, and sell signals (red markers) when it crosses below, suggesting shifts in market momentum.
2.2 Volume Price Trend (VPT)
VPT integrates volume with the percentage change in price, cumulatively indicating the trend’s strength and direction. It’s calculated by multiplying the volume for each period by the percentage price change and adding this to the previous period’s VPT value. The formula can be expressed as:
Equation 2. VPT Formula: Adjusts volume by the percentage change in price to reflect the cumulative impact of trading and price direction, aiding in trend strength assessment.
VPT is used to understand the balance between supply and demand. Traders watch for divergences between VPT and price movement as a potential indicator of future price changes. It’s especially useful in confirming the strength of a trend.
Specifically, VPT’s sensitivity to volume changes in relation to price adjustments provides an advanced warning system for shifts in market sentiment.
For instance, a divergence between VPT and price — where price continues to rise while VPT begins to fall — can signal weakening momentum and potentially forecast a price reversal.
import yfinance as yf
import matplotlib.pyplot as plt
import pandas as pd
def calculate_vpt(data):
vpt = [0]
for i in range(1, len(data)):
price_change = data['Close'][i] - data['Close'][i-1]
vpt.append(vpt[-1] + (data['Volume'][i] * price_change / data['Close'][i-1]))
return vpt
# Retrieve data
ticker = 'AAPL' # Example ticker
data = yf.download(ticker, start="2020-01-01", end="2023-01-01")
# Calculate VPT
data['VPT'] = calculate_vpt(data)
data['VPT_MA'] = data['VPT'].rolling(window=30).mean() # 20-day moving average
# Generate buy and sell signals
buy_signal = (data['VPT'] > data['VPT_MA']) & (data['VPT'].shift(1) <= data['VPT_MA'].shift(1))
sell_signal = (data['VPT'] < data['VPT_MA']) & (data['VPT'].shift(1) >= data['VPT_MA'].shift(1))
# Plotting
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8), gridspec_kw={'height_ratios': [2, 1]})
# Stock price plot with buy and sell signals
ax1.plot(data['Close'], label='Close Price', alpha=0.5)
ax1.scatter(data.index[buy_signal], data['Close'][buy_signal], label='Buy Signal', marker='^', color='green')
ax1.scatter(data.index[sell_signal], data['Close'][sell_signal], label='Sell Signal', marker='v', color='red')
ax1.set_title(f'{ticker} Stock Price')
ax1.set_ylabel('Price')
ax1.legend()
# VPT subplot
ax2.plot(data['VPT'], label='VPT', color='blue')
ax2.plot(data['VPT_MA'], label='30-day MA of VPT', color='orange', alpha=0.6)
ax2.set_title(f'{ticker} Volume Price Trend (VPT)')
ax2.set_ylabel('VPT')
ax2.legend()
plt.tight_layout()
plt.show()
Figure. 2: The chart pairs stock price action with VPT and its 30-day moving average, highlighting buy (green markers) and sell (red markers) signals based on VPT's movement relative to its average, offering insights into trend confirmation and potential reversals.
2.3 Volume Rate of Change (VROC)
VROC quantifies the percentage rate at which volume changes over a specific period, comparing current volume to volume from n days ago. This measurement highlights the acceleration or deceleration in trading activity, independent of price direction. The formula for VROC is given by:
Equation 3. VROC Formula: Measures the percent change in volume over a chosen period, providing insights into the pace of market activity and potential momentum shifts.
VROC is primarily used to confirm price trends and potential price reversals. High VROC with rising prices suggests a strong uptrend, while during a decline it signals heavy selling.
VROC offers a distinct perspective on market dynamics by focusing solely on volume change rates. Less well-known, VROC can hint at rising trading activity before it affects prices.
High VROC with flat prices can signal a coming price breakout. This suggests more traders are entering, potentially tipping the supply/demand scales.
import yfinance as yf
import matplotlib.pyplot as plt
import pandas as pd
def calculate_vroc(data, period=20):
vroc = ((data['Volume'] - data['Volume'].shift(period)) / data['Volume'].shift(period)) * 100
return vroc
# Retrieve data
ticker = 'AAPL' # Example ticker
data = yf.download(ticker, start="2020-01-01", end="2023-01-01")
# Calculate VROC
vroc_period = 20 # 20-day VROC
data['VROC'] = calculate_vroc(data, vroc_period)
data['VROC_MA'] = data['VROC'].rolling(window=vroc_period).mean() # 20-day moving average of VROC
# Generate buy and sell signals
buy_signal = (data['VROC'] > data['VROC_MA']) & (data['VROC'].shift(1) <= data['VROC_MA'].shift(1))
sell_signal = (data['VROC'] < data['VROC_MA']) & (data['VROC'].shift(1) >= data['VROC_MA'].shift(1))
# Plotting
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25, 8), gridspec_kw={'height_ratios': [3, 1]})
# Stock price plot with buy and sell signals
ax1.plot(data['Close'], label='Close Price', alpha=0.5)
ax1.scatter(data.index[buy_signal], data['Close'][buy_signal], label='Buy Signal', marker='^', color='green')
ax1.scatter(data.index[sell_signal], data['Close'][sell_signal], label='Sell Signal', marker='v', color='red')
ax1.set_title(f'{ticker} Stock Price')
ax1.set_ylabel('Price')
ax1.legend()
# VROC subplot
ax2.plot(data['VROC'], label='VROC', color='blue')
ax2.plot(data['VROC_MA'], label=f'{vroc_period}-day MA of VROC', color='orange', alpha=0.6)
ax2.set_title(f'{ticker} Volume Rate of Change (VROC)')
ax2.set_ylabel('VROC (%)')
ax2.legend()
plt.tight_layout()
plt.show()
Figure. 3: The visualization contrasts stock closing prices with the VROC and its 20-day moving average, marking buy (green) and sell (red) signals based on the crossover of VROC and its average. This illustrates the indicator's utility in spotting changes in market momentum through volume analysis.
2.4 Chaikin Money Flow (CMF)
CMF integrates price and volume to measure the buying and selling pressure over a specified period, typically 20 days.
CMF tracks buying and selling pressure with volume and price changes. Forget the formula, focus on its message: positive CMF means buying interest, negative means selling:
Equation 4. CMF Formula: Measures the market pressure by combining price movements and volume over a set period, indicating buying or selling pressure.
The CMF’s utility extends beyond mere trend confirmation to include its predictive capacity for spotting liquidity-driven price movements. A key insight from the CMF is its ability to reveal hidden buying or selling pressure that may not be evident from price action alone.
For instance, a rising CMF in a consolidating market can preempt a breakout, suggesting that despite the lack of price movement, underlying buying pressure is accumulating.
Related Articles:
Top 6 Volatility Indicators In Python
Newsletter