Pine Script arrays: keep a list of prices, step by step
A normal Pine Script value holds one number per candle. An array holds a whole list. That's how a script remembers the last 5 places price turned around, not just the latest one.
What an array is
An array is a list. Like a row of numbered boxes, each holding one value.
Why would a chart script need a list? Because some questions are about many past things at once:
- "How many of the last 5 turning points sit above today's price?"
- "What's the average body size of the last 20 candles?"
- "Which of my saved levels is closest to price?"
A plain variable can't hold those. An array can. New to Pine Script? Start with Pine Script for beginners.
The moves you'll use most
Pine Script version 6 lets you write array actions with a dot, like list.push(5).
| Move | What it does |
|---|---|
| array.new<float>() | Makes an empty list of numbers |
| list.push(x) | Adds x to the end |
| list.unshift(x) | Adds x to the front |
| list.shift() | Removes the first item |
| list.pop() | Removes the last item |
| list.size() | How many items are in it |
| list.get(i) | Reads the item at position i |
| list.avg(), list.max(), list.min() | Average, biggest, smallest |
Positions start at 0. In a list of 5, the first is 0 and the last is 4.
Example 1: the average body of the last 20 candles
A candle's body is the part between the open and the close. This script keeps the last 20 body sizes in a list and plots their average:
//@version=6
indicator("Average body size")
count = input.int(20, "How many candles", minval=1, maxval=500)
var array<float> bodies = array.new<float>()
bodies.push(math.abs(close - open))
if bodies.size() > count
bodies.shift()
plot(bodies.avg(), "Average body", color=color.orange)
plot(math.abs(close - open), "This body", color=color.gray, style=plot.style_columns)
Line by line:
- Line 1 says this is Pine Script version 6.
- Line 2 names the script. No overlay, so it gets its own panel under the chart.
- Line 3 makes the list length a setting.
- Line 4 makes an empty list of numbers. var is vital: it means "make the list once and keep it". Without var, the list would start empty on every candle.
- Line 5 adds this candle's body size to the end. math.abs makes it positive, so red and green candles count the same.
- Line 6 checks if the list is now longer than 20.
- Line 7 if so, drops the oldest item from the front. The list always holds the newest 20.
- Line 8 plots the average of the list.
- Line 9 plots this candle's body as gray columns, so you can compare.
This "push to the end, shift off the front" pattern is called a rolling window. It's the most common use of arrays.
On a live candle, the script runs many times. Pine Script undoes the last run's changes each time, so the live candle is only counted once.
Example 2: the last 5 turning points
This one saves the last 5 places price turned down. Then it counts how many sit above the current close:
//@version=6
indicator("Turning points above price")
ph = ta.pivothigh(high, 5, 5)
var array<float> highs = array.new<float>()
if not na(ph)
highs.unshift(ph)
if highs.size() > 5
highs.pop()
above = 0
for level in highs
if level > close
above += 1
plot(above, "Levels above price", color=color.red, style=plot.style_stepline)
Line by line:
- Line 3 finds a high with 5 lower candles on each side. That's a swing high: a place price turned down. See Swing high and swing low. It's na (empty) on most candles. More on this in Pine Script swing highs and lows.
- Line 4 makes an empty list, once.
- Line 5 says "when a new turning point is found…"
- Line 6 puts it at the front. So position 0 is always the newest.
- Lines 7 and 8 drop the oldest from the end if there are more than 5.
- Line 9 starts a counter at 0.
- Line 10 is a loop: "for each level in the list…" It runs once per item.
- Lines 11 and 12 add 1 to the counter when that level is above the close.
- Line 13 plots the count as steps.
When the count drops to 0, price has closed above all 5 recent turning points. That's a simple way to see a Trend building. Compare it with higher highs and higher lows.
The repaint catch in Example 2
ta.pivothigh() can only confirm a turning point 5 candles later, once 5 lower candles have formed on the right. So the list updates 5 candles late.
That's honest: the script never uses candles it couldn't have seen. But if you draw the point back on the candle where it happened, the chart looks like it knew sooner than it did. Keep that in mind when you judge it on past charts.
The error everyone hits
"Index out of bounds." It means you asked for a position that doesn't exist. For example, highs.get(0) when the list is still empty.
Always check the size first:
newest = highs.size() > 0 ? highs.get(0) : na
Read it as: "if the list has anything in it, take item 0, otherwise nothing".
In version 6 there's a shortcut. and stops early when the first part is false. So this is safe too:
if highs.size() > 0 and close > highs.get(0)
label.new(bar_index, high, "Above newest level")
If the list is empty, Pine Script never reaches highs.get(0). (Put this in a script with overlay=true, so the label lands on the candles.) Labels are covered in Pine Script labels and lines.
Loops: for…in vs counting
for level in highs is the easy loop. It hands you each item in turn.
You can also count positions:
countAbove = 0
for i = 0 to highs.size() - 1
if highs.get(i) > close
countAbove += 1
Careful: if the list is empty, that's "0 to -1". Pine Script then counts down, and highs.get(0) breaks. Use for…in, or check the size first.
A list of levels isn't a list of trades. Knowing 3 old turning points sit above price is information. Whether it's a trade still depends on the bigger chart, your stop and your size.
Try it today
- Paste Example 2 into the Pine Editor on a computer. Save and add it to a daily chart.
- Scroll back. Find a stretch where the count fell to 0 and stayed there.
- Now change both 5s in line 3 to 10. Fewer, bigger turning points get saved. Does the picture change?
- Step through a month with bar replay. Notice each new level arrives 5 candles late.
Keep lists short. Arrays can hold thousands of items, but a chart script rarely needs more than 50. Small lists are faster and easier to check.
Learning from a real script
Lists of levels are how a finished indicator remembers structure. Our course includes the full source code of the StructureX chart tool to learn from and customize. It's source code, not a ready-made trading bot.
Common mistakes
- Forgetting var. The list empties on every candle and never grows.
- Never trimming it. Without shift() or pop(), it grows forever.
- Reading an empty list. Check size() first.
- Counting loops on empty lists. Use for…in instead.
- Mixing up front and end. push adds to the end; unshift adds to the front.
Questions
What is an array in Pine Script?
A list of values of one type, like numbers. It lets a script remember many past values at once, such as the last 5 turning points.
How do I add to an array in Pine Script?
Use list.push(value) to add to the end, or list.unshift(value) to add to the front. Make the list with var so it keeps its items.
Why does my Pine Script array reset every bar?
The array was made without var. Write var array<float> list = array.new<float>() so it's made once.
How do I fix "index out of bounds" in Pine Script?
You asked for a position that doesn't exist. Check list.size() > 0 before list.get(), and remember positions start at 0.
Want the next step? We'll walk you through it.
Four free lessons by email, one every few days — each with a 10-minute exercise on a real chart.
Send Me the Free Lessons 4 short emails · No spam · Unsubscribe anytimeOr see the whole method, step by step:
See What’s Inside the Course Try everything free first. Decide when you're ready. · Open the free chart tool (works in the TradingView phone app)One chart idea a day, in plain English. Follow along: