ATR Plots + Overlay

ATR Plots + Overlay
This tool calculates and displays Average True Range (ATR)-based levels on your chart for any selected timeframe, giving traders a quick visual reference for expected price movement relative to the most recent bar’s open price. It plots guide levels above and below that open and shows how much of the typical ATR-based range has already been covered—all in one interactive table and on-chart overlay.



What It Does
ATR Calculation:
Uses true range data over a user-defined period (default 14), smoothed via RMA, SMA, EMA, or WMA, on the selected timeframe (e.g., 1h, 4h, daily) to calculate the ATR value.

Projected Levels:
Plots four reference levels relative to the open price of the most recent bar on the chosen timeframe:

+100% ATR: Open + ATR

+50% ATR: Open + 50% of ATR

−50% ATR: Open − 50% of ATR

−100% ATR: Open − ATR

Coverage %:
Tracks high and low prices for the current session on the selected timeframe and calculates what percentage of the ATR has already been covered:

Coverage % = (High − Low) ÷ ATR × 100

Interactive Table:
Shows the ATR value and current coverage percentage in a customizable table overlay. Position, color scheme, borders, transparency, and an optional empty top row are all adjustable via settings.



Customization Options
Table Settings:
Position the table (top/bottom × left/right).

Customize background color, text color, border color, and thickness.

Optionally add an empty top row for spacing.

Line Settings:
Choose color, line style (solid/dotted/dashed), and width.

Lines automatically update with each new bar on the selected timeframe, anchored to that bar’s open price.

General Inputs:
ATR length (number of bars).

Smoothing method (RMA, SMA, EMA, WMA).

Timeframe selection for ATR calculations (e.g., 15m, 1h, Daily).



How to Use It for Trading
Measure Volatility: Quickly gauge the expected price movement based on ATR for any timeframe.

Identify Overextension: Use the coverage % to see how much of the expected ATR range is already consumed.

Plan Entries & Exits: Align trade targets and stops with ATR levels for more objective planning.

Visual Reference: Horizontal guide lines and table update automatically as new bars form, keeping information clear and actionable.



Ideal For
Intraday traders using ATR levels to frame trades.

Swing traders wanting ATR-based reference points for larger timeframes.

Anyone seeking a volatility-based framework for planning stops, targets, or identifying overextended conditions.6 days ago

Release Notes

Fixed time frame issue – now correctly plots lines based on open for selected time frame when chart time frame is higher than selected ATR time frame.

// ATR Plots + Overlay
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © abbadon9 [If you modify this script please acknowledge me.]

 //@version=6
indicator("ATR Plots + Overlay (MTF Fixed)", shorttitle="ATR+", overlay=true)

// --- Input start --- //
length       = input.int(title="ATR Length", defval=14, minval=1, group='ATR Settings')
smoothing    = input.string(title="Smoothing", defval="WMA", options=["RMA","SMA","EMA","WMA"], group='ATR Settings')
atrTF        = input.timeframe("Chart", "ATR Timeframe", options=["Chart","1","3","5","15","30","60","120","240","D","W","M"], group="ATR Settings")

ma_function(source, length) =>
    switch smoothing
        "RMA" => ta.rma(source, length)
        "SMA" => ta.sma(source, length)
        "EMA" => ta.ema(source, length)
        => ta.wma(source, length)
    
m = float(1)

// --- ATR calculation ---
a = ma_function(ta.tr(true), length) * m 

// === Table Settings ===
showTopSpacer = input.bool(true, "Add Empty Top Spacer Row", group='Table Settings')
dash_loc    = input.string("Bottom Right","Table Position", options=["Top Right","Bottom Right","Top Left","Bottom Left", "Middle Right","Bottom Center"], group='Table Settings')
text_size   = input.string('Normal',"Table Size", options=["Tiny","Small","Normal","Large"]  ,group='Table Settings')
col_H       = input.color(color.new(color.rgb(94, 49, 157), 100), "Table Color", inline="0", group='Table Settings')
Col_txt     = input.color(color.rgb(255, 255, 255, 25), "Text Color", inline="0", group='Table Settings')

// === Border Settings ===
borderColor = input.color(color.new(color.rgb(94, 49, 157), 100), "Table Border Color", group="Table Settings")
borderWidth = input.int(1, "Table Border Width", minval=0, maxval=5, group="Table Settings")

// === Line Settings ===
col_D       = input.color(color.rgb(255, 235, 59, 25), "Close", inline="0", group='Line Settings') 
col_P100    = input.color(color.rgb(242, 54, 69, 25), "+100%", inline="0", group='Line Settings') 
col_M100    = input.color(color.rgb(242, 54, 69, 25), "-100%", inline="0", group='Line Settings')
col_P50     = input.color(color.rgb(255, 152, 0, 25), "+50%", inline="0", group='Line Settings') 
col_M50     = input.color(color.rgb(255, 152, 0, 25), "-50%", inline="0", group='Line Settings')  
styleOption = input.string("dotted", "Line Style", options=["solid", "dotted", "dashed"], group='Line Settings' )
lineWidth   = input.int(1, "Line Width", minval=1, maxval=5, group='Line Settings')

lineStyle = styleOption == "dotted" ? line.style_dotted :
     styleOption == "dashed" ? line.style_dashed :
         line.style_solid       

// --- Definitions --- //
CLS         = close
ACLS        = a
EMA         = ta.ema(close,27)

// --- ATR Data based on selected timeframe --- //
atrD        = request.security(syminfo.tickerid, atrTF == "Chart" ? timeframe.period : atrTF, ACLS)
openTF      = request.security(syminfo.tickerid, atrTF == "Chart" ? timeframe.period : atrTF, open)
timeTF      = request.security(syminfo.tickerid, atrTF == "Chart" ? timeframe.period : atrTF, time)

// Approximate end time for the higher timeframe candle
getTFseconds(tf) =>
    tf == "1" ? 60 : tf == "3" ? 180 : tf == "5" ? 300 : tf == "15" ? 900 : tf == "30" ? 1800 : tf == "60" ? 3600 : tf == "120" ? 7200 : tf == "240" ? 14400 : tf == "D" ? 86400 : tf == "W" ? 604800 : tf == "M" ? 2592000 : timeframe.in_seconds(timeframe.period)

tfSeconds = atrTF == "Chart" ? timeframe.in_seconds(timeframe.period) : getTFseconds(atrTF)
timeTF_end = timeTF + (tfSeconds * 1000)

// --- Levels ---
DP100       = (atrD * m * 1 )+ openTF
DP050       = (atrD * m * 0.50 )+ openTF
DM100       = openTF- (atrD * m * 1)
DM050       = openTF- (atrD * m * 0.50)

// --- Range to ATR Ratio --- //
intradayHigh = request.security(syminfo.tickerid, atrTF == "Chart" ? timeframe.period : atrTF, high)
intradayLow  = request.security(syminfo.tickerid, atrTF == "Chart" ? timeframe.period : atrTF, low)
intradayRange = intradayHigh - intradayLow
rangeToAtrRatio = (intradayRange / atrD) * 100  

// --- Draw Lines --- //
var line CDLine    = line.new(na, na, na, na, xloc=xloc.bar_time, style=lineStyle, width=lineWidth, color=col_D,      extend=extend.both)
var line DP100Line = line.new(na, na, na, na, xloc=xloc.bar_time, style=lineStyle, width=lineWidth, color=col_P100,   extend=extend.both)
var line DM100Line = line.new(na, na, na, na, xloc=xloc.bar_time, style=lineStyle, width=lineWidth, color=col_M100,   extend=extend.both)
var line DP50Line  = line.new(na, na, na, na, xloc=xloc.bar_time, style=lineStyle, width=lineWidth, color=col_P50,    extend=extend.both)
var line DM50Line  = line.new(na, na, na, na, xloc=xloc.bar_time, style=lineStyle, width=lineWidth, color=col_M50,    extend=extend.both)

isHigherChartTF = timeframe.in_seconds(timeframe.period) > tfSeconds

if ta.change(timeTF) != 0
    x1 = isHigherChartTF ? time : timeTF
    x2 = isHigherChartTF ? time + tfSeconds * 1000 : timeTF_end

    line.set_xy1(CDLine,    x1, openTF)
    line.set_xy2(CDLine,    x2, openTF)

    line.set_xy1(DP100Line, x1, DP100)
    line.set_xy2(DP100Line, x2, DP100)

    line.set_xy1(DM100Line, x1, DM100)
    line.set_xy2(DM100Line, x2, DM100)

    line.set_xy1(DP50Line,  x1, DP050)
    line.set_xy2(DP50Line,  x2, DP050)

    line.set_xy1(DM50Line,  x1, DM050)
    line.set_xy2(DM50Line,  x2, DM050)


// --- Table Position & Size --- //
max    = 160    
min    = 10     
var table_position = dash_loc   == 'Top Left'       ? position.top_left :
  dash_loc                      == 'Bottom Left'    ? position.bottom_left :
  dash_loc                      == 'Middle Right'   ? position.middle_right :
  dash_loc                      == 'Bottom Center'  ? position.bottom_center :
  dash_loc                      == 'Top Right'      ? position.top_right : position.bottom_right
var table_text_size = text_size == 'Tiny'           ? size.tiny :
  text_size                     == 'Small'          ? size.small :
  text_size                     == 'Normal'         ? size.normal : size.large

// === Table with configurable border ===
var t = table.new(table_position, 15, math.abs(max-min)+2,
  frame_color   = borderColor,
  frame_width   = borderWidth,
  border_color  = borderColor,
  border_width  = borderWidth)

if barstate.islast
    rowOffset = showTopSpacer ? 1 : 0

    if showTopSpacer
        table.cell(t, 4, 1, "", text_color=Col_txt, text_size=table_text_size, bgcolor=color.new(color.black, 100))
        table.cell(t, 6, 1, "", text_color=Col_txt, text_size=table_text_size, bgcolor=color.new(color.black, 100))

    table.cell(t,4,5+rowOffset,' ATR ',text_color=Col_txt,text_size=table_text_size,bgcolor=col_H)
    table.cell(t,4,6+rowOffset,' COVERED ',text_color=Col_txt,text_size=table_text_size,bgcolor=col_H )

    table.cell(t,6,5+rowOffset, str.tostring(atrD, ' #.### '),text_color=Col_txt,text_size=table_text_size,bgcolor=col_H)
    table.cell(t,6,6+rowOffset, str.tostring(rangeToAtrRatio, ' #.##') + "%", text_color=Col_txt, text_size=table_text_size, bgcolor=col_H)