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:
- Using another moving average function that supports a “
series int
” length, such astalib.sma()
, or - Avoiding any calculations that result in a “
series int
” value forlength
.
Arithmetic Operators
There are five arithmetic operators in Lipi Script:
Operator | Meaning |
---|---|
+ | 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:
Operator | Meaning |
---|---|
< | 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:
Operator | Meaning |
---|---|
not | Negation |
and | Logical Conjunction |
or | Logical Disjunction |
The not
operator is unary. When applied to a true
operand, the result will be false
, and vice versa.
and
Operator Truth Table
a | b | a and b |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
or
Operator Truth Table
a | b | a or b |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
?:
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
istrue
, thencolor.red
is returned. If it isfalse
, thentimeframe.isdaily
is evaluated. - If
timeframe.isdaily
istrue
, thencolor.green
is returned. If it isfalse
, thentimeframe.ismonthly
is evaluated. - If
timeframe.ismonthly
istrue
, thencolor.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]
returnsna
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:
Precedence | Operator |
---|---|
9 | [] |
8 | unary + , unary - , not |
7 | * , / , % |
6 | + , - |
5 | > , < , >= , <= |
4 | == , != |
3 | and |
2 | or |
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.