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:
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.
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.
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.
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
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_falseExample:
POSITION_DIRECTION > 0 ? PRICE_CLOSE - POSITION_AVG_PRICE : POSITION_AVG_PRICE - PRICE_CLOSEThis 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.
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:
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
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
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
Trigonometry
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.
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.
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 < 25Price 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.03A 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 * 2A 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 <= 14Only 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_CONFIRMEDAn 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:
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.
Enable the expression value label (Inputs tab → Show expression value label). Every expression shows its current value, evaluation status (EVAL, SKIP, EMPTY), and usage.
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.Check token names. A single character mismatch (
ema_50vs.EMA_50) will cause an "Unknown identifier/token" error. Token names are case-sensitive and must match exactly.Check token values. Even if the name is correct, verify the value makes sense. A custom token showing
0on every bar means the source connection is broken.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.