Pine Script v6 iteration

Pine Script loops: bounds, direction and runtime limits

A Pine Script for loop counts between inclusive bounds; a while loop repeats while its condition is true. In v6, for reevaluates its to endpoint before each iteration. Use a positive by step: the initial bounds determine direction. A plain 0 to -1 loop visits 0 and -1, so it is not an empty range. Guard empty arrays before reading an index, and give condition-driven loops a finite stopping rule.

Review loop bounds with AI

Inclusive bounds and positive steps

The examples below are complete, finite v6 indicators. The first builds a text trace without reading array indices. Add it to a chart and compare its label with the table. The expected sequences are derived from the documented counter rules, not recorded chart executions.

HeaderExpected counter valuesIterations
for i = 0 to 30, 1, 2, 34
for i = 3 to 0 by 23, 12; the next value would pass 0
for i = 0 to -10, -12; descending, not empty
for i = 2 to 221
Complete v6 trace: inclusive, descending and equal boundspinescript
//@version=6
indicator("Loop direction trace", overlay=true)

if barstate.islastconfirmedhistory
    string ascending = ""
    for i = 0 to 3
        ascending += str.tostring(i) + ","
    string descending = ""
    for i = 3 to 0 by 2
        descending += str.tostring(i) + ","
    string zeroToMinusOne = ""
    for i = 0 to -1
        zeroToMinusOne += str.tostring(i) + ","
    string equalBounds = ""
    for i = 2 to 2
        equalBounds += str.tostring(i) + ","
    if ascending != "0,1,2,3," or descending != "3,1,"
        runtime.error("Unexpected positive-step trace")
    if zeroToMinusOne != "0,-1," or equalBounds != "2,"
        runtime.error("Unexpected inclusive-bound trace")
    label.new(bar_index, high, ascending + " | " + descending + " | " + zeroToMinusOne + " | " + equalBounds)

Why by -1 does not select a reverse loop

Pine requires a positive step magnitude. Do not use by -1 or by 0. The default step is 1; descending initial bounds cause subtraction. Direction stays fixed even if the endpoint changes later. The descending trace above therefore visits 3 then 1, without landing on 0.

An empty array needs an explicit guard

For a size-zero array, size minus one equals -1. An unguarded indexed loop would begin at 0 and attempt an invalid read immediately. The guard below keeps empty visits at zero. The populated array is read at indices 0, 1, 2, producing 10 + 20 + 30 = 60. Neither array changes size during traversal.

  • For forward indexing, require 0 <= index < size before a read. Index equal to size is already outside the array.
  • A to expression of na also prevents iteration; an explicit size guard makes the empty case visible here.
  • for...in can traverse array elements directly. Avoid structural mutations during a read-only traversal.
Complete v6 empty and populated array checkspinescript
//@version=6
indicator("Guarded array loops")

array<float> empty = array.new<float>(0)
int emptySize = array.size(empty)
int emptyVisits = 0
if emptySize > 0
    for i = 0 to emptySize - 1
        float value = array.get(empty, i)
        emptyVisits += 1

array<float> values = array.from(10.0, 20.0, 30.0)
int count = array.size(values)
float total = 0.0
if count > 0
    for i = 0 to count - 1
        total += array.get(values, i)

if emptyVisits != 0 or total != 60.0
    runtime.error("Expected zero empty visits and sum 60")
plot(total, "Populated sum: 60")
plot(emptyVisits, "Empty visits: 0")

v6 reevaluates the endpoint on each iteration

The v6 migration guide explicitly changes the v5 rule: a variable or expression used after to can alter the remaining range. To retain the starting range, evaluate it into a variable before the loop and do not reassign that variable. This example starts with endpoint 4, then reduces it to 1 during the first visit. The dynamic loop visits 0, 1; the frozen loop visits 0, 1, 2, 3, 4.

LoopAfter visiting 0Next decisions
Dynamic endpointendpoint becomes 1Visit 1; reject 2 because 2 > 1
Frozen endpointmutableEnd becomes 1; fixedEnd stays 4Visit 1, 2, 3, 4; reject 5
Complete v6 dynamic versus frozen endpoint tracepinescript
//@version=6
indicator("Dynamic versus frozen endpoint", overlay=true)

if barstate.islastconfirmedhistory
    int endpoint = 4
    string dynamicTrace = ""
    for i = 0 to endpoint
        dynamicTrace += str.tostring(i) + ","
        if i == 0
            endpoint := 1

    int mutableEnd = 4
    int fixedEnd = mutableEnd
    string frozenTrace = ""
    for i = 0 to fixedEnd
        frozenTrace += str.tostring(i) + ","
        if i == 0
            mutableEnd := 1

    if dynamicTrace != "0,1," or frozenTrace != "0,1,2,3,4,"
        runtime.error("Unexpected v6 endpoint trace")
    label.new(bar_index, high, "Dynamic: " + dynamicTrace + " | Frozen: " + frozenTrace)

Changing array size changes more than the endpoint

Appending during a loop whose endpoint is array.size(values) - 1 can keep extending the work in v6. Removing elements can shorten the range or shift later elements into earlier indices. Freezing the original size prevents endpoint drift, but does not make removed indices valid. For a read-only pass, keep the array unchanged or traverse a separate copy. For deletion, define the removal order and recheck the current size before every access.

Bound a while loop and stop on the first match

This search reads [4, 7, 9, 12] until it finds a value at least 8. The finite limit is the smaller of the array size and the user budget. With budget 4, it reads index 0 (4), index 1 (7), then index 2 (9), and break exits before reading 12. With budget 2 it reads only 4 and 7, leaving found at -1. The trace and visit count expose both outcomes.

Complete v6 while search: guarded access, progress and breakpinescript
//@version=6
indicator("Bounded while search", overlay=true)
int budget = input.int(4, "Maximum visits", minval=1, maxval=4)

if barstate.islastconfirmedhistory
    array<int> values = array.from(4, 7, 9, 12)
    int limit = math.min(array.size(values), budget)
    int i = 0
    int visits = 0
    int found = -1
    string trace = ""
    while i < limit
        int value = array.get(values, i)
        trace += str.tostring(i) + ":" + str.tostring(value) + ","
        visits += 1
        if value >= 8
            found := i
            break
        i += 1
    int expectedFound = budget >= 3 ? 2 : -1
    int expectedVisits = math.min(budget, 3)
    if found != expectedFound or visits != expectedVisits
        runtime.error("Unexpected search result or visit count")
    label.new(bar_index, high, trace + " found=" + str.tostring(found) + " visits=" + str.tostring(visits))

Termination and runtime limits are separate checks

A while condition is tested before each visit, so an initially false condition executes no body. break exits immediately; continue skips the rest of the current body. If a while loop puts its only counter update after continue, that path can stop making progress. Review every path through the body.

  • TradingView limits a loop to 500 ms on one bar. Nested work counts toward the outer loop limit.
  • The total dataset execution limit also applies. Passing the per-bar limit does not guarantee the full chart will finish.
  • Prefer an equivalent built-in calculation when available. Gate display-only work to the intended bar; do not skip historical calculations that your series needs.
  • Syntax success does not establish runtime performance, valid array accesses or correct trading behavior. Run empty, one-element, maximum-size and no-match cases on a chart.
Where Pineify fits

Pine Script AI Coding Agent

Give the agent your complete script, Pine version, array mutation rules and expected access sequence. Ask it to repair the bounds or termination condition and run a syntax check. Compare the resulting trace with your expected sequence in TradingView before using it in a trading system.

Review loop bounds with AI

Frequently asked questions

Educational Pine v6 examples, not a trading strategy or investment advice. Expected traces are hand-derived; syntax validation does not execute the examples or verify their behavior on your chart.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.