Appearance
State in functions and branches
Why does ta.sma(close, 10) inside your function or your if give a different value than the same call on a line of its own? A call that remembers past bars records only the bars it runs on, and each place in the script that calls it keeps a record of its own. Run each such call on every bar, at the top level, and pick among the results afterwards.
pine
//@version=6
indicator("Average by regime")
float atr = ta.atr(14)
bool calm = atr < ta.sma(atr, 50)
// Both averages run on every bar, so neither window has holes.
float fast = ta.sma(close, 10)
float slow = ta.sma(close, 30)
plot(calm ? fast : slow, "Picked")
// Here each average sees only the bars its branch was taken.
plot(calm ? ta.sma(close, 10) : ta.sma(close, 30), "Holes")The second plot, Holes, goes wrong after each change of regime. Right after the market turns calm, its ta.sma(close, 10) still averages calm bars from before the last volatile stretch, however long ago they were.
Every call keeps its own state
A call site is one place in the text where a function is called. Each call site keeps its own copy of everything that remembers: the windows of ta.* calls, var variables, and the past that x[1] reads. Two calls of one function are two sites, even on the same line:
pine
//@version=6
indicator("Each call keeps its own count")
count() =>
var int n = 0 // one n for each place count() is called
n += 1
n
plot(count(), "One call") // 1, 2, 3, ...
plot(count() + count(), "Two calls") // 2, 4, 6, ...The copy follows the whole path of calls. A function that calls ta.sma from inside two other functions keeps two averages, one for each, so a helper can be shared by any number of callers.
A call inside a loop is one site for every pass of the loop. All the passes share one state, and on each bar it keeps the value the last pass gave, so a ta.sma in a loop isn't one average per pass. The run warns with "keeps one state for all its passes". Call it before the loop. When the loop is over a list of symbols, AmenoScript's request.each runs the call once for each symbol instead.
A call in a branch runs only when reached
A call inside an if block, a ternary's branch, a switch arm or the right side of and and or runs only on the bars that reach it. On other bars it doesn't run, so it records nothing. The right side of and runs only when the left side is true, and the right side of or only when it's false.
What a call then reads depends on how its function keeps the past. Most of ta.* falls in one of these groups, as TradingView reads them:
| Function | Over the bars a branch skipped |
|---|---|
ta.sma, ta.ema, ta.rma, ta.highest, ta.lowest, ta.stdev, ta.median, math.sum, ta.cum and the other windows | They count calls, not bars: a window of 10 is the last 10 calls, however far apart. |
ta.wma, ta.swma, ta.alma, ta.hma, ta.change, ta.mom, ta.roc, ta.percentrank | They read their source bar by bar, and a skipped bar repeats the source of the last call. |
ta.tr, ta.atr, ta.kc, ta.kcw, ta.supertrend | Each true range reads the close of the bar before, whether or not the call ran there. |
ta.highestbars, ta.lowestbars, ta.pivothigh, ta.pivotlow | They count their offsets in bars. |
ta.sar | It starts on the second bar only, so in a branch that skips that bar it stays na. |
ta.linreg, ta.rci | They read length + 1 slots by bar, and a slot no call wrote reads 0. |
The Script Editor warns when a ta.* call sits directly in a branch, as TradingView does with its warning CW10003. It doesn't warn when the call is inside a function of yours that the branch calls, which has the same effect.
The fix
Make every stateful call on every bar, at the top level, and use its value in the branch, as fast and slow do in the first example. The same goes for a function of your own: call it on every bar, keep its result in a variable, and read the variable where you need it.
When you do want a call to see only some bars, such as an average of the closes on up days, put it in the branch on purpose and check the table for what it reads.
A parameter's past
x[1] counts bars, not calls, and a parameter's past is no exception. In a function called on some bars only, a bar it skipped holds the value of its last call. So in a function called every third bar, src[1] is the argument of the last call, three bars back, not the bar before:
pine
//@version=6
indicator("A parameter's past")
prev(float src) => src[1] // src's past, not close's
float gap = na
if bar_index % 3 == 0
gap := close - prev(close) // three bars' change, not one
plot(gap, "Change since the last call")
plot(close - close[1], "Change since the bar before")A parameter has its own past for each call path, whatever argument you pass, and none on its first call: there src[1] is na, though close has a long history. A local variable declared in the function's body counts the same way.
A function's copy of an outer name
Inside a function, the chart's own series read the bars: open, high, low, close, volume, time, hl2, hlc3, ohlc4 and hlcc4, so close[1] is always the bar before. Any other name from outside the function reads through a copy the function keeps: as each call ends, the function stores that bar's value of the name, and x[1] reads the value stored at the call before. That covers bar_index, the date variables such as dayofmonth, strategy.equity and the other strategy.*, barstate.* and syminfo.* names, and your own variables.
pine
//@version=6
indicator("What a function's [1] reads")
int barNo = bar_index // one of the script's own variables
look() =>
float c = close[1] // the chart's series: the bar before
int gap = barNo - barNo[1] // the copy: as of the last call
[c, gap]
float same = na
int sinceCall = na
if bar_index % 3 == 0 // called on every third bar
[c, gap] = look()
same := c == close[1] ? 1 : 0
sinceCall := gap
plot(same, "close[1] is the bar before") // 1
plot(sinceCall, "barNo[1] is three bars back") // 3Called on every bar, the function's copy is filed on every bar, so it reads exactly as the script's own past does. Only a function called on some bars sees the difference.
Traps
- A function of yours in a branch. Its
ta.*calls see only the bars the branch ran, and no warning says so. andandorskip their right side.up and ta.crossover(a, b)runs the crossover only on up bars, so it can miss a cross.- A
ta.*call in a loop is one call site for all its passes. - A first call has no past. A function first called late in the chart reads na for its parameters'
[1]on that call. bar_index[1]in a function called on some bars reads its last call, not the bar before. When you mean the bar before, computebar_index[1]at the top level and pass it in.
See also
- Functions and methods, declaring and calling them
- Operators, what
x[k]reads - var and varip, what a variable keeps from bar to bar
- ta, the technical analysis functions
- each and pairs, one state per symbol in AmenoScript