Skip to content

Types & Variables

Variable Declaration

Variables can be declared with explicit or inferred types:

navi
// Type inferred
let a = 10;                // inferred as int
let name = "AAPL";         // inferred as string

// Explicit type
let b: float = 3.14;
let label: string = "Buy";
let flag: bool = true;

If the type cannot be inferred, an explicit annotation is required:

navi
let a = na;                // ERROR: cannot infer variable type
let a: float = na;         // OK

Reassignment

Use = to reassign a variable. Compound assignment operators are also supported:

navi
let a = 10;
a = 20;    // Reassignment
a += 5;    // a = a + 5
a -= 1;    // a = a - 1
a *= 2;    // a = a * 2
a /= 3;    // a = a / 3
a %= 4;    // a = a % 4

Type Qualifiers

Navi has four type qualifiers that describe when a value is known, from most restrictive to least:

QualifierMeaning
constKnown at compile time, never changes
inputKnown at script startup (e.g., user inputs)
simpleKnown on bar 0, same value on every bar
seriesCan change on every bar
navi
let a: const int = 2;
let b: input int = 10;
let c: simple int = a + b;
let d: series float = close;

The qualifier hierarchy flows: const -> input -> simple -> series. A value can be auto-promoted up this hierarchy but not demoted.

var — Initialize Once, Persist Across Bars

Without a declaration mode, variables are re-initialized on every script execution (every bar, every tick). The var keyword changes this — the variable is initialized only once on the first bar, and retains its last assigned value on all subsequent bars:

navi
indicator("Green Bars Count");
var count = 0;
let isGreen = close >= open;
if isGreen {
    count = count + 1;
}
plot(count);

Without var, count would reset to 0 on every bar, so the plot would only show 0 or 1. With var, the value accumulates across bars.

var inside if blocks

var can be used inside if blocks. The variable is initialized on the first execution that enters the block:

navi
var a = close;
var b = 0.0;
if close > open {
    var x = close; // initialized once when this branch first executes
    b = x;
}

var with collections

var works with all types including arrays and objects. A var array persists and can grow across bars:

navi
var a = Array.new<float>(0);
a.push(close);             // array grows by 1 element on each bar

Real-time behavior of var

On historical bars, var and non-var behave identically because the script runs exactly once per bar. The difference is visible on real-time bars: without var, the variable resets on each tick; with var, it retains its value across ticks but is subject to rollback when the bar closes (the value reverts to what it was at the last confirmed bar, then re-executes once for the new confirmed bar).

varip — Persist Without Rollback

varip (var intrabar persist) is similar to var — initialized only once. The key difference is that varip variables are not subject to rollback on real-time bars. They retain their values across all executions, including multiple ticks within the same unconfirmed bar:

navi
indicator("varip demo");
varip updateCount: int = na;
if barstate.is_new {
    updateCount = 1;
} else {
    updateCount = updateCount + 1;
}
plot(updateCount, style: PlotStyle.Circles);

This is useful for tracking tick-level data. On historical bars, varip behaves identically to var.

varip on type fields

varip can be used on individual type fields to make them persist across intrabar updates while other fields still roll back:

navi
struct Counter {
    int bars = 0;
    varip int ticks = 0;
}

var counter: Counter = Counter.new();
counter.bars += 1; // subject to rollback on unconfirmed bars
counter.ticks += 1; // NOT subject to rollback

Comparison Table

BehaviorNo keywordvarvarip
InitializationEvery executionOnce (first bar)Once (first bar)
Historical barsRe-init each barPersistsPersists
Real-time ticksRe-init each tickPersists (with rollback)Persists (no rollback)

Tuple Destructuring

Functions that return tuples can be destructured with (...) syntax:

navi
let (median, upperBand, lowerBand) = ta.bb(close, 20, 2.0);
plot(median);
plot(upperBand);
plot(lowerBand);

Discard (_)

Use _ to discard unwanted values. It can be used multiple times:

navi
let (_, upper, _) = ta.bb(close, 20, 2.0);

let _ = someFunction();     // Discard return value
let _ = anotherFunction();

na — Missing Values

na represents a missing or undefined value. Many functions return na when there isn't enough data:

navi
let sma5 = ta.sma(close, 5); // na for the first 4 bars

// Check for na with na()
if not na(sma5) {
    label.new(bar_index, sma5, str.tostring(sma5));
}

Use nz() to replace na with a default:

navi
let value = nz(ta.sma(close, 5), 0.0);  // Replace na with 0

Use fixnan() to replace na with the last non-na value:

navi
let value = fixnan(ta.sma(close, 5));

Next Steps

Released under the MIT License.