← Back to Home
Technical Indicators Average True Range (ATR)

Technical Indicators Average True Range (ATR)

Developed by the legendary technical analyst J. Welles Wilder Jr., the Average True Range (ATR) is a cornerstone indicator for measuring market volatility. A key characteristic of ATR is that it is not directional; it quantifies the degree of price movement or “choppiness” but does not indicate the trend’s direction.

The ATR calculation is based on the “True Range” (TR). The True Range aims to capture the full extent of price movement for a given period, including any price gaps that might occur when the market is closed (e.g., overnight or over a weekend).

For each period, the True Range (TR) is defined as the greatest of the following three values: 1. The difference between the current period’s High and its Low: TR_1 = \text{Current High} - \text{Current Low} 2. The absolute value of the difference between the current period’s High and the previous period’s Close: TR_2 = |\text{Current High} - \text{Previous Close}| 3. The absolute value of the difference between the current period’s Low and the previous period’s Close: TR_3 = |\text{Current Low} - \text{Previous Close}|

So, for each period: TR = \max(TR_1, TR_2, TR_3) TR = \max((\text{High} - \text{Low}), |\text{High} - \text{Previous Close}|, |\text{Low} - \text{Previous Close}|)

Once the True Range is calculated for each period, the Average True Range (ATR) is typically an N-period smoothed moving average of these TR values. J. Welles Wilder Jr. used a specific smoothing method (a form of exponential moving average) for his indicators, and this is generally what ATR calculations (including TA-Lib’s) employ. The common default period (N) for ATR is 14 periods.

Usage: ATR is a versatile tool used by traders and analysts in various aspects of trading:

TA-Lib Function: The Technical Analysis Library (TA-Lib) provides a function to calculate ATR:

talib.ATR(high_prices, low_prices, close_prices, timeperiod=14)

Code Example (Calculation & Plot with yfinance Data):

The following Python code demonstrates how to fetch HLC data using yfinance, calculate the ATR, and plot the price alongside the ATR indicator.

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 = "SPY"  # Example: SPDR S&P 500 ETF Trust
# 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"

try:
    data = yf.download(ticker_symbol, start=data_start_date, end=data_end_date, auto_adjust=False, progress=False)
    if isinstance(data.columns, pd.MultiIndex) and data.columns.nlevels > 1: # Droplevel based on user history
            data.columns = data.columns.droplevel(level=1)
except Exception as e:
    print(f"Error downloading data for {ticker_symbol}: {e}")
    data = pd.DataFrame() # Ensure data is an empty DataFrame on error

if data.empty or not all(col in data.columns for col in ['High', 'Low', 'Close']) or \
   any(data[col].isnull().all() for col in ['High', 'Low', 'Close']):
    print(f"Insufficient HLC data found for {ticker_symbol}. Exiting.")
else:
    # Prepare data: ensure no NaNs in HLC for the period used by TA-Lib
    hlc_data = data[['High', 'Low', 'Close']].dropna()

    high_prices = hlc_data['High']
    low_prices = hlc_data['Low']
    close_prices_for_atr = hlc_data['Close'] # Name distinct from plotting close prices

    # --- 2. Average True Range (ATR) Calculation ---
    time_period_atr = 14 # Standard period for ATR

    # ATR needs HLC data and sufficient lookback period for smoothing.
    # TA-Lib will output NaNs for the initial 'timeperiod' entries.
    if len(close_prices_for_atr) > time_period_atr:
        atr_values = talib.ATR(
            high_prices, low_prices, close_prices_for_atr,
            timeperiod=time_period_atr
        )
        indicator_name = f"ATR({time_period_atr})"
        print(f"\n--- {indicator_name} - Average True Range for {ticker_symbol} ---")

        valid_atr = atr_values[~np.isnan(atr_values)]
        if len(valid_atr) >= 5:
            print(f"Output {indicator_name} (last 5 valid): {valid_atr[-5:].round(2)}")
        elif len(valid_atr) > 0:
            print(f"Output {indicator_name} (all valid): {valid_atr.round(2)}")
        else:
            print(f"Output {indicator_name}: No valid ATR values calculated (all NaNs).")

        # Filter data for the desired plotting period
        atr_series = pd.Series(atr_values, index=hlc_data.index)
        
        plot_hlc_data = hlc_data[hlc_data.index >= plot_start_date]
        plot_atr_values = atr_series[plot_hlc_data.index] 
        plot_date_index = plot_hlc_data.index
        plot_close_prices = plot_hlc_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 (using 'Close' from the plotting data slice)
            axes[0].plot(plot_date_index, plot_close_prices, label=f'{ticker_symbol} 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 Average True Range
            axes[1].plot(plot_date_index, plot_atr_values, label=indicator_name, color='brown')
            axes[1].set_ylabel('ATR Value')
            axes[1].set_xlabel('Date')
            axes[1].legend(loc='upper left')
            axes[1].grid(True)

            plt.tight_layout()
            plt.show()
        else:
             print(f"No data available for the plotting period starting {plot_start_date}.")

    else:
        print(f"\nSkipping ATR plot for {ticker_symbol}: Insufficient data (need > {time_period_atr} points after cleaning).")
        if not hlc_data.empty:
             print(f"Cleaned data available for {len(hlc_data)} periods. ATR period is {time_period_atr}.")
Pasted image 20250603141127.png

Explanation of the Code:

  1. Import Libraries: Includes yfinance for data, talib for ATR, numpy, matplotlib.pyplot for plotting, and pandas.
  2. Data Fetching:
    • yf.download() retrieves High, Low, and Close (HLC) data. Your preference for auto_adjust=False and droplevel is incorporated. Basic error handling is added.
    • Rows with any missing HLC values are dropped using dropna() on the selected columns to ensure clean input for TA-Lib.
  3. Average True Range (ATR) Calculation:
    • time_period_atr is set (commonly 14).
    • A check if len(close_prices_for_atr) > time_period_atr: ensures sufficient data. TA-Lib’s ATR will produce NaN values for the initial timeperiod due to its smoothing calculation.
    • talib.ATR() computes the ATR values.
    • The last few valid ATR values are printed.
  4. Data Slicing for Plotting: The data is sliced to the plot_start_date for a clearer chart if a longer history was fetched. The ATR numpy array is converted to a Pandas Series with the correct index for this alignment.
  5. Plotting:
    • plt.subplots(2, 1, ...) creates two stacked subplots: one for price and one for ATR.
    • Price Plot (axes[0]): Displays the closing prices.
    • ATR Plot (axes[1]): Shows the calculated ATR values. Since ATR is a measure of volatility in absolute price terms, its scale will depend on the price level of the asset.
    • Standard plot elements like titles, labels, and legends enhance readability.
    • plt.tight_layout() adjusts subplot spacing.
    • plt.show() displays the generated chart.
  6. Insufficient Data Handling: Messages are printed if data is insufficient at various stages.

By providing a clear measure of an asset’s typical price range, the ATR empowers traders to make more informed decisions regarding risk management, position sizing, and strategy selection in varying market conditions.