Loops#

Pine Script provides two loop structures: for and while. Since Pine Script executes bar by bar, loops are typically used to repeat calculations within a single bar — for example, finding the highest point over the past N bars, or batch-processing array elements.

Note: Loops increase computation time per bar. Using deeply nested loops on charts with many bars may cause script timeouts. It is recommended to use Pine Script’s built-in ta.* functions first, as they are optimized for series calculations.

for Loop#

Basic Syntax#

for variable = startValue to endValue
    // loop body

The for loop increments from the start value to the end value (inclusive), stepping by 1 each time:

//@version=6
indicator("for Loop Demo")

// Manually compute the sum of closing prices for the past 5 bars
var float total = 0.0
total := 0.0
for i = 0 to 4
    total := total + close[i]

plot(total / 5, "5-Bar Average Price")

by — Custom Step#

Use by to specify a custom increment (can be negative for counting down):

// Increment by 2
for i = 0 to 10 by 2
    // i = 0, 2, 4, 6, 8, 10

// Count down: from 10 to 0
for i = 10 to 0 by -1
    // i = 10, 9, 8, ..., 0

for…in — Iterating Over Arrays#

for...in retrieves each element of an array one by one:

//@version=6
indicator("for-in Demo")

prices = array.from(close, close[1], close[2], close[3], close[4])

var float sum = 0.0
sum := 0.0
for price in prices
    sum := sum + price

plot(sum / array.size(prices), "Average Price")

If you also need the index alongside the value, use for [index, value] in array:

prices = array.from(10.0, 20.0, 30.0)

for [i, val] in prices
    // i = 0, 1, 2
    // val = 10.0, 20.0, 30.0
    label.new(bar_index - i, val, str.tostring(val))

while Loop#

while continues executing as long as its condition is true:

while condition
    // loop body

Example — find how many bars ago the close was last above the 20-period MA:

//@version=6
indicator("while Demo")

ma20 = ta.sma(close, 20)

// Walk back from the current bar to find the first bar with close above MA
barsAgo = 0
while close[barsAgo] <= ma20[barsAgo] and barsAgo < 50
    barsAgo := barsAgo + 1

plot(barsAgo, "Bars since last close above MA")

Note: A while loop must ensure its condition eventually becomes false, or it will cause an infinite loop and script timeout. It is recommended to add a maximum iteration limit (such as barsAgo < 50 above).

break — Exit the Loop Early#

break immediately terminates the loop, skipping all remaining iterations:

//@version=6
indicator("break Demo")

// Find how many bars ago the highest closing price occurred
highestBar = 0
for i = 1 to 50
    if close[i] > close[highestBar]
        highestBar := i
    // Stop searching if there's a drop greater than 1%
    if close[0] - close[i] > close[0] * 0.01
        break

plot(highestBar, "Bars ago with highest close")

continue — Skip the Current Iteration#

continue skips the current iteration and jumps to the next one:

//@version=6
indicator("continue Demo")

// Accumulate volume only for bullish bars (close > open)
var float bullVolume = 0.0
bullVolume := 0.0

for i = 0 to 9
    if close[i] <= open[i]
        continue  // Skip bearish bars
    bullVolume := bullVolume + volume[i]

plot(bullVolume, "Bullish volume (last 10 bars)")

Practical Example: Manually Finding High/Low#

Although ta.highest and ta.lowest are already available, the following example demonstrates a full application of loops:

//@version=6
indicator("Loop High/Low Finder", overlay=true)

lookback = input.int(20, "Lookback Bars", minval=1)

// Use a for loop to find the highest and lowest closing prices
var float myHighest = na
var float myLowest  = na
myHighest := close
myLowest  := close

for i = 1 to lookback - 1
    if close[i] > myHighest
        myHighest := close[i]
    if close[i] < myLowest
        myLowest := close[i]

plot(myHighest, "Highest Close", color=color.green)
plot(myLowest,  "Lowest Close",  color=color.red)

Reference#