Operators

Introduction

Operators are used in different ways to create expressions that yield a result:

  • Arithmetic operators
  • Comparison operators
  • Logical operators
  • The ?: ternary operator
  • The [] history-referencing operator

Other operators are specifically for assigning values to variables:

  • = is used to assign a value to a variable but only when the variable is declared (the first time it’s used).
  • := assigns a value to a variable that has already been declared. The following operators can also be used in this way: +=, -=, *=, /=, %=.

As explained on the Type System page, qualifiers and types are crucial in determining the type of results that expressions produce. This, in turn, impacts the functions and operations you can use with those results. Expressions always yield a value with the strongest qualifier used in the expression. For instance, if you multiply an “input int” with a “series int”, the expression will return a “series int” result, which cannot be used as the argument for length in talib.ema().

This script will produce a compilation error:

indicator("");
lenInput = input.int(14, "Length");
factor = year > 2020 ? 3 : 1;
adjustedLength = lenInput * factor;
//Compilation error!
ma = talib.ema(close, adjustedLength);
plot(ma);

The compiler will display an error: “Cannot call ‘talib.ema’ with argument ‘length’=adjustedLength. An argument of series int type was used, but a simple int is expected.” This error occurs because lenInput is an “input int,” while factor is a “series int” (its value depends on the year value for each bar). Therefore, the adjustedLength variable receives a “series int” type. The issue is that the Reference Manual entry for talib.ema() specifies that its length parameter needs a “simple” value, which is a weaker qualifier than “series.” As a result, a “series int” value cannot be used.

The solution to this issue involves:

  1. Using another moving average function that supports a “series int” length, such as talib.sma(), or
  2. Avoiding any calculations that result in a “series int” value for length.

Arithmetic Operators

There are five arithmetic operators in Lipi Script:

OperatorMeaning
+Addition and string concatenation
-Subtraction
*Multiplication
/Division
%Modulo (remainder after division)

The arithmetic operators listed above are all binary—meaning they require two operands (or values) to operate, as in 1 + 2. The + and - operators can also function as unary operators, which means they work on a single operand, like -1 or +1.

When both operands are numbers and at least one of them is a float, the result will also be a float. If both operands are of int type, the result will be an int. When at least one operand is na, the result will also be na.

The + operator also acts as the concatenation operator for strings. For example, "EUR" + "USD" produces the string "EURUSD".

The % operator calculates the modulo by rounding down the quotient to the lowest possible value. Here is a simple example that illustrates how the modulo is calculated behind the scenes:

indicator("Modulo function")
modulo(series int a, series int b) =>
    a - b * math.floor(nz(a/b))
plot(modulo(-1, 100))

Comparison Operators

There are six comparison operators in Lipi Script:

OperatorMeaning
<Less Than
<=Less Than or Equal To
!=Not Equal
==Equal
>Greater Than
>=Greater Than or Equal To

Comparison operations are binary. When both operands are numerical values, the result is of type bool—meaning the result will be true, false, or na.

Examples:

1 > 2; // false
1 != 1; // false
// Depends on values of `close` and `open`
close >= open; 

Logical Operators

There are three logical operators in Lipi Script:

OperatorMeaning
notNegation
andLogical Conjunction
orLogical Disjunction

The not operator is unary. When applied to a true operand, the result will be false, and vice versa.

and Operator Truth Table

aba and b
truetruetrue
truefalsefalse
falsetruefalse
falsefalsefalse

or Operator Truth Table

aba or b
truetruetrue
truefalsetrue
falsetruetrue
falsefalsefalse

?: Ternary Operator

The ?: ternary operator is used to create expressions in the following form:

condition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse;

The ?: ternary operator returns a result based on the value of condition. If condition is true, then valueWhenConditionIsTrue is returned. If condition is false or na, then valueWhenConditionIsFalse is returned.

A combination of ternary expressions can be used to achieve the same effect as a switch structure. For example:

timeframe.isintraday ? color.red : timeframe.isdaily ? color.green : timeframe.ismonthly ? color.blue : na;

The example is evaluated from left to right:

  • If timeframe.isintraday is true, then color.red is returned. If it is false, then timeframe.isdaily is evaluated.
  • If timeframe.isdaily is true, then color.green is returned. If it is false, then timeframe.ismonthly is evaluated.
  • If timeframe.ismonthly is true, then color.blue is returned; otherwise, na is returned.

Note: The return values on each side of the : are expressions—not local blocks—so they do not count towards the limit of 500 local blocks per scope.

[] History-Referencing Operator

The [] history-referencing operator allows referencing past values of time series data. Past values are those that a variable held on bars prior to the current one. For more on script execution timing, refer to the Execution Model page.

The [] operator is applied following a variable, expression, or function call. The value inside the square brackets specifies the number of bars back you want to reference. For instance, volume[2] accesses the value of volume from two bars prior to the current one.

As the script advances through successive bars, the [] operator’s offset dynamically references different bars. This dynamic referencing highlights the unique behavior of series as compared to arrays. In Lipi Script, the close variable (or close[0]) represents the “close” price of the current bar. For example, if executing on the third bar:

  • close holds the close price of the third bar.
  • close[1] contains the close of the preceding bar (second bar).
  • close[2] refers to the first bar’s close.
  • close[3] returns na since no earlier bar exists.

On the next bar (fourth in the dataset), close will now hold the fourth bar’s close, close[1] references the third bar, and so on. Consequently, the offset changes as the script advances. Series values shift dynamically in index as each new bar is processed, unlike arrays, which maintain static indexing and structure in the Lipi Script runtime environment.

If the chart’s market is open and the script runs on the last (realtime) bar, close shows the current price, updating with the bar’s final close price only after it concludes.

The bar_index variable in Lipi Script holds the index of the bar currently executing. It begins at 0 on the first bar and increments by 1 for each subsequent bar, reaching the dataset’s total bar count minus one by the final bar.

When using [] in Lipi Script, be aware that history references may sometimes return na. This special na value signifies an undefined or non-numerical result, much like NaN. Unhandled na values can propagate through calculations, affecting outcomes on later bars, including the realtime bar. Lipi Script provides functions like na and nz to help handle such cases.

Here are examples of valid [] operator usage:

high[10];
talib.sma(close, 10)[1];
talib.highest(high, 10)[20];
close > nz(close[1], open);

Note: The [] operator can only be used once on the same value. The following usage is not allowed:

// Error: incorrect use of [] operator
close[1][2]; 

Operator Precedence

The order of calculations is dictated by the precedence of operators. Operators with higher precedence are evaluated first. Below is a list of operators arranged by decreasing precedence:

PrecedenceOperator
9[]
8unary +, unary -, not
7*, /, %
6+, -
5>, <, >=, <=
4==, !=
3and
2or
1?:

If an expression contains multiple operators with the same precedence, they are calculated left to right.

To enforce a different calculation order than that dictated by precedence, parts of the expression can be grouped using parentheses.

= Assignment Operator

The = operator is utilized to assign a value to a variable during its initialization—or declaration—meaning it is used the first time the variable is referenced. This indicates that it is a new variable that will start with this specified value on each bar.

Here are examples of valid variable declarations:

i = 1;
MS_IN_ONE_MINUTE = 1000 * 60;
showPlotInput = input.bool(true, "Show plots");
pHi = pivothigh(close, 5, 5);
plotColor = color.green;

See the Variable Declarations page for more information on how to declare variables.

:= Reassignment Operator

The := operator is used to reassign a value to an existing variable. It indicates that we are utilizing a variable that was previously declared in the script and assigning it a new value.

Variables that have been initially declared and then reassigned using := are referred to as mutable variables. All of the following examples represent valid variable reassignments. For additional information on how static functions, please refer to the section on the static declaration mode:

/
indicator("", "", true)
// Declare `pHi` and initilize it on the first bar only.
static float pHi = na
// Reassign a value to `pHi`
pHi := nz(talib.pivothigh(close, 5, 5), pHi)
plot(pHi)

Note that:

We declare pHi using the code: static float pHi = na. The static keyword informs Lipi Script that we want this variable to be initialized with na only on the first bar of the dataset. The float keyword indicates to the compiler that we are declaring a variable of type “float.” This specification is essential because, unlike most cases, the compiler cannot automatically determine the type of the value on the right side of the = sign.

While the variable declaration is executed only on the first bar due to the use of static, the line pHi := nz(talib.pivothigh(close, 5, 5), pHi) will be executed on all of the chart’s bars. On each bar, it checks whether the pivothigh() call returns na—which occurs when the function has not found a new pivot. The nz() function performs the “checking for na” task. If its first argument (talib.pivothigh(close,5, 5)) is na, it returns the second argument (pHi) instead of the first. When pivothigh() returns the price point of a newly found pivot, that value is assigned to pHi. Conversely, if it returns na because no new pivot is found, we reassign the previous value of pHi to itself, effectively preserving its prior value.

Note that:

  • The line preserves its previous value until a new pivot is identified.
  • Pivots are detected five bars after they actually occur because our talib.pivothigh(close, 5, 5) call specifies that we need five lower highs on both sides of a high point for it to be recognized as a pivot.
  • For more information on how to reassign values to variables, please refer to the Variable Reassignment section.