How to Calculate Support and Resistance Levels Using Python: A Step-by-Step Guide

·

Support and resistance levels are foundational concepts in technical analysis, offering traders valuable insights into potential price reversal zones. These levels help identify where buying interest (support) or selling pressure (resistance) may dominate, making them essential for determining optimal entry and exit points in trading.

Whether you're analyzing 5-minute intraday charts or long-term daily bars, understanding how to programmatically detect these levels can significantly enhance your market analysis. In this comprehensive guide, we’ll walk through a Python-based approach to automatically calculate and visualize both support and resistance levels using real-world stock data from Apple (AAPL).

Disclaimer: This article is for educational purposes only. It does not constitute investment advice, nor is it provided by a licensed financial advisor.

Why Automate Support and Resistance Detection?

Manually drawing support and resistance lines on a chart is subjective and time-consuming. By automating the process with Python, we introduce consistency, scalability, and objectivity—key traits for systematic trading strategies.

Using libraries like yahooquery, pandas, scipy, and mplfinance, we can:


Step 1: Install and Import Required Libraries

Begin by installing the necessary packages:

pip install yahooquery pandas scipy mplfinance numpy

Now import them in your script:

import yahooquery as yq
import pandas as pd
import numpy as np
import scipy as sp
import mplfinance as mpf

Step 2: Fetch Historical Price Data

We'll use Apple (AAPL) as our example. Retrieve daily OHLC (Open, High, Low, Close) data starting from January 2022:

bars = yq.Ticker('AAPL').history(start='2022-01-01', interval='1d').reset_index(level=0, drop=True)
bars.index = pd.to_datetime(bars.index)

This creates a clean DataFrame containing daily price bars—perfect for technical analysis.

👉 Discover powerful tools to backtest your trading strategies using real market data


Step 3: Visualize the Base Candlestick Chart

Before detecting levels, visualize the raw price action:

mpf.plot(bars, type='candle', style='charles', title='AAPL Candlestick Chart', volume=True)

This gives context and helps validate the accuracy of our algorithm later.


Step 4: Identify Strong Resistance Levels

We use scipy.signal.find_peaks to detect local maxima in the 'high' price series. Key parameters include:

For strong resistance levels, we set conservative thresholds:

strong_peak_distance = 60
strong_peak_prominence = 20

strong_peaks, _ = sp.signal.find_peaks(
    bars['high'],
    distance=strong_peak_distance,
    prominence=strong_peak_prominence
)

strong_peaks_values = bars.iloc[strong_peaks]["high"].values.tolist()

# Include 52-week high as an additional strong resistance
yearly_high = bars["high"].iloc[-252:].max()
strong_peaks_values.append(yearly_high)

These values represent major resistance zones where price has struggled to break through.

Visualizing Strong Resistance Lines

Overlay these levels on the candlestick chart:

add_plot = [
    mpf.make_addplot(np.full(bars.shape[0], r), color='r', linestyle='--')
    for r in strong_peaks_values
]

mpf.plot(
    bars,
    type='candle',
    style='charles',
    title='AAPL: Strong Resistance Levels',
    volume=True,
    addplot=add_plot
)

This provides a clear view of long-term resistance—ideal for swing and position traders.


Step 5: Detect General (Short-Term) Resistance Levels

While strong peaks capture major turning points, short-term traders benefit from more frequent resistance zones. We define general peaks with looser criteria but require multiple rejections at similar price levels.

Key steps:

  1. Find all minor peaks with shorter distance
  2. Group nearby peaks using a width threshold
  3. Assign a "rank" based on how many times price revisits a level
  4. Only accept levels rejected at least resistance_min_pivot_rank times
peak_distance = 5
peak_rank_width = 2
resistance_min_pivot_rank = 3

peaks, _ = sp.signal.find_peaks(bars['high'], distance=peak_distance)
peak_to_rank = {peak: 0 for peak in peaks}

for i, current_peak in enumerate(peaks):
    current_high = bars.iloc[current_peak]["high"]
    for previous_peak in peaks[:i]:
        if abs(current_high - bars.iloc[previous_peak]["high"]) <= peak_rank_width:
            peak_to_rank[current_peak] += 1

Now filter and merge:

resistances = strong_peaks_values.copy()

for peak, rank in peak_to_rank.items():
    if rank >= resistance_min_pivot_rank:
        resistances.append(bars.iloc[peak]["high"] + 1e-3)

resistances.sort()

# Merge close levels into bins
resistance_bins = []
current_bin = [resistances[0]]

for r in resistances[1:]:
    if r - current_bin[-1] < peak_rank_width:
        current_bin.append(r)
    else:
        resistance_bins.append(current_bin)
        current_bin = [r]
resistance_bins.append(current_bin)

# Average each bin for cleaner levels
resistances = [np.mean(bin) for bin in resistance_bins]

👉 Learn how algorithmic trading can help you act on support/resistance signals faster


Step 6: Calculate Support Levels

The method for finding support is nearly identical—just invert the low prices to detect troughs:

troughs, _ = sp.signal.find_peaks(-bars['low'], distance=peak_distance)

Repeat the same ranking and binning logic using bars['low'] instead of highs. The resulting values represent key support zones where buying interest has historically emerged.

Final visualization includes both support (green dashed lines) and resistance (red dashed lines):

add_plots = [
    mpf.make_addplot(np.full(bars.shape[0], s), color='g', linestyle='--') for s in supports
] + [
    mpf.make_addplot(np.full(bars.shape[0], r), color='r', linestyle='--') for r in resistances
]

mpf.plot(
    bars,
    type='candle',
    style='charles',
    title='AAPL: Support & Resistance Levels',
    volume=True,
    addplot=add_plots
)

Core Keywords

These keywords naturally appear throughout the article, enhancing SEO while maintaining readability.


Frequently Asked Questions

What are support and resistance levels?

Support is a price level where demand is strong enough to prevent further declines. Resistance is where supply overwhelms demand, halting upward movement. These levels reflect market psychology and are crucial for timing trades.

Can this method work on other timeframes?

Yes! The same logic applies to any timeframe—simply adjust the distance and prominence parameters accordingly. For example, intraday traders might use 5-minute bars with smaller thresholds.

How accurate is automated detection compared to manual drawing?

Automated detection removes subjectivity and ensures consistency across assets. While no method is perfect, combining statistical thresholds with price recurrence improves reliability over purely visual methods.

Why use both strong and general peaks?

Strong peaks identify major structural levels relevant for long-term investors. General peaks highlight short-term congestion zones useful for day or swing traders. Together, they offer a complete picture.

Can I apply this to cryptocurrencies?

Absolutely. The same principles apply to crypto price charts. Just replace the data source with one supporting crypto (e.g., Binance API), and keep the rest of the logic unchanged.

How do I avoid overfitting the parameters?

Use walk-forward analysis or out-of-sample testing. Start with default values (like those shown here), then fine-tune based on performance across multiple assets and time periods—not just one stock.


By integrating code-driven analysis into your workflow, you gain a scalable edge in identifying high-probability trade setups. Whether you're building a full algorithmic system or just enhancing your charting toolkit, mastering support and resistance detection in Python is a powerful skill.

👉 Start applying these insights with advanced trading tools that support automated strategies