Appearance
Types, qualifiers and na
Why does a value read na, or a function warn that it got a series value? Besides its number, a value carries two facts: whether it holds anything, and when it's known.
- A value that holds nothing is na. Test it with
na(), and replace it withnz()orfixnan(). - A value that can change from bar to bar is series. A parameter that must stay fixed for the run wants a literal, a
constor an input instead.
pine
//@version=6
indicator("Distance from the last swing low")
length = input.int(5, "Swing length") // an input: fixed before bar 0
float pivot = ta.pivotlow(low, length, length) // na on most bars
float swing = fixnan(pivot) // the last swing found so far
float pct = nz((close / swing - 1) * 100) // 0 before the first swing
plot(pct, "Above the swing, %")Every value has a type, such as float or string, which says what it holds. It also has a qualifier, such as series, which says when its value is known. The types reference lists every type and qualifier. This page is about the two things those tables lead to in practice: na, and the warnings a qualifier gives.
What na is
na is a value that isn't there. Every type has one: a float or an int with no number, a string with no text, a color with no colour, a drawing or an object that doesn't exist. A script meets it all the time:
- History before the first bar.
close[1]is na on bar 0, since there is no bar before it. - A window that isn't full.
ta.sma(close, 20)is na until it has 20 bars. - An event that didn't happen.
ta.pivotlowis na on every bar that doesn't confirm a pivot. - A declaration that says so, such as
float level = na, and a branch that didn't run (see Control flow). - A request with no data, and 0 divided by 0.
An empty string counts as na: na("") is true.
Testing for na
Test a value with na(x), which is true when x holds nothing. Don't write x == na: TradingView refuses it, strict Pine refuses it with the same reason, and in AmenoScript it is always false, since every comparison with na is false.
na() also reads an infinity as na, so na(1 / zero) is true for a zero that holds 0. See Dividing.
Replacing na
| Call | Gives |
|---|---|
nz(x) | x, or 0 where x is na. |
nz(x, y) | x, or y where x is na. |
fixnan(x) | x, or the last value of x that wasn't na. Before the first one, na. |
x ?? y | x, or y where x is na. AmenoScript only, and it keeps an infinity where nz replaces it. See coalescing. |
Pick the replacement that means something for the value. A count can start at 0, but a price of 0 would draw a line to the bottom of the chart: there, fixnan or a plain na that draws nothing is usually the better answer.
What na does to a calculation
na spreads. Arithmetic with na on either side gives na, so close + na is na, and each ta.* function has its own rule for a na in its window (see the ta reference). Every comparison with na on either side is false, != included, so x != x is false for a na x. A condition that reads na therefore takes the else way.
A bool is never na
A bool is true or false, never na, as in TradingView's v6. Wherever a condition, not, and, or, == or != reads a bool that holds nothing, it reads false. That covers a bool's history before the first bar, and a bool an if or switch gave when no branch ran.
pine
//@version=6
indicator("A missing bool reads false")
bool rose = close > close[1] // false on bar 0, where close[1] is na
bool before = rose[1] // on bar 0 there is no rose[1]: it holds nothing
plot(before ? 1 : 0, "Rose the bar before") // 0 on bar 0: it reads false
plot(not before ? 1 : 0, "Didn't") // 1 on bar 0rose is an ordinary false on bar 0, since a comparison with na is false. before holds nothing there, and still reads false.
So don't hand a bool to na(), nz() or fixnan(). TradingView refuses a bool there. This engine doesn't refuse it yet, and na(before) reads true on bar 0, where every condition reads before as false. Ask the question of the numbers the bool came from instead: na(close[2]).
Qualifiers
A qualifier says when a value is known. From the earliest to the latest:
constis known when the script is written: a literal such as20, or a name declaredconst.inputis known when the settings are read, before the first bar: whatinput.intand most other inputs return.simpleis known on the first bar and stays the same:syminfo.ticker,timeframe.period.seriescan change on every bar:close,bar_index, whateverta.smareturns.
A value takes the latest qualifier of its parts. input.int(10) * 2 is still input, but close > open ? 10 : 20 is series, since it reads close.
Some parameters need a value that stays put. ta.ema builds one smoothing factor from its length, so its length must be simple: a literal, a const, an input, or anything made only from those. The plot family's style arguments, an input's default and several others have rules of the same kind; each reference entry gives its parameters' qualifiers.
What a qualifier warning means
Where TradingView refuses to compile over a qualifier, this engine warns and runs the script anyway, in Pine and AmenoScript alike. The warning names the function, the argument and what it got:
pine
//@version=6
indicator("A length that moves")
len = close > open ? 10 : 20 // series: it reads close
plot(ta.ema(close, len)) // warns: ta.ema's length must be simpleThe script runs and draws here, but TradingView won't compile it. Fix it before you take the script there: pick the length from an input, or compute both averages on every bar and choose between their values.
Declared qualifiers aren't checked
const float x = close and simple int n = bar_index run here with no warning, though TradingView refuses both. The checker reads the qualifiers that function arguments need, not the ones a declaration states.
Converting between types
Call a type as a function to convert a value to it.
| Call | Gives |
|---|---|
int(2.7), int(-2.7) | 2 and -2: the fraction is cut toward zero. int(na) is na. |
float(3) | 3.0. Mixing an int with a float in arithmetic makes the result a float anyway. |
bool(x) | false for 0, na and an infinity, true for any other number. |
Dividing two ints keeps the fraction: 7 / 2 is 3.5. Write int(7 / 2) or math.floor(7 / 2) when you want 3.
Traps
x == na. It never finds a na. Usena(x).na()on a bool. It can read true for a missing bool that every condition reads as false. Test the numbers instead.- A series value where a function wants simple. It runs here with a warning and fails on TradingView.
- Replacing na with 0 by habit.
nzon a price makes a line to zero. Usefixnan, or leave the na so nothing draws. - A loose type. This engine lets a reassignment change a variable's type, and keeps
int n = 1.5as 1.5, where TradingView refuses both. See the types reference.
See also
- Types and qualifiers reference: every type, qualifier and declaration form
- Operators: na and infinities in comparisons and arithmetic
- Variables: declaring a typed variable
- Types and enums: types you declare yourself
- nz and na