Skip to content

Free TradingView indicator

Average Daily Range

Projects the average daily range from the day open and shows how much of the ADR has been used so far today.

Volatilityv1.0TradingView (Pine Script v6) Non-repainting signals Alerts

What it does

The average daily range (ADR) is the average distance between the high and low of recent days. It gives a realistic sense of how far an instrument usually travels in one day. This indicator draws ADR projections from the day open and shows how much of the typical range has already been covered.

That helps answer practical questions: is a target realistic for today, has the market already made its usual move, and how wide should a daily stop be relative to normal volatility?

How it works

  1. ADR = simple average of (high − low) over the last N completed daily bars. The current day is never included.
  2. The daily values are requested with lookahead off and the documented one-bar offsets, so the ADR for a day is fixed when the day starts.
  3. Day open, day high and day low are tracked from the chart's own bars, starting at the first bar of each daily session.
  4. Projections: open + ADR, open − ADR, open + ADR ÷ 2 and open − ADR ÷ 2.
  5. Optional range-remaining targets: today's low + ADR and today's high − ADR. These move as the day extends.
  6. ADR % used = (today's high − today's low) ÷ ADR × 100.
  7. The panel shows ADR in price, in pips for forex symbols quoted with 5 or 3 decimals (one pip = ten ticks) and in ticks for other symbols.

Illustrative example: if a pair's 14-day ADR is 0.0080 (80 pips) and today's range so far is 0.0060, the panel shows 75% used.

How to use it

  • Target sanity check: if 90% of the ADR is already used, a further 50-pip target in the same direction asks for an unusually large day.
  • Late entries: many intraday traders become more cautious about chasing moves once the day's range reaches 100% of ADR.
  • Stops: comparing your stop-loss distance with the ADR shows whether it sits inside normal daily noise.
  • Range-remaining targets: after the low is set, low + ADR shows where an average-sized day would end.

Convert ADR distances to money with the Pip Calculator.

Settings

InputDefaultWhat it changes
ADR length (days)14Days averaged.
Show day open +/- full ADRonOuter projections.
Show day open +/- half ADRonInner projections.
Show day openonOpen line.
Show range-remaining targetsoffDynamic low + ADR and high − ADR.
Show ADR panel / Panel positionon / Top rightInfo table.
Colorsred / orange / grayLine colors.

Limitations

  • ADR is an average. Many days are much smaller or larger, especially around major news.
  • The day boundary comes from the data feed and can differ from your broker's server time, which changes the day open and range.
  • Day open, high and low are taken from chart bars, so the first partial day on the chart is incomplete.
  • Projections from the open assume nothing about direction; price rarely reaches both sides in one day.
  • Requires an intraday chart.

Pine Script source

In TradingView open Pine Editor, paste the code, click Save and then Add to chart.

FXP_AverageDailyRange.pine

88 lines

// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/average-daily-range/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
indicator("FXP Average Daily Range", shorttitle = "FXP ADR", overlay = true)

// ── Inputs ─────────────────────────────────────────────────────────────
grpA      = "ADR"
adrLen    = input.int(14, "ADR length (days)", minval = 1, maxval = 200, group = grpA)

grpL      = "Projections"
showFull  = input.bool(true, "Show day open +/- full ADR", group = grpL)
showHalf  = input.bool(true, "Show day open +/- half ADR", group = grpL)
showOpen  = input.bool(true, "Show day open", group = grpL)
showDyn   = input.bool(false, "Show range-remaining targets (low + ADR, high - ADR)", group = grpL)

grpVis    = "Display"
showTbl   = input.bool(true, "Show ADR panel", group = grpVis)
tblPos    = input.string("Top right", "Panel position", options = ["Top right", "Bottom right", "Top left", "Bottom left"], group = grpVis)
fullCol   = input.color(color.red, "Full ADR color", group = grpVis)
halfCol   = input.color(color.orange, "Half ADR color", group = grpVis)
openCol   = input.color(color.gray, "Day open color", group = grpVis)

if not timeframe.isintraday
    runtime.error("Average Daily Range projections need an intraday chart.")

// ── ADR from completed daily bars (non-repainting) ─────────────────────
int htfIdx = barstate.isrealtime ? 1 : 0
int chIdx  = barstate.isrealtime ? 0 : 1
float adrRaw = request.security(syminfo.tickerid, "D", ta.sma(high - low, adrLen)[htfIdx], lookahead = barmerge.lookahead_off)
float adr    = adrRaw[chIdx]

// ── Today's open, high and low from chart bars ─────────────────────────
bool newDay = timeframe.change("D")
var float dOpen = na
var float dHigh = na
var float dLow  = na
if newDay
    dOpen := open
    dHigh := high
    dLow  := low
else
    dHigh := math.max(nz(dHigh, high), high)
    dLow  := math.min(nz(dLow, low), low)

float usedPct = adr > 0 and not na(dOpen) ? (dHigh - dLow) / adr * 100.0 : na
float upFull  = dOpen + adr
float dnFull  = dOpen - adr
float upHalf  = dOpen + adr / 2.0
float dnHalf  = dOpen - adr / 2.0
float upDyn   = dLow + adr
float dnDyn   = dHigh - adr

// ── Plots (break at each new day) ──────────────────────────────────────
plot(showOpen and not newDay ? dOpen : na, "Day open", color = openCol, style = plot.style_linebr)
plot(showFull and not newDay ? upFull : na, "Open + ADR", color = fullCol, linewidth = 2, style = plot.style_linebr)
plot(showFull and not newDay ? dnFull : na, "Open - ADR", color = fullCol, linewidth = 2, style = plot.style_linebr)
plot(showHalf and not newDay ? upHalf : na, "Open + ADR/2", color = halfCol, style = plot.style_linebr)
plot(showHalf and not newDay ? dnHalf : na, "Open - ADR/2", color = halfCol, style = plot.style_linebr)
plot(showDyn and not newDay ? upDyn : na, "Low + ADR", color = color.new(fullCol, 50), style = plot.style_circles, linewidth = 1)
plot(showDyn and not newDay ? dnDyn : na, "High - ADR", color = color.new(fullCol, 50), style = plot.style_circles, linewidth = 1)
plot(adr, "ADR", display = display.data_window)
plot(usedPct, "ADR % used", display = display.data_window)

// ── Panel ──────────────────────────────────────────────────────────────
// Pip size for forex symbols quoted with 5 or 3 decimals (one pip = ten ticks); ticks otherwise.
int   decimals = math.round(-math.log10(syminfo.mintick))
bool  isFx     = syminfo.type == "forex"
float pipSize  = isFx and (decimals == 5 or decimals == 3) ? syminfo.mintick * 10 : syminfo.mintick
string unitTxt = isFx ? "pips" : "ticks"

string posStr = tblPos == "Top right" ? position.top_right : tblPos == "Bottom right" ? position.bottom_right : tblPos == "Top left" ? position.top_left : position.bottom_left
var table tbl = table.new(posStr, 2, 3, bgcolor = color.new(color.black, 80), border_width = 1)
if barstate.islast and showTbl
    table.cell(tbl, 0, 0, "ADR (" + str.tostring(adrLen) + "d)", text_color = color.white, text_size = size.small)
    table.cell(tbl, 1, 0, na(adr) ? "n/a" : str.tostring(adr, format.mintick), text_color = color.white, text_size = size.small)
    table.cell(tbl, 0, 1, "ADR (" + unitTxt + ")", text_color = color.white, text_size = size.small)
    table.cell(tbl, 1, 1, na(adr) ? "n/a" : str.tostring(adr / pipSize, "#.#"), text_color = color.white, text_size = size.small)
    table.cell(tbl, 0, 2, "Used today", text_color = color.white, text_size = size.small)
    table.cell(tbl, 1, 2, na(usedPct) ? "n/a" : str.tostring(usedPct, "#") + "%", text_color = usedPct >= 100 ? color.orange : color.white, text_size = size.small)

// ── Alerts (closed bars) ───────────────────────────────────────────────
bool reachedAdr = barstate.isconfirmed and usedPct >= 100 and not (usedPct[1] >= 100) and not newDay
bool hitUpFull  = barstate.isconfirmed and high >= upFull and not (high[1] >= upFull[1]) and not newDay
bool hitDnFull  = barstate.isconfirmed and low <= dnFull and not (low[1] <= dnFull[1]) and not newDay
alertcondition(reachedAdr, "Day range reached ADR", "FXP ADR: today's range has reached 100% of the ADR on {{ticker}}")
alertcondition(hitUpFull, "Price reached open + ADR", "FXP ADR: price reached the day open + ADR projection on {{ticker}} {{interval}}")
alertcondition(hitDnFull, "Price reached open - ADR", "FXP ADR: price reached the day open - ADR projection on {{ticker}} {{interval}}")

Key inputs

Key inputs of Average Daily Range
InputDefaultDescription
ADR length (days)14Number of completed days averaged.
Show day open +/- full ADRtrueLines one full ADR above and below the day open.
Show day open +/- half ADRtrueLines half an ADR above and below the day open.
Show day opentrueLine at the first price of the day.
Show range-remaining targets (low + ADR, high - ADR)falseDynamic levels that update with the day's high and low.
Show ADR paneltrueTable with the ADR in price, pips or ticks, and the percentage used today.

Educational tools, not advice. Indicators and Expert Advisors describe price; they do not predict it, and past behaviour does not guarantee future results. Test everything on a demo account first and never risk money you cannot afford to lose. See our disclaimer.

Keep exploring

Related tradingview indicators