Back To Top

March 15, 2024

Top 6 Momentum Indicators in Python

End-to-end Implementation with buy and sell signals. Indicators include Awesome Oscillator, RSI, ROC, CCI and Stochastic Oscillator

Momentum trading, which hinges on the continuation of existing market trends, has always been very popular among financial traders. This article dives into the heart of momentum trading by exploring and implementing key momentum indicators using Python.

Our focus is on seven pivotal indicators: the Relative Strength Index (RSI), Stochastic Oscillator, Rate of Change (ROC), Commodity Channel Index (CCI), Williams %R, Awesome Oscillator. Each of these plays a unique role in identifying potential buy and sell signals.

This article cuts through the complexity, offering Python users practical, ready-to-deploy code for each indicator with buy and sell signals and adjustable parameters to minimize false positives. 

  • Relative Strength Index (RSI)
  • Stochastic Oscillator
  • Rate of Change (ROC)
  • Commodity Channel Index (CCI)
  • Williams %R
  • Awesome Oscillator

2. Python Implementation

2.1 Relative Strength Index (RSI)

RSI is among the most powerful momentum indicators. The RSI is an oscillator that measures the speed and change of price movements, oscillating between 0 and 100.

It’s calculated based on the ratio of average gains to average losses over a specified period, typically 14 days. The formula for RSI is:

Equation 1. RSI Formula: A momentum oscillator that evaluates the magnitude of recent price changes to identify overbought or oversold conditions in the price of an asset.

Where RS is the average gain of up periods during the specified time frame divided by the average loss of down periods.

The RSI’s ability to detect overbought and oversold conditions offers a strategic edge in predicting potential reversal points. However, a key insight often overlooked is the concept of divergence between RSI and price. 

When the price of an asset makes a new high or low that is not mirrored by the RSI, it signals a weakening of the current trend and a possible reversal.

This divergence is a critical aspect of momentum indicators, serving as a more reliable indicator than the RSI value alone, as it reflects a fundamental discrepancy between price movement and momentum. 

By identifying divergences, traders can anticipate trend reversals ahead of the crowd. This provides a valuable analysis for making decisions in anticipation of market movements.

				
					import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from ta.momentum import RSIIndicator

def fetch_stock_data(ticker, start_date, end_date):
    data = yf.download(ticker, start=start_date, end=end_date)
    return data

def plot_rsi(dates, prices, rsi):
    buy_signals = (rsi.shift(1) <= 30) & (rsi > 30)
    sell_signals = (rsi.shift(1) >= 70) & (rsi < 70)

    fig, (ax2, ax1) = plt.subplots(2, 1, figsize=(30, 10), sharex=True)

    ax1.set_ylabel('RSI', color='b')
    ax1.plot(dates, rsi, color='b', label='RSI', alpha=0.4)
    ax1.axhline(30, color='gray', linestyle='--', alpha=0.4)
    ax1.axhline(70, color='gray', linestyle='--', alpha=0.4)
    ax1.tick_params(axis='y', labelcolor='b')
    ax1.legend(loc='upper right')

    # Add vertical lines for buy signals on RSI plot
    for date in dates[buy_signals]:
        ax1.axvline(date, color='b', linestyle='--', alpha=1)

    # Add vertical lines for sell signals on RSI plot
    for date in dates[sell_signals]:
        ax1.axvline(date, color='r', linestyle='--', alpha=1)

    ax2.set_xlabel('Date')
    ax2.set_ylabel('Stock Price', color='g')
    ax2.plot(dates, prices, color='g', label='Stock Price')
    ax2.scatter(dates[buy_signals], prices[buy_signals], marker='^', color='b', label='Buy')
    ax2.scatter(dates[sell_signals], prices[sell_signals], marker='v', color='r', label='Sell')
    ax2.tick_params(axis='y', labelcolor='g')
    ax2.legend(loc='lower right')

    # Add vertical lines for buy signals on price plot
    for date in dates[buy_signals]:
        ax2.axvline(date, color='b', linestyle='--', alpha=1)

    # Add vertical lines for sell signals on price plot
    for date in dates[sell_signals]:
        ax2.axvline(date, color='r', linestyle='--', alpha=1)

    plt.title('Stock Price and RSI with Buy/Sell Signals')
    plt.tight_layout()
    plt.show()

ticker = "AFX.DE"
start_date = "2018-01-01"
end_date = "2023-12-30"

stock_data = fetch_stock_data(ticker, start_date, end_date)
prices = stock_data['Close']
rsi = RSIIndicator(prices).rsi()

plot_rsi(prices.index, prices, rsi)
				
			
momentum indicators RSI entreprenerdly

Figure. 2: The graph illustrates stock price movement and RSI indicator, highlighting buy signals (blue markers) when RSI crosses above 30 (oversold threshold) and sell signals (red markers) when it crosses below 70 (overbought threshold). This visual representation aids in identifying potential entry and exit points based on momentum

2.2 Stochastic Oscillator

The Stochastic Oscillator is a momentum indicator comparing a particular closing price of a security to a range of its prices over a certain period of time. 

The sensitivity of the oscillator to market movements is reducible by adjusting that time period or by taking a moving average of the result. The formula for the %K line is:

Equation 2. Stochastic Oscillator Formula: Measures the level of the closing price relative to the high-low range over a specific period, aiming to identify momentum and potential price turning points.

where C is the closing price, L14 is the lowest price in the last 14 periods, and H14 is the highest price in the same 14 periods. The %D line is typically a 3-period moving average of the %K line.

A nuanced understanding of the Stochastic Oscillator is its ability to highlight market conditions where prices close near their high or low, which can be indicative of momentum. 

Importantly, divergences between the %K and %D lines and the price can be a powerful signal of potential reversals. For example, if the price makes a new high but the oscillator fails to reach a new high, it may suggest a loss of momentum and an impending reversal. 

This divergence between price and momentum indicators is especially significant when it occurs near the overbought or oversold thresholds.

By paying attention to these divergences, as well as the position of %K and %D relative to the 50% level, traders can gain insights into the strength of the trend and the likelihood of continuation or reversal.

				
					import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt

# Fetch data
ticker = "AFX.DE"
data = yf.download(ticker, start="2020-01-01", end="2023-12-31")

# Calculate the Stochastic Oscillator
high14 = data['High'].rolling(14).max()
low14 = data['Low'].rolling(14).min()
data['%K'] = (data['Close'] - low14)*100/(high14 - low14)
data['%D'] = data['%K'].rolling(3).mean()

upper_threshold = 85
lower_threshold = 15

# Create signals
data['Buy_Signal'] = (data['%K'] < lower_threshold) & (data['%D'] < lower_threshold)
data['Sell_Signal'] = (data['%K'] > upper_threshold ) & (data['%D'] > upper_threshold )

# Plot data
fig, axs = plt.subplots(2, figsize=(30,10))

# Plot the closing prices and the buy/sell signals
axs[0].plot(data['Close'], label='ASML.AS', color='black')
buy_signals = data[data['Buy_Signal']].index
sell_signals = data[data['Sell_Signal']].index
axs[0].scatter(data.loc[buy_signals].index, data.loc[buy_signals]['Close'], color='green', marker='^', alpha=1, label='Buy Signal')
axs[0].scatter(data.loc[sell_signals].index, data.loc[sell_signals]['Close'], color='red', marker='v', alpha=1, label='Sell Signal')

# Add vertical lines for buy signals on price plot
for date in buy_signals:
    axs[0].axvline(date, color='green', linestyle='--', alpha=0.3)

# Add vertical lines for sell signals on price plot
for date in sell_signals:
    axs[0].axvline(date, color='red', linestyle='--', alpha=0.3)

axs[0].legend(loc='upper left')
axs[0].set_title(f'{ticker} Price and Buy/Sell Signals')
axs[0].set_xlabel('Date')
axs[0].set_ylabel('Price')
axs[0].grid()

# Plot the Stochastic Oscillator (%K and %D lines)
axs[1].plot(data['%K'], label='%K line', color='blue')
axs[1].plot(data['%D'], label='%D line', color='orange')
axs[1].axhline(lower_threshold, color='red', linestyle='--', label='Oversold')  # Horizontal line for oversold threshold
axs[1].axhline(upper_threshold, color='green', linestyle='--', label='Overbought')  # Horizontal line for overbought threshold

# Add vertical lines for buy signals on Stochastic Oscillator plot
for date in buy_signals:
    axs[1].axvline(date, color='green', linestyle='--', alpha=0.3)

# Add vertical lines for sell signals on Stochastic Oscillator plot
for date in sell_signals:
    axs[1].axvline(date, color='red', linestyle='--', alpha=0.3)

axs[1].legend(loc='upper left')
axs[1].set_title('Stochastic Oscillator')
axs[1].set_xlabel('Date')
axs[1].set_ylabel('Value')
axs[1].grid()

plt.tight_layout()
plt.show()
				
			
momentum indicators stochastic entreprenerdly

Figure. 2: The chart shows the stock closing price along with the Stochastic Oscillator (%K and %D lines), highlighting overbought (above 85) and oversold (below 15) signals. Buy signals (green markers) are indicated when both %K and %D are below the oversold threshold, while sell signals (red markers) occur when they exceed the overbought threshold, suggesting potential entry and exit points.

2.3 Rate of Change (ROC)

The ROC is a momentum oscillator that measures the percentage change in price between the current price and the price a certain number of periods ago. It reflects the speed at which a security’s price is changing, indicating upward or downward momentum. The formula for ROC is:

Equation 3. ROC Formula: Calculates the percentage rate at which a security's price has changed over a specified number of periods, highlighting the speed of price movements.

The ROC’s primary value lies in its ability to signal not just the direction but also the velocity of price movements, providing a dual perspective on market dynamics. 

An insightful aspect of ROC, , as one of the key momentum indicators, is its sensitivity to changes in price velocity, which can serve as an early warning system for increases in market volatility. 

For instance, a sharp rise in the ROC could indicate not only strengthening momentum but also a potential increase in volatility, presaging more significant price shifts. Conversely, a rapid decline could signal decreasing momentum and potential market consolidation or reversal. 

This ability to anticipate changes in volatility, based on the rate of price changes, offers traders and investors a nuanced tool for assessing market conditions and adjusting their strategies accordingly, especially when considering entry or exit points in fast-moving markets.

				
					import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt

def fetch_stock_data(ticker, start_date, end_date):
    data = yf.download(ticker, start=start_date, end=end_date)
    return data

def calculate_momentum(prices, n=14):
    roc = (prices - prices.shift(n)) / prices.shift(n) * 100
    return roc

def plot_momentum(dates, prices, roc):
    buy_signals = (roc.shift(1) <= 0) & (roc > 0)
    sell_signals = (roc.shift(1) >= 0) & (roc < 0)

    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(30, 12), sharex=True)

    ax1.set_ylabel('Stock Price', color='g')
    ax1.plot(dates, prices, color='g', label='Stock Price')
    ax1.scatter(dates[buy_signals], prices[buy_signals], marker='^', color='b', label='Buy', s=150)
    ax1.scatter(dates[sell_signals], prices[sell_signals], marker='v', color='r', label='Sell', s=150)
    ax1.tick_params(axis='y', labelcolor='g')
    ax1.legend(loc='upper left')

    ax2.set_xlabel('Date')
    ax2.set_ylabel('Momentum (ROC)', color='b')
    ax2.plot(dates, roc, color='b', label='Momentum')
    ax2.tick_params(axis='y', labelcolor='b')
    ax2.legend(loc='upper left')

    plt.title('Stock Price and Momentum using Rate of Change (ROC) with Buy/Sell Signals')
    plt.tight_layout()
    plt.show()

ticker = "TSLA"
start_date = "2018-01-01"
end_date = "2023-12-30"

stock_data = fetch_stock_data(ticker, start_date, end_date)
prices = stock_data['Close']
momentum = calculate_momentum(prices)

plot_momentum(prices.index, prices, momentum)
				
			
momentum indicators ROC entreprenerdly

Figure. 3: The graph displays stock price along with its ROC indicator, showing buy signals (blue arrows) when ROC crosses above zero, indicating upward momentum, and sell signals (red arrows) when ROC crosses below zero, suggesting downward momentum. These signals help to visualize the price momentum and potential shifts in market trend.

2.4 Commodity Channel Index (CCI)

The CCI is a versatile indicator used to identify cyclical trends in securities. It compares the current typical price (TP) to the average TP over a specific period, usually 20 days.

The formula also incorporates the mean deviation (MD) of the TP during the same period to normalize the differences, making the CCI sensitive to variations in market behavior. The formula is given by:

Equation 4. CCI Formula: A momentum oscillator that measures the deviation of a security's price from its statistical mean, helping to identify cyclical trends and potential reversals.

Where the Typical Price (TP) is calculated as the average of the high, low, and close prices.

The CCI helps traders in two ways. It can spot new trends as they begin. It can also warn of potential market reversals by finding extreme price movements.

The key to leveraging the CCI effectively lies in understanding its behavior around the +100 and -100 thresholds, which indicate overbought and oversold conditions, respectively. 

Additionally, the concept of divergences — when the CCI and the price chart move in opposite directions — can be particularly revealing. For example, an asset’s price may hit new highs. If the CCI doesn’t follow, it suggests weakening momentum. This pattern could signal a potential reversal.

This divergence serves as a crucial signal. It suggests that despite the prevailing trend, underlying market strength is fading. This offers pontential entry or exit points for traders.

				
					import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import numpy as np

# Download historical data as dataframe
ticker = "ASML.AS"
df = yf.download(ticker, start='2020-01-01', end='2023-12-14')

# Calculate the Typical Price
df['TP'] = (df['High'] + df['Low'] + df['Close']) / 3

# Calculate the moving average of the Typical Price
window = 20  # Use a 20-day period for the moving average
df['TP_MA'] = df['TP'].rolling(window).mean()

# Calculate the Mean Deviation
df['MD'] = df['TP'].rolling(window).apply(lambda x: np.abs(x - x.mean()).mean())

# Calculate the Commodity Channel Index (CCI)
df['CCI'] = (df['TP'] - df['TP_MA']) / (0.015 * df['MD'])

# Initialize a new column for the CCI trend
df['CCI_trend'] = np.nan

# When CCI is above +100, set trend to +1
df.loc[df['CCI'] > 100, 'CCI_trend'] = 1

# When CCI is below -100, set trend to -1
df.loc[df['CCI'] < -100, 'CCI_trend'] = -1

# Forward fill the trend column. This way, once a threshold is crossed, the trend remains until the opposite threshold is crossed.
df['CCI_trend'].ffill(inplace=True)

# Identify the points where the CCI crosses the thresholds
df['crosses_above'] = ((df['CCI'] > 100) & (df['CCI'].shift(1) < 100) & (df['CCI_trend'].shift(1) != 1))
df['crosses_below'] = ((df['CCI'] < -100) & (df['CCI'].shift(1) > -100) & (df['CCI_trend'].shift(1) != -1))

# Plotting
fig, (ax1, ax2) = plt.subplots(2, figsize=(30, 8))

# Plot the closing price
df['Close'].plot(ax=ax1)
ax1.set_ylabel('Price')

# Plot vertical lines for CCI threshold crossings
ax1.vlines(df[df['crosses_above']].index, ymin=df['Close'].min(), ymax=df['Close'].max(), colors='g', linestyles='dashed', label='Crosses above +100')
ax1.vlines(df[df['crosses_below']].index, ymin=df['Close'].min(), ymax=df['Close'].max(), colors='r', linestyles='dashed', label='Crosses below -100')

# Correctly add scatter plot for buy and sell signals
# Note: The correction involves ensuring the scatter plots match the vertical line conditions
ax1.scatter(df[df['crosses_above']].index, df[df['crosses_above']]['Close'], color='g', label='Buy Signal', marker='^', s=100) # Green for buy
ax1.scatter(df[df['crosses_below']].index, df[df['crosses_below']]['Close'], color='r', label='Sell Signal', marker='v', s=100) # Red for sell

ax1.legend()

# Plot the CCI
df['CCI'].plot(ax=ax2, color='blue', label='CCI')
ax2.axhline(100, color='red', linestyle='--')  # Overbought line
ax2.axhline(-100, color='green', linestyle='--')  # Oversold line

# Repeat vertical lines for visual consistency across charts
ax2.vlines(df[df['crosses_above']].index, ymin=-200, ymax=200, colors='g', linestyles='dashed', label='Crosses above +100')
ax2.vlines(df[df['crosses_below']].index, ymin=-200, ymax=200, colors='r', linestyles='dashed', label='Crosses below -100')

ax2.set_ylabel('CCI')
ax2.legend()

plt.show()
				
			
CCI Momentum Indicators entreprenerdly

Figure. 4: The chart illustrates stock price movements along with the CCI, highlighting buy signals (green markers) when the CCI crosses above +100, suggesting an uptrend, and sell signals (red markers) when it crosses below -100, indicating a downtrend. These crossings, combined with the continuous CCI trend, provide insights into the market's momentum and potential shifts in trends.

2.5 Williams %R

Williams %R is a momentum indicator that reflects the level of the close relative to the highest high for a set period. 

It oscillates between 0 and -100, indicating overbought and oversold conditions. The formula for Williams %R is:

Equation 5. Williams %R Formula: Measures the current closing price in relation to the high and low over a specific period, identifying overbought and oversold conditions.

where H14​ is the highest high over the past 14 periods, L14​ is the lowest low over the same period, and C is the current close.

Williams %R offers a key insight beyond its basic use. It can identify momentum shifts before price movements.

Especially when used in conjunction with a moving average, Williams %R can provide a more nuanced view of market conditions. 

In an uptrend, a move above -80 after being below it can signal trend continuation. This movement above -80 presents a potential buying opportunity. Conversely, in a downtrend, a bounce off the -20 level can reaffirm selling pressure. 

Interpreting indicator signals in the overall market trend context adds depth to their utility. This allows more informed decisions based on short-term momentum and overall sentiment.

Related Articles:

Top 6 Volatility Indicators In Python

End-to-end Implementation with buy and sell signals. Indicators include Keltner Channels, Relative Volatility Index, and more

Top 9 Volume Indicators In Python

End-to-end Implementation with buy and sell signals. Indicators include VPT, VROC, CMF, Klinger Oscillator and VROC

Top 6 Trend Indicators In Python

End-to-end Implementation with buy and sell signals.
Prev Post

Meta Teaches AI to See the World Like Us With…

Next Post

Top 9 Volume Indicators in Python

post-bars
Mail Icon

Newsletter

Get Every Weekly Update & Insights

[mc4wp_form id=]

Leave a Comment