For Loops in Pine Script: Bounds, Arrays and Execution Limits
A Pine Script loop repeats operations within one script execution. Pine already evaluates a script across chart bars, so a loop over historical offsets is an additional iteration inside that execution. Understanding both levels helps prevent unnecessary work and incorrect history references.

Counter loops and collection loops
The basic header is for counter = start_value to end_value, followed by an indented body. The endpoint is inclusive when reached. A counter can descend when the starting value exceeds the ending value; it is not always an increment-only loop.
In Pine v6, the stopping expression is evaluated again at each iteration. Changing a variable used by that expression can change the loop's length. Be especially careful when the bound depends on an array that the body resizes. TradingView's loop documentation describes counter loops, for...in and their local scopes.
break exits the current loop. continue skips the rest of that iteration. A collection loop can return each element or both its index and value.
A manual moving-average calculation
The following existing examples are retained for code inspection. They are not reported as chart-tested strategies or performance benchmarks. Add one complete indicator at a time when evaluating them.
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// (C) Pineify
//======================================================================//
// ____ _ _ __ //
// | _ \(_)_ __ ___(_)/ _|_ _ //
// | |_) | | '_ \ / _ \ | |_| | | | //
// | __/| | | | | __/ | _| |_| | //
// |_| |_|_| |_|\___|_|_| \__, | //
// |___/ //
//======================================================================//
//@version=6
indicator("[Pineify] Custom SMA with For Loop", overlay=true)
length = input.int(30, title="How many bars to average?")
src = close
sum = 0.0
for i = 0 to length - 1
sum := sum + src[i]
sma = sum / length
plot(sma, color=color.blue, title="My Custom SMA")
For a positive length and available data, the loop sums that many closes, including the current bar, then divides by the length. The accumulator resets on each script execution. It is not declared with var, which would retain a value across bars.
The input has no minimum or maximum guard. Keep it positive and within available history when inspecting this educational example. Zero or negative settings do not represent a valid moving-average window. Missing historical values can propagate through the sum; this simple calculation is not a full reimplementation of every ta.sma() missing-value behavior. Prefer a built-in calculation when its semantics match the task.
Compare historical highs with the current high
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// (C) Pineify
//======================================================================//
// ____ _ _ __ //
// | _ \(_)_ __ ___(_)/ _|_ _ //
// | |_) | | '_ \ / _ \ | |_| | | | //
// | __/| | | | | __/ | _| |_| | //
// |_| |_|_| |_|\___|_|_| \__, | //
// |___/ //
//======================================================================//
//@version=6
indicator("[Pineify] Bar Counter", overlay=true)
lookbackInput = input.int(50, "How far back to look?", minval = 1, maxval = 4999)
higherBars = 0
lowerBars = 0
if barstate.islast
var label lbl = label.new(na, na, "", style = label.style_label_left)
for i = 1 to lookbackInput
if high[i] > high
higherBars += 1
else if high[i] < high
lowerBars += 1
label.set_xy(lbl, bar_index, high)
label.set_text(lbl, str.tostring(higherBars, "# higher bars\\n") + str.tostring(lowerBars, "# lower bars"))
The counters compare earlier highs with the current high. Equal highs are counted in neither total, and unavailable historical comparisons also contribute to neither. Therefore the two totals need not sum to the requested lookback.
The work runs under barstate.islast, which includes updates to the current realtime bar. It does not mean the bar is confirmed. As the current high changes, the counts can change. The persistent label is updated, while the counters reset for each execution. The retained format string contains a double-escaped newline sequence; inspect its actual label display rather than assuming it produces two lines.
A maximum lookback input is not proof that every chart has that many usable bars or that every historical buffer is sized correctly. Check the earliest available data and runtime diagnostics.
Iterate through array elements
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// (C) Pineify
//======================================================================//
// ____ _ _ __ //
// | _ \(_)_ __ ___(_)/ _|_ _ //
// | |_) | | '_ \ / _ \ | |_| | | | //
// | __/| | | | | __/ | _| |_| | //
// |_| |_|_| |_|\___|_|_| \__, | //
// |___/ //
//======================================================================//
//@version=6
indicator("[Pineify] Array Fun", overlay=true)
arr = array.from(1, 5, 2, 4, 3)
if barstate.islast
label.new(bar_index, close, str.tostring(arr))
sum = 0
for el in arr
sum += el
if barstate.islast
label.new(bar_index + 5, close, str.tostring(sum))
sum_even = 0
for [ind, el] in arr
if ind % 2 == 1
sum_even += el
if barstate.islast
label.new(bar_index + 10, close, str.tostring(sum_even))
plot(close)
The first loop sums the five values to 15. The second uses zero-based indices: index 1 contains 5 and index 3 contains 4, totaling 9. Despite the name sum_even, it is selecting odd-numbered indices, not even values.
This array is recreated on every execution. Labels are created on last-bar executions; this is a demonstration of iteration, not a design for managing a persistent dashboard's drawings. Arrays can hold supported types beyond numbers, but an individual typed array has one element type.
Trace break and continue
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// (C) Pineify
//======================================================================//
// ____ _ _ __ //
// | _ \(_)_ __ ___(_)/ _|_ _ //
// | |_) | | '_ \ / _ \ | |_| | | | //
// | __/| | | | | __/ | _| |_| | //
// |_| |_|_| |_|\___|_|_| \__, | //
// |___/ //
//======================================================================//
//@version=6
indicator("[Pineify] Loop Control", overlay=false)
length = 10
sum = 0
for i = 1 to length
if i == 3
continue // Skip when i is 3
if i == 7
break // Stop completely when i is 7
sum := sum + i
plot(sum, title="Sum")
The loop skips 3 and exits when it reaches 7, before adding that value. It therefore adds 1, 2, 4, 5 and 6, yielding 18. This is a deterministic arithmetic example, not a trading result.
Scope, plotting and execution limits
plot() belongs in global scope. Calculate a result in the loop and plot it afterward; drawing objects such as labels have different scope rules and resource limits. A value declared only inside the loop's local scope is not available afterward unless the surrounding structure returns it or updates an appropriately declared outer variable.
TradingView's limitations page specifies a 500-millisecond limit for a loop on one bar, with the outer loop controlling nested-loop timing. Total script execution has a separate limit. Neither is a universal 500-iteration ceiling. The work per iteration and number of bars matter, not simply whether the chart timeframe is below one minute.
For an empty collection, prefer a collection iteration or an explicitly guarded index range. Do not assume an indexed range from zero to size-minus-one will behave as an empty loop: descending counter semantics matter.
Verify a loop before expanding it
Check the smallest valid input, an empty collection where relevant, insufficient history and a realtime bar. For arithmetic, compare with a manually calculated small input. For a standard indicator, compare values with the corresponding built-in implementation while matching missing-data behavior.
Pineify can help draft or revise Pine code. Request a syntax check on the final source, then test runtime behavior in TradingView. Generated code remains subject to plot, history and execution limits; combining calculations does not remove account restrictions.


