Variables and State
series, var and := — the difference between a value recomputed every bar and one that persists.
There are three ways to declare a name and one way to change one, and the differences are not cosmetic. A plain assignment, weighted = 0.0, declares the name and recomputes it from scratch on every bar. Adding the series keyword, series basis = ta.sma(src, len), says the same thing with the intent made explicit and is what allows the right-hand side to refer to its own history as basis[1]. A var declaration, var sar = low, runs its initialiser exactly once — on the first bar of the data set — and from then on the name simply keeps whatever the previous bar left in it. And := assigns to a name that already exists. The rule is short: = declares, := reassigns, and var changes only when the initialiser runs.
The choice between series and var is not a matter of taste, and a trailing stop shows why. A ratchet that can be written as a single expression over its own previous value is a series: series longStop = testUp[1] > longStop[1] ? math.max(rawLong, longStop[1]) : rawLong reads the prior stop once, produces the new one, and is finished. Parabolic SAR cannot be written that way. Within one bar the stop is first advanced a fraction of the distance toward the extreme point, then clamped so it cannot trespass into the range of the previous two bars, then possibly discarded and replaced because price traded through it. That is three writes to one value on one bar, and three writes need somewhere to write into: var sar = low, followed by a short sequence of sar := statements.
Which brings up the property that matters most in practice — order. Series declarations are definitions, so the order they appear in is a matter of readability. A var is storage, and := writes to it in the order the statements are written, so every line that reads the name sees whatever the lines above it left there. In the SAR implementation the clamp has to sit between the advance and the flip test, and moving it produces a different indicator that still compiles. Keep var state in one short block, write to it in an obvious sequence, and do not scatter reassignments through the file.
The accumulators inside a loop use := as well, and they are not var. weighted = 0.0 before the loop and weighted := weighted + (len - i) * src[i] inside it is a value that resets on every bar, which is exactly what a windowed calculation wants. The keyword, not the assignment operator, is what makes something persist: := on a plain local lives for one bar, := on a var lives for the whole run.
na is the other half of the story. History that does not exist yet is na, any arithmetic involving na yields na rather than throwing, and na(x) is the only way to ask. It has two everyday uses: seeding a recursion, as in na(ema[1]) ? ta.sma(src, len) : ..., and guarding the first bar of a ratchet, as in na(upper[1]) or rawUp < upper[1] ? rawUp : upper[1] — note that the na() test comes first in that or chain, because there is nothing sensible for the comparison beside it to return. A var initialiser is the third answer to the same problem: it runs on the first bar, so there is nothing left to seed. All three share one consequence worth remembering when a live chart disagrees with a backtest: a var, and any series that reads its own history, depends on where the data begins, so load enough warm-up bars before you trust the first values.