The script
A search back through history that a built-in can't do (how many bars since a higher high), a while loop, and the same count kept by a var counter. Paste it into a new indicator in the Pine Editor, then save and add it to the chart.
//@version=6
indicator("How many bars since a higher high", overlay = false)
maxBack = input.int(300, "Search at most N bars back", minval = 10, maxval = 2000)
// for + break: walk back until a bar's high exceeds this bar's high
int barsBack = na
for i = 1 to maxBack
if high[i] > high
barsBack := i
break
// The loop reads high up to 2000 bars back: keep that much history from the start
max_bars_back(high, 2000)
// while: count the closes in a row that were higher than the one before
int streak = 0
while streak < maxBack and close[streak] > close[streak + 1]
streak += 1
// The same streak without a loop: a var counter updated once per bar
var int streakVar = 0
streakVar := close > close[1] ? streakVar + 1 : 0
plot(nz(barsBack, maxBack), "Bars since a higher high", na(barsBack) ? color.orange : color.gray, style = plot.style_columns)
plot(streak, "Higher closes in a row (while)", color.teal)
plot(streakVar, "Higher closes in a row (var)", color.blue, display = display.data_window)How it works
for i = 1 to maxBackruns withi= 1, 2, …maxBack: both ends are included. Inside,high[i]is the highibars ago.breakstops at the first older bar with a higher high, so on most bars the loop only runs a few passes. When no higher high exists withinmaxBackbars,barsBackstaysnaand the column is orange: this bar's high is the highest of the whole window.- Why a loop here.
ta.highestbars()needs a fixed window; "how far back until something higher" has no fixed window, so a search loop is the natural tool. max_bars_back(high, 2000)tells Pine to keep 2,000 bars ofhighhistory from the start, because the offsetichanges at run time. The max_bars_back example covers this in detail.whilechecks its condition before each pass: here it stops at the first close that isn't higher than the one before it, or atmaxBack. The cap makes sure the loop ends.streakVargets the same number with one line per bar and no loop: the counter adds 1 on a higher close and drops to 0 otherwise. Prefer this form whenever the answer for this bar can be built from the previous bar's answer.
Pitfalls
- Loops that take too long. A loop runs again on every bar, so nested loops over hundreds of past bars add up quickly and the script stops with Loop is too long (> 500 ms). Cap the number of passes,
breakearly, or replace the loop with a built-in or avarcounter. - Counting down by accident. When the end is below the start, Pine counts down:
for i = 0 to n - 1withn = 0runs fori = 0andi = -1. On an empty array that's an Index is out of bounds error. Checkn > 0first, or loop withfor x in array. - ta. calls inside the loop.*
ta.sma(close, i)inside a loop is called a different number of times and with different lengths from bar to bar, so its history is wrong, and TradingView warns should be called on each calculation. Calculate built-ins before the loop, or do the arithmetic yourself inside it. - Offsets beyond the history buffer.
close[i]with anithat only gets large after the script has loaded can fail on live bars with historical offset beyond the buffer limit. Size the buffer withmax_bars_back(). - Variables declared inside the loop. A variable declared inside the loop body doesn't exist after it (Undeclared identifier). Declare it before the loop and update it with
:=inside.
Variations
A weighted average written as a loop. The loop below reproduces ta.wma(), so the two lines overlap; change the weight formula (w) to build an average that has no built-in:
//@version=6
indicator("Custom weighted average (loop)", overlay = true)
len = input.int(20, "Length", minval = 2, maxval = 200)
// Weights len, len - 1, ..., 1: the newest bar weighs most, as in ta.wma()
float num = 0.0
float den = 0.0
for i = 0 to len - 1
float w = len - i
num += close[i] * w
den += w
plot(num / den, "Loop average", color.orange, 2)
plot(ta.wma(close, len), "ta.wma()", color.blue)
Steps and skipping. for i = 0 to 20 by 5 takes i = 0, 5, 10, 15, 20. Inside any loop, continue jumps to the next pass, for example to skip bars with no volume: if volume[i] == 0 then continue. Walking through an array with for x in a is shown in the arrays example.
Questions
Does a Pine Script for loop include the last number?
Yes. for i = 1 to 5 runs five times, with i = 1, 2, 3, 4 and 5. That's why loops over an array run to size() - 1, and why they need a size check first.
How do I loop backwards in Pine Script?
Write the larger number first: for i = 10 to 0 counts down from 10 to 0, because Pine counts down whenever the end is below the start. Looping backwards is also the safe way to remove array elements during the loop.
Why does my loop make the script slow or stop?
The whole loop runs on every bar of the chart, and on every live update. A loop of 1,000 passes on 20,000 bars is 20 million passes. Use built-ins, break early, cap the passes, or carry results from bar to bar with var.
Related pages
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.