Edgoro
Indicator · Language features

Pine Script arrays: a rolling window example (v6)

Use arrays in Pine Script v6: keep one value per day with push() and shift(), average and rank them, avoid out-of-bounds errors and arrays rebuilt every bar.

Short answer

Declare the array with var so it survives from bar to bar (var array<float> a = array.new<float>()), add values with a.push(), drop the oldest with a.shift() once it's longer than the window, and read it with methods such as a.avg(), a.median() or a.get(i). Arrays are the tool when you keep values selectively, such as one per day or one per pivot, rather than one per bar.

The script

An average daily range built from intraday bars: one value per completed day goes into an array, and today's range is shown as a percentage of the average. Paste it into a new indicator in the Pine Editor, then save and add it to the chart.

Pine Script v6
//@version=6
indicator("Average daily range (arrays)", overlay = false)

days = input.int(14, "Days in the average", minval = 1, maxval = 250)

// One value per completed day, oldest first
var array<float> ranges = array.new<float>()
var float dayHigh = na
var float dayLow  = na

bool newDay = timeframe.change("D")
if newDay
    // Store the day that just ended, then start the new one
    if not na(dayHigh)
        ranges.push(dayHigh - dayLow)
        if ranges.size() > days
            ranges.shift()
    dayHigh := high
    dayLow  := low
else
    dayHigh := math.max(nz(dayHigh, high), high)
    dayLow  := math.min(nz(dayLow, low), low)

bool  ready    = ranges.size() > 0
float adr      = ready ? ranges.avg() : na
float medRange = ready ? ranges.median() : na
float widest   = ready ? ranges.max() : na
float todayPct = adr > 0 ? (dayHigh - dayLow) / adr * 100 : na

plot(todayPct, "Today's range, % of ADR", todayPct >= 100 ? color.orange : color.teal, style = plot.style_columns)
hline(100, "100% of ADR", color.gray, hline.style_dashed)
plot(adr, "ADR", display = display.data_window)
plot(medRange, "Median daily range", display = display.data_window)
plot(widest, "Widest day in the window", display = display.data_window)
plot(ranges.size(), "Days stored", display = display.data_window)

How it works

  • var array<float> ranges = array.new<float>() creates an empty array of floats once. Without var it would be a new, empty array on every bar.
  • One value per day. timeframe.change("D") is true on the first bar of each day. Only then is the finished day's range pushed, so the array holds days, not bars. A moving average of bars like ta.sma(high - low, 14) couldn't do that on an intraday chart.
  • The window. push() adds at the end and shift() removes the first, oldest element once there are more than days.
  • Reading it. avg(), median() and max() summarize the whole array in one call. ready guards them on the first day, before anything is stored.
  • Today so far. dayHigh - dayLow is the range of the current, unfinished day, compared with the average of finished days. The columns turn orange once today has moved more than an average day.
  • ranges.size() in the Data Window shows how many days are in the average; it stops growing at days.

Pitfalls

  • An array without var. array<float> ranges = array.new<float>() builds a new empty array on every bar, so it never holds more than one value and the average is just the last range.
  • Reading an empty array. ranges.get(0), ranges.first() or ranges.last() on an empty array stops the script with Index is out of bounds. Check size() first, as ready does.
  • Loops from 0 to size() - 1. On an empty array, for i = 0 to ranges.size() - 1 runs from 0 to -1, and Pine counts down when the end is below the start, so the loop runs twice and get(0) fails. Use for r in ranges, or check the size first.
  • Sorting the array you shift. ranges.sort() reorders the values in place, so shift() would then remove the smallest value, not the oldest. Sort a copy: array<float> sorted = ranges.copy().
  • Arrays that only grow. An array that receives a value every bar and is never trimmed keeps growing toward the 100,000-element limit, and returning arrays from request.security() copies them on every bar (Memory limits exceeded). Trim with shift().

Variations

Walk through the array. for r in ranges reads each element in order, here to count the stored days wider than the average, and percentile_nearest_rank() gives a percentile without sorting anything:

// How many of the stored days were wider than the average
int wideDays = 0
for r in ranges
    if r > adr
        wideDays += 1
// The range that 80% of the stored days did not exceed
float p80 = ready ? ranges.percentile_nearest_rank(80) : na
plot(wideDays, "Days wider than the ADR", display = display.data_window)
plot(p80, "80th percentile range", display = display.data_window)

Index and value together. for [i, r] in ranges gives the position too: i = 0 is the oldest day and i = ranges.size() - 1 the most recent. The loops example covers the other loop forms.

Questions

How do I get the last element of an array in Pine Script?

Call a.last(), or a.get(a.size() - 1). Both fail on an empty array, so check a.size() > 0 first. a.first() and a.get(0) return the oldest element of an array filled with push().

Can I use [1] to get an array's values from the previous bar?

Not for a var array: it's the same array on every bar, so ranges[1] points to the same object, with today's contents. To keep past states, store the values you need in separate variables or in another array.

What is the maximum size of an array in Pine Script?

100,000 elements. Long before that, large arrays slow the script down and use memory, so keep them trimmed to the window you need with shift() or pop().

Every script on this page was checked with Edgoro Pine Check (Pine Script v6 rules). Pine Check is a static checker, not TradingView's compiler: TradingView compiles the script when you save it in the Pine Editor. The scripts are examples to learn from, not trading systems. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.

Need a different version of this script?

Describe the change in plain English and Edgoro writes the Pine Script v6 for you, checked with Pine Check. For example:

“Show today's range as a percentage of the average daily range of the last 20 days on an intraday chart, and alert me when it passes 100%.”