Expression Reference

Every condition in your YAML — every gate, trigger, activation, cancellation, and price calculation — is an expression. This page documents the complete expression language: what you can write, how it evaluates, and w...

Written By Axiom Admin

Last updated About 1 month ago

Expression Reference

Every condition in your YAML — every gate, trigger, activation, cancellation, and price calculation — is an expression. This page documents the complete expression language: what you can write, how it evaluates, and where the edges are.

The expression language is what replaces Pine Script in your workflow. Instead of writing code, you write readable conditions like PRICE_CLOSE > EMA_50 && RSI_K < 30. The engine compiles these once, then evaluates them on every bar by looking up token values and applying operators.

When examples use names like EMA_50, RSI_K, BB_LOWER, or ATR_14, assume those are custom tokens you connected from indicators on your chart. They are not built in.

If you are looking for the list of available token names, see Default Tokens. This page covers what you can do with those tokens once you have them.


The basics

An expression is a string that produces either a boolean result (true/false) or a numeric result (a number). Which type you need depends on where the expression is used:

Expression field

Expected result type

Example

Gate conditions

Boolean

PRICE_CLOSE > EMA_200

Trigger conditions

Boolean

CROSSOVER(PRICE_CLOSE, EMA_50)

Activation expressions

Boolean

RSI_K < 30 && VOLUME > 500000

Cancel / reset expressions

Boolean

PRICE_CLOSE < PRICE_LOW[5]

Limit price expressions

Numeric

PRICE_CLOSE - (2 * MINTICK)

Stop price expressions

Numeric

POSITION_AVG_PRICE - (100 * MINTICK)

If an expression returns the wrong type for its field — a number where a boolean is expected, or vice versa — the engine produces a type error.


Operators

Comparison operators

Compare two numeric values. Return a boolean.

Operator

Meaning

Example

>

Greater than

PRICE_CLOSE > 50000

<

Less than

RSI_K < 30

>=

Greater than or equal

VOLUME >= 1000000

<=

Less than or equal

PRICE_CLOSE <= BB_LOWER

==

Equal (within mintick tolerance)

BAR_DAYOFWEEK == 2

!=

Not equal

POSITION_DIRECTION != 0

Both operands must be numeric. Comparing a boolean to a number produces a type error. If you want to check whether a boolean token is true, use it directly: SESSION_ISMARKET — not SESSION_ISMARKET == TRUE.

Equality uses mintick tolerance. == does not test exact floating-point equality. It tests whether the two values are within half a mintick of each other. This prevents false negatives from floating-point rounding. In practice, PRICE_CLOSE == 50000 will match a close of 50000.005 on an asset with a mintick of 0.01. This is almost always what you want.

na propagation. If either operand is na (not available — meaning the data does not exist yet), all comparisons return false except !=, which returns true when exactly one side is na. This means conditions that reference tokens without enough history will silently fail rather than erroring. A condition like PRICE_CLOSE[200] > 50000 will be false on the first 200 bars because PRICE_CLOSE[200] is na.

Logical operators

Combine boolean values. Return a boolean.

Operator

Meaning

Example

&&

Logical AND

PRICE_CLOSE > EMA_50 && RSI_K < 70

`\

\

`

Logical OR

`SESSION_ISMARKET \

\

SESSION_ISPREMARKET`

You must use && and ||. The words AND and OR are not recognized as operators. If you write AND or OR, the parser will treat them as token names and attempt to look them up. Since no such tokens exist, the engine will raise an "Unknown identifier/token" error and the strategy will not run. Always use && and ||. Both operands must be boolean.

Arithmetic operators

Operate on numeric values. Return a number.

Operator

Meaning

Example

+

Addition

POSITION_AVG_PRICE + (50 * MINTICK)

-

Subtraction

PRICE_HIGH - PRICE_LOW

*

Multiplication

VOLUME * PRICE_CLOSE

/

Division

STRATEGY_NETPROFIT / STRATEGY_CLOSEDTRADES

%

Modulo (remainder)

BAR_INDEX % 10 == 0 — every 10th bar

Division by zero produces an error. If the divisor evaluates to zero, the engine halts with an error rather than returning na or infinity. Use SAFE_DIV if the divisor might be zero (see Functions below).

na propagation. If either operand is na, the result is na. Arithmetic on missing data does not error — it produces missing data.

Unary operators

Operator

Meaning

Example

- (prefix)

Negation

-PRICE_CLOSE (the negative of the close price)

+ (prefix)

Identity (no-op)

+PRICE_CLOSE (same as PRICE_CLOSE)

!

Logical NOT

!SESSION_ISMARKET — true when not in market session

Unary - requires a numeric operand. ! requires a boolean operand. The engine distinguishes unary minus from subtraction by context: -PRICE_CLOSE at the start of an expression or after an operator is unary; PRICE_CLOSE - PRICE_LOW is subtraction.

Ternary operator

The conditional operator. Selects one of two values based on a boolean condition.

condition ? value_if_true : value_if_false

Example:

POSITION_DIRECTION > 0 ? PRICE_CLOSE - POSITION_AVG_PRICE : POSITION_AVG_PRICE - PRICE_CLOSE

This returns the profit distance regardless of direction — subtracting in the right order for longs vs. shorts.

Rules:

  • The condition must be boolean.

  • Both branches must be the same type (both numeric or both boolean).

  • Ternaries can be nested, but readability degrades fast. If you need complex branching, consider splitting the logic across multiple YAML fields (gate + trigger) instead.


Operator precedence

When an expression has multiple operators, precedence determines which evaluates first. Higher precedence evaluates first.

Precedence

Operators

Associativity

6 (highest)

Unary -, unary +, !

Right

5

*, /, %

Left

4

+, -

Left

3

>, <, >=, <=, ==, !=

Left

2

&&

Left

1

`\

\

`

Left

0 (lowest)

?: (ternary)

Right

What this means in practice:

PRICE_CLOSE > EMA_50 && RSI_K < 30 evaluates as (PRICE_CLOSE > EMA_50) && (RSI_K < 30) because comparisons (precedence 3) bind tighter than && (precedence 2).

PRICE_HIGH - PRICE_LOW * 0.5 evaluates as PRICE_HIGH - (PRICE_LOW * 0.5) because multiplication (precedence 5) binds tighter than subtraction (precedence 4). If you wanted half the range, write (PRICE_HIGH - PRICE_LOW) * 0.5.

When in doubt, use parentheses. They cost nothing and they make intent explicit. (PRICE_CLOSE > EMA_50) && (RSI_K < 30) is clearer than relying on precedence, even when the result is the same.


Literals

Three built-in literal values:

Literal

Type

Value

Use

TRUE

Boolean

True

Explicit true in ternary branches or default gate conditions

FALSE

Boolean

False

Explicit false

NA

Numeric

Not available

Represents missing data. Useful in ternary expressions: VOLUME > 0 ? PRICE_CLOSE / VOLUME : NA

Numeric literals work as expected: 50000, 3.14, 0.001, -2.5. The engine recognizes any valid number.


Token history references

Any numeric token can be referenced at a historical offset using bracket syntax:

TOKEN_NAME[N]

Where N is a non-negative integer (0 or greater). N = 0 is the current bar (same as writing the token without brackets). N = 1 is the previous bar. N = 10 is ten bars ago.

Rules:

  • No spaces inside the brackets: PRICE_CLOSE[1] is correct. PRICE_CLOSE[ 1 ] will fail.

  • The offset must be a whole number: PRICE_CLOSE[1.5] is an error.

  • Negative offsets are not allowed: PRICE_CLOSE[-1] is an error.

  • History references work on both numeric and boolean tokens. SESSION_ISMARKET[1] returns whether the previous bar was in the market session. POSITION_ACTIVE[1] returns whether a position was open on the previous bar.

  • Literal values (TRUE, FALSE, NA) do not support history references.

What happens when history is not available: If N exceeds the number of available bars, the token returns na. On bar 50, PRICE_CLOSE[100] is na. Any comparison with na returns false (except !=). Any arithmetic with na returns na. This means conditions that reference deep history will silently not fire on early bars. This is usually the correct behavior — you do not want a 200-bar lookback condition firing on bar 10 — but it is invisible unless you check the expression diagnostics.

Practical examples

Expression

What it means

PRICE_CLOSE > PRICE_CLOSE[1]

Current close is higher than the previous close (up bar)

PRICE_HIGH > HIGHEST(PRICE_HIGH, 20)

Current high exceeds the highest high of the last 20 bars

VOLUME > VOLUME[1] * 2

Volume is more than double the previous bar's volume

PRICE_CLOSE[1] > EMA_50

The previous bar's close was above the EMA — useful for crossover confirmation


Functions

Functions are called with uppercase names and parentheses: FUNCTION_NAME(arg1, arg2, ...). No namespace prefixes — write ABS(x), not math.abs(x). The engine will reject namespace syntax and suggest the correct form.

Type conversion and utility

Function

Arguments

Returns

What it does

NA(x)

1 numeric

Boolean

Returns true if x is na (not available)

NZ(x)

1 numeric

Numeric

Returns x if it is a number, 0 if it is na

NZ(x, y)

2 numeric

Numeric

Returns x if it is a number, y if x is na

BOOL(x)

1 numeric

Boolean

Converts a number to boolean: true if x != 0 and x is not na

INT(x)

1 numeric

Numeric

Truncates x to an integer (rounds toward zero)

FLOAT(x)

1 numeric

Numeric

Explicit float cast — ensures x is treated as a floating-point value. Equivalent to float(source) in Pine Script.

NZ is one of the most useful functions in the language. When a token might be na on early bars (because it needs history to warm up), wrapping it in NZ() gives you a safe default of zero instead of propagating na through the entire expression.

Core math

Function

Arguments

Returns

What it does

ABS(x)

1 numeric

Numeric

Absolute value

CEIL(x)

1 numeric

Numeric

Round up to nearest integer

FLOOR(x)

1 numeric

Numeric

Round down to nearest integer

ROUND(x)

1 numeric

Numeric

Round to nearest integer

ROUND(x, n)

2 numeric

Numeric

Round x to n decimal places

ROUND_TO_MINTICK(x)

1 numeric

Numeric

Round x to the nearest mintick increment — essential for price expressions

SIGN(x)

1 numeric

Numeric

Returns -1, 0, or 1

SQRT(x)

1 numeric

Numeric

Square root

EXP(x)

1 numeric

Numeric

e raised to the power of x

LOG(x)

1 numeric

Numeric

Natural logarithm

LOG10(x)

1 numeric

Numeric

Base-10 logarithm

POW(x, y)

2 numeric

Numeric

x raised to the power of y

MAX(x, y)

2 numeric

Numeric

The larger of x and y

MIN(x, y)

2 numeric

Numeric

The smaller of x and y

AVG(x, y)

2 numeric

Numeric

The mean of x and y

SUM(x, n)

2 numeric

Numeric

Rolling sum of x over the last n bars. Equivalent to Pine Script's math.sum(x, n). Use x + y for simple addition.

Trigonometry

Function

Arguments

Returns

What it does

SIN(x)

1 numeric

Numeric

Sine of x (radians)

COS(x)

1 numeric

Numeric

Cosine of x (radians)

TAN(x)

1 numeric

Numeric

Tangent of x (radians)

ASIN(x)

1 numeric

Numeric

Arcsine of x

ACOS(x)

1 numeric

Numeric

Arccosine of x

ATAN(x)

1 numeric

Numeric

Arctangent of x

TODEGREES(x)

1 numeric

Numeric

Convert radians to degrees

TORADIANS(x)

1 numeric

Numeric

Convert degrees to radians

Technical analysis helpers

These functions operate on the bar history of a token value. They are the expression-language equivalents of common Pine Script ta.* functions.

Function

Arguments

Returns

What it does

CROSSOVER(a, b)

2 numeric

Boolean

True when a crosses above b (was below or equal, now above)

CROSSUNDER(a, b)

2 numeric

Boolean

True when a crosses below b (was above or equal, now below)

CROSS(a, b)

2 numeric

Boolean

True when a crosses b in either direction

RISING(x, n)

2 numeric

Boolean

True when x has been rising for n consecutive bars

FALLING(x, n)

2 numeric

Boolean

True when x has been falling for n consecutive bars

HIGHEST(x, n)

2 numeric

Numeric

Highest value of x over the last n bars

LOWEST(x, n)

2 numeric

Numeric

Lowest value of x over the last n bars

HIGHESTBARS(x, n)

2 numeric

Numeric

How many bars ago x reached its highest value in the last n bars

LOWESTBARS(x, n)

2 numeric

Numeric

How many bars ago x reached its lowest value in the last n bars

CHANGE(x, n)

2 numeric

Numeric

The difference x - x[n] — how much x changed over n bars

MOM(x, n)

2 numeric

Numeric

Momentum — same as CHANGE(x, n)

ROC(x, n)

2 numeric

Numeric

Rate of change — (x - x[n]) / x[n] * 100 as a percentage

BARSSINCE(cond)

1 boolean

Numeric

Number of bars since cond was last true

RANGE(x, n)

2 numeric

Numeric

The range (highest minus lowest) of x over the last n bars

ALL_TIME_HIGH(x)

1 numeric

Numeric

The all-time highest value of x from bar 0 to now

ALL_TIME_LOW(x)

1 numeric

Numeric

The all-time lowest value of x from bar 0 to now

CROSSOVER and CROSSUNDER are among the most commonly used functions. CROSSOVER(PRICE_CLOSE, EMA_50) means "the close was at or below the EMA on the previous bar and is now above it." This is a single-bar event — it fires on the bar where the cross happens and is false on the next bar.

HIGHEST and LOWEST are your breakout detection tools. PRICE_HIGH > HIGHEST(PRICE_HIGH[1], 20) means "this bar's high exceeds the highest high of the previous 20 bars." Note the [1] offset on the argument — without it, the current bar's high would be included in the lookback, and the condition would fire when the current bar is the highest, which is always true by definition.

Axiom helper functions

These are utility functions specific to the expression engine, designed for common trading math.

Function

Arguments

Returns

What it does

SAFE_DIV(x, y, fallback)

3 numeric

Numeric

Returns x / y if y != 0, returns fallback if y == 0 or na — avoids division-by-zero errors

CLAMP(x, lo, hi)

3 numeric

Numeric

Clamps x between lo and hi: returns lo if x < lo, hi if x > hi, else x

BETWEEN(x, lo, hi)

3 numeric

Boolean

True if lo <= x <= hi

NEAR(x, target, tolerance)

3 numeric

Boolean

True if ABS(x - target) <= tolerance

ROUND_STEP(x, step)

2 numeric

Numeric

Rounds x to the nearest multiple of step

DIST(a, b)

2 numeric

Numeric

Absolute distance: ABS(a - b)

PCT_FROM(base, value)

2 numeric

Numeric

Percentage distance from base to value: ((value - base) / base) * 100. Errors if base is zero or na.

PCT_CHANGE(current, previous)

2 numeric

Numeric

Percentage change from previous to current: ((current - previous) / previous) * 100. Takes two explicit values, not a lookback length — use PCT_CHANGE(PRICE_CLOSE, PRICE_CLOSE[5]) for a 5-bar percentage change. Errors if previous is zero or na.

SAFE_DIV is essential whenever you divide by a value that could be zero. STRATEGY_NETPROFIT / STRATEGY_CLOSEDTRADES will error if there are zero closed trades. SAFE_DIV(STRATEGY_NETPROFIT, STRATEGY_CLOSEDTRADES, 0) returns 0 instead. The third argument is the fallback value returned when the divisor is zero or na.

CLAMP is useful in price expressions. CLAMP(PRICE_CLOSE - 100 * MINTICK, PRICE_LOW, PRICE_HIGH) computes a stop price but ensures it stays within the current bar's range.

BETWEEN simplifies range checks. BETWEEN(RSI_K, 30, 70) is cleaner than RSI_K >= 30 && RSI_K <= 70 and means exactly the same thing.


Type system

The expression engine has two types: float (numbers) and bool (true/false). Every token, literal, and function result is one of these two types. The engine is strict about types:

  • Arithmetic operators (+, -, *, /, %) require numeric operands and produce numeric results.

  • Comparison operators (>, <, >=, <=, ==, !=) require numeric operands and produce boolean results.

  • Logical operators (&&, ||) require boolean operands and produce boolean results.

  • ! requires a boolean operand.

  • Unary - requires a numeric operand.

Mixing types produces a clear error message: "Type error for operator '+': Requires numeric operands. Got 'bool' and 'float'." If you see a type error, check which token or sub-expression is producing the wrong type and either convert it (BOOL() to go from number to bool) or restructure the expression. For example, use a ternary SESSION_ISMARKET ? 1 : 0 to convert a boolean token to a number in arithmetic contexts.


Common patterns

Crossover with confirmation

CROSSOVER(PRICE_CLOSE, EMA_50) && VOLUME > VOLUME[1]

Enter when price crosses above the EMA, but only if volume is increasing. The crossover fires on a single bar; the volume condition adds a confirming filter on the same bar.

Mean reversion entry

PRICE_CLOSE < BB_LOWER && RSI_K < 25

Price below the lower Bollinger Band and RSI deeply oversold. Both BB_LOWER and RSI_K would be custom tokens connected from indicators on your chart.

Percentage-based take profit price

POSITION_AVG_PRICE * 1.03

A limit price expression that sets the take profit at 3% above the average entry price. Use in the take_profit_limit_price field.

ATR-based stop loss price

POSITION_AVG_PRICE - ATR_14 * 2

A stop price expression that sets the stop two ATR values below entry. ATR_14 would be a custom token from an ATR indicator on your chart. Use in the stop_loss_stop_price field.

Breakeven stop

POSITION_AVG_PRICE + (5 * MINTICK)

A stop price just above the entry price — five ticks of profit to cover commission. Gate this exit on POSITION_PROFIT_PERCENT > 1 so it only activates after the trade has moved in your favor.

Session and time filtering

SESSION_ISMARKET && BAR_HOUR >= 10 && BAR_HOUR <= 14

Only evaluate during market hours, and only between 10:00 and 14:00. Useful as a setup gate condition for intraday strategies that should avoid the open and close volatility.

Multi-setup cross-referencing

TREND_FILTER_CONFIRMED && PULLBACK_SETUP_CONFIRMED

An entry gate that requires two independent setups to both be in CONFIRMED state. This is how you compose layered regime filters using the dynamic state tokens described in Default Tokens.


Debugging expressions

When an expression does not behave as expected:

  1. Check the error table first. If the strategy is not running at all, there is likely a validation error — a missing required field, a token typo, or a syntax error in an expression. Unknown token names and unrecognized functions produce "Unknown identifier/token" errors that prevent execution.

  2. Enable the expression value label (Inputs tab → Show expression value label). Every expression shows its current value, evaluation status (EVAL, SKIP, EMPTY), and usage.

  3. Simplify. Replace the entire expression with something trivially true, like PRICE_CLOSE > 0. If the intent fires, the expression syntax is fine — the problem is in the condition logic. Add conditions back one at a time.

  4. Check token names. A single character mismatch (ema_50 vs. EMA_50) will cause an "Unknown identifier/token" error. Token names are case-sensitive and must match exactly.

  5. Check token values. Even if the name is correct, verify the value makes sense. A custom token showing 0 on every bar means the source connection is broken.

  6. Test on a specific bar. Scroll to a bar where you believe the condition should be true. Read the expression diagnostic for that bar. If it shows false, look at each sub-condition and find which one is failing.

The most common expression problems fall into two categories. First: validation errors that prevent the strategy from running — token typos, function name misspellings, and syntax mistakes. These are caught during the first-bar diagnostics and shown in the error table. Second: logic errors that produce valid but wrong results — a precedence assumption that groups operators differently than you intended, or a condition that requires more history than is available on early bars.

See Troubleshooting for the full diagnostic workflow.