Functions & Methods
Function Definitions
Single-Line Functions
fn double(x) {
x * 2
}
fn add(a, b) {
a + b
}Multi-Line Functions
The last expression in the body is the return value. There is no return keyword:
fn smaCustom(src, length) {
let sum = 0.0;
for i = 0 to length - 1 {
sum += src[i]
}
sum / length
}Functions Returning Tuples
fn calcBands(src, length, mult) {
let basis = ta.sma(src, length);
let dev = mult * ta.stdev(src, length);
(basis, basis + dev, basis - dev)
}
let (mid, upper, lower) = calcBands(close, 20, 2.0);Note: In Navi, tuples may only appear as function return values and must be immediately destructured on assignment. They cannot be stored in variables, passed as arguments, or used inside other expressions.
Default Parameters
fn myPlot(src, length = 14, title = "Default") {
let sma = ta.sma(src, length);
plot(sma, title);
sma
}Named Arguments
When calling functions, you can use named arguments:
plot(close, title: "Close", color: color.BLUE, linewidth: 2)Function Overloading
Multiple functions can share the same name if they have different parameter types:
fn format(x: int) {
str.tostring(x)
}
fn format(x: float) {
str.tostring(x, "#.##")
}
fn format(x: string) {
x
}Methods
Methods are functions whose first parameter is the receiver (self). They support two equivalent call forms:
method double(self: int) {
self * 2
}
let x = 5;
x.double(); // dot-call — receiver on the left
double(x); // free-function call — receiver as first argument (UFCS)Both forms are interchangeable. Use whichever reads more naturally for your context.
Methods work with custom types:
struct Position {
float entry,
float size,
}
method pnl(self: Position, currentPrice: float) {
(currentPrice - self.entry) * self.size
}
method isProfit(self: Position, currentPrice: float) {
self.pnl(currentPrice) > 0
}
let pos = Position.new(entry: 100.0, size: 10.0);
if pos.isProfit(close) {
label.new(bar_index, high, str.tostring(pos.pnl(close), "#.##"))
}No Recursive Calls
Navi does not allow recursion. A function cannot call itself, either directly or indirectly through other functions. The compiler rejects any call cycle at compile time:
// ERROR — direct recursion is not allowed
fn factorial(n) {
n <= 1 ? 1 : n * factorial(n - 1)
}
// ERROR — indirect recursion is also rejected
fn isEven(n) {
n == 0 ? true : isOdd(n - 1)
}
fn isOdd(n) {
n == 0 ? false : isEven(n - 1)
}This is a fundamental language constraint, not an implementation limitation. Use for or while loops for iterative computation instead:
fn factorial(n) {
let result = 1;
for i = 2 to n {
result *= i
}
result
}Explicit Return Types
Navi supports annotating a return type before the function name for stricter type checking:
fn greet(name: string): string {
"Hello, " + name
}
fn average(a: float, b: float): float {
(a + b) / 2.0
}This also works with methods:
method isPositive(self: float): bool {
self > 0
}Generic Functions
Navi allows type parameters declared with <...> after the function name:
fn identity<T>(value: T) {
value
}
fn first<T>(arr: array<T>) {
array.get(arr, 0)
}
method contains<T>(self: array<T>, value: T) {
array.indexof(self, value) >= 0
}Generic functions allow you to write reusable code that works with multiple types.
Variadic Parameters
Navi allows the last parameter to be variadic with ...:
fn sum(first: int, rest: int...) {
first + rest
}The variadic parameter must be the last one in the parameter list.
Property Functions
The property keyword declares a function that cannot have parameters and is called without parentheses, making it look like a variable or property access:
property size() {
42
}
// Access as a variable — no parentheses:
let value = size; // 42
// Calling with parentheses is an ERROR:
let value = size(); // error: function `size` not definedExamples from the standard library:
// Candlestick data (prelude/candlestick.1.nvs)
export property open(): series float {
@native.candlestick(1)
}
export property close(): series float {
@native.candlestick(4)
}
export property hl2(): series float {
(high + low) / 2
}
// Constants (stdlib/math.1.nvs)
export property pi(): const float {
3.1415926535897932
}Properties allow Navi's standard library to expose built-in values like close, open, math.pi, and color.RED as properties in Navi source code.
Static Methods on Types
The staticmethod keyword defines a static method associated with a specific object type. Static methods are called on the type itself, not on an instance:
struct AB {
int a,
}
staticmethod(AB) add(a: int, b: int): int {
a + b
}
// Call on the type name:
let result = AB.add(20, 5); // 25Static methods can only be defined for struct or newtype types, not for enums or primitive types.
Examples from the standard library:
// Factory methods for chart.Point (stdlib/chart.1.nvs)
export struct Point {
int index,
int time,
float price,
}
export staticmethod(Point) now(price: float = close): Point {
Point.new(bar_index, time_now, price)
}
export staticmethod(Point) from_time(time: int, price: float): Point {
Point.new(na, time, price)
}These are called as chart.Point.now() and chart.Point.from_time(time, price).
Static Properties on Types
The staticproperty keyword is like staticmethod, but the accessor takes no parameters and is referenced without parentheses — just like a regular property:
struct Counter {
int value,
}
staticproperty(Counter) zero(): Counter {
Counter.new()
}
// Access without ():
let c = Counter.zero;staticproperty can only be defined for struct or newtype types, not for enums or primitive types.
staticmethod | staticproperty | |
|---|---|---|
| Parameters | Yes | None |
| Call syntax | Type.name(args) | Type.name |
Operator Overloading
The operator keyword defines custom behavior for arithmetic operators on user-defined types:
struct Vec2 {
float x,
float y,
}
operator+(a: Vec2, b: Vec2): Vec2 {
Vec2.new(a.x + b.x, a.y + b.y)
}
operator-(a: Vec2, b: Vec2): Vec2 {
Vec2.new(a.x - b.x, a.y - b.y)
}
// Now you can use the operators naturally:
let a: Vec2 = Vec2.new(1, 2);
let b: Vec2 = Vec2.new(3, 4);
let c: Vec2 = a + b; // Vec2.new(4, 6)Supported operators: +, -, *, / (no space between operator and the symbol). Multiple overloads for different operand types are allowed.
Built-in Functions
Navi provides many built-in functions:
// Plotting
plot(close, "Close", color.BLUE)
plot_shape(close > open, style: Shape.TriangleUp)
bg_color(close > open ? color.new(color.GREEN, 90) : na)
// Technical Analysis
let sma = ta.sma(close, 20);
let ema = ta.ema(close, 12);
let (macdLine, signal, hist) = ta.macd(close, 12, 26, 9);
let rsi = ta.rsi(close, 14);
// Math
let rounded = math.round(close, 2);
let maxVal = math.max(open, close);
// String operations
let text = str.tostring(close, "#.##");
// Input
let length = input.int(14, "RSI Length", minval: 1);
let src = input.source(close, "Source");Next Steps
- Custom Types & Enums — user-defined types, enums, and newtype declarations
- Standard Library — explore the standard library