Expressions and Functions
Expressions contain only two data types: double-precision real numbers and strings.
Even if a block exposes an integer value (for example the length of an RSI), that value is treated as a real variable when used in an expression.
Results of comparisons and logical operations are also reals equal to 0 or 1.
Missing values are represented as NaN.
When an expression is used as a boolean guard in [[condition]] or [[if]], 0.0 and NaN are treated as false. Any other non-zero value is treated as true.
Where should you enter expressions?
Expressions can be entered in several parts of a TOML strategy.
General rule: an expression field accepts either an unquoted number (TOML integer or floating-point) or a string (TOML string).
Exception: in [[objective]], formula must be written as a TOML string.
In custom series calculations
Custom series use a formula to compute a new series from available price series, indicator outputs, and the variables they expose.
These variables include for example fast.length, fast.type, fast.symbol, and fast.timeframe.
The formula is compiled at load time, then evaluated while the series is being computed.
In [[custom_series]], only symbols available at indicator stage can be used. Runtime backtest, portfolio, order, and trade variables are not available there.
[[custom_series]]
id = "mad"
formula = "fast / slow"In combination filtering
Combination filtering uses a condition expression to remove hyperparameter combinations that do not match your criteria. This expression is evaluated for each combination before that backtest run starts.
[constraints]
condition = "fast.length < slow.length and fast.type = slow.type"In objective definitions
The objective uses a formula to turn result metrics into a score.
The formula is compiled before execution and evaluated after each backtest.
In this block, formula is always read as a TOML string.
[[objective]]
id = "risk_adjusted_return"
formula = "(grossprofit_percent - grossloss_percent) / max_drawdown_percent"
ascending = falseIn conditional blocks
Conditional blocks use expressions to decide which execution path to follow. Depending on the block type, an expression is evaluated either when the block executes or on candle updates to maintain cross-candle state.
[[condition]]
id = "entry_filter"
condition = "fast > slow and fast[1] <= slow[1]"
next_block_id = "enter_long"In variable calculations
The [[variable]] block uses two expressions: an initialization and an update formula.
initialization is evaluated once at backtest startup, then formula is evaluated each time the block is visited.
[[variable]]
id = "variable_trend_streak"
variable = "trend_streak"
initialization = 0
formula = "close > ema_200 ? trend_streak + 1 : 0"
next_block_id = "check_entry"In order-related action blocks
Order blocks can use expressions for quantity and price values. These values are evaluated when the order block is triggered, right before order submission or order synchronization.
[[entry]]
id = "enter_long"
order_id = "main"
qty_percent = 50
limit = "close * 0.995"
stop = "close * 1.005"
next_block_id = "watch_exit"Precision and rounding
All expressions are evaluated as double-precision reals. Market constraints (price tick, quantity tick, minimum size) are not applied during expression evaluation; they are applied during order execution.
Constants
The following math constants are available. Their names are reserved and cannot be used as variable identifiers.
| Constant | Description |
|---|---|
euler | Base of natural logarithms |
pi | Pi constant |
phi | Golden ratio |
rphi | Inverse golden ratio |
Series and fields
Expressions can use the standard price series open, high, low, close, volume, hl2, hlc3, ohlc4, and hlcc4.
Indicator blocks expose their series as variables, whose names match the block identifier (or distinct identifiers if the block produces multiple outputs).
To access a series’ history, add an index in brackets: series[n] refers to the value n bars in the past (0 is the current bar, 1 the previous bar, etc.).
Direct access, for example close, is equivalent to close[0].
If the index exceeds the available history, the returned value is NaN. In [[condition]] and [[if]], such a value does not validate the condition until the required history is available.
Example: detect a bullish crossover of a fast moving average above a slow one (the fast MA moves from below to above between the previous and current bars):
[[condition]]
condition = "ema_fast > ema_slow and ema_fast[1] <= ema_slow[1]"Strings
Some variables expose strings, for example ticker and tickerid or block metadata.
Strings can be compared with == or != and concatenated with +.
String literals use single quotes.
[[condition]]
condition = "tickerid == 'BINANCE:BTCUSDT'"You can extract a substring with s[start:stop].
The form s[] returns the length.
Operators
Expressions support the following operators.
Examples use real values and return real values (comparisons and logic return 0 or 1).
Arithmetic
| Symbol | Name |
|---|---|
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
% | Modulo |
^ | Power |
Comparisons (return 0.0 or 1.0)
| Symbol | Name |
|---|---|
< | Less than |
<= | Less or equal |
> | Greater than |
>= | Greater or equal |
== | Equal |
!= | Not equal |
The operators = and <> are accepted as aliases for == and !=.
Logical (return 0.0 or 1.0)
Logical operators treat any non-zero value as true and zero as false.
They return 1.0 for true and 0.0 for false.
| Symbol | Name |
|---|---|
and | AND |
or | OR |
not | NOT |
nand | NAND |
nor | NOR |
xor | XOR |
xnor | XNOR |
Example usage:
Suppose we want an entry condition that is true only if the RSI is below 30 and the closing price is above the ema_200 moving average.
[[condition]]
id = "entry_condition"
condition = "rsi_value < 30 and close > ema_200"The condition expression will evaluate to 1.0 (true) only if both comparisons are true.
If either is false, the result will be 0.0 (false).
Ternary
| Symbol | Name |
|---|---|
cond ? a : b | Ternary |
Unary
| Symbol | Name |
|---|---|
+x | Unary plus |
-x | Unary minus |
not x | Logical negation |
Update semantics in variable blocks
In a variable block, the variable is declared by the variable key.
Then initialization and formula must be provided as single expressions, and the engine applies the variable update internally.
To keep PineScript export unambiguous, initialization and formula must not contain statement separators (;) or assignment operators (:=, +=, -=, *=, /=, %=).
Available functions
Missing-value handling
| Name | Description |
|---|---|
na(x) | 1 if x is missing, else 0 |
nz(x) | 0 if x is missing, else x |
bool(x) | 1 if x is defined and non-zero, else 0 |
Basic math
| Name | Description |
|---|---|
abs(x) | Absolute value |
ceil(x) | Ceiling |
floor(x) | Floor |
exp(x) | Exponential |
log(x) | Natural logarithm |
log10(x) | Base-10 logarithm |
pow(a,b) | Power |
round(x) | Round to nearest |
sqrt(x) | Square root |
sign(x) | Sign of x (-1, 0, 1) |
min(...) | Minimum (at least 2 values) |
max(...) | Maximum (at least 2 values) |
avg(...) | Average (at least 2 values) |
clamp(min, x, max) | Clamp x to [min, max] |
inrange(min, x, max) | 1 if x is inside the range, else 0 |
random(...) | Uniform random number in a range |
Accepted forms: random(), random(min), random(min, max), and random(min, max, seed).
Without seed, the generator is initialized at runtime, so two separate backtests may produce different values.
Trigonometry and angles
| Name | Description |
|---|---|
sin(x) | Sine |
cos(x) | Cosine |
tan(x) | Tangent |
asin(x) | Arc-sine |
acos(x) | Arc-cosine |
atan(x) | Arc-tangent |
atan2(y,x) | Arc-tangent with quadrants |
sinh(x) | Hyperbolic sine |
cosh(x) | Hyperbolic cosine |
tanh(x) | Hyperbolic tangent |
asinh(x) | Inverse hyperbolic sine |
acosh(x) | Inverse hyperbolic cosine |
atanh(x) | Inverse hyperbolic tangent |
todegrees(x) | Convert radians to degrees |
toradians(x) | Convert degrees to radians |