XCREENER Docs
XQLLanguage Reference

Syntax

Literals, operators, and grouping.

Query Structure

A query has a header (KEY = value pragmas, in any order, optionally interleaved with let statements) followed by a core-logic body: a single boolean expression that decides whether an instrument matches.

market = "CRYPTO"
timeframe = h1
columns = [close, rsi(14)]
sort = rsi(14) asc
limit = 20

rsi(14) < 30

Supported pragma keys

Only market, timeframe, columns, sort, and limit are recognized. Any other key (e.g. GROUPBY) is a parse error.

market and timeframe

market is a quoted string, restricted to one of:

"CRYPTO" | "FOREX" | "INDICES" | "COMMODITIES" | "METALS"

timeframe is a bare identifier, restricted to one of:

m15 | m30 | h1 | h2 | h4 | d | w

Both are closed enums: an unrecognized value (market = "STOCKS", timeframe = h3) is rejected at parse time.

Series References

Five bare identifiers resolve to a series on the expression's timeframe: close, open, high, low, volume. See OHLCV Series for what each one represents, bar ordering, and how they differ by market.

close > open

Comparators and Logical Operators

The core-logic body supports the comparators <, >, <=, >=, ==, !=, the logical operators and, or, not, and parentheses for grouping boolean sub-expressions.

(rsi(14) < 30 or not (close > 100)) and close >= 50

Arithmetic Operators

+, -, *, / combine any two value expressions: series references, function calls, let references, literals, or other arithmetic expressions. * and / bind tighter than + and -; operators at the same precedence level associate left-to-right.

close - sma(50) > 0
volume > avg(volume, 20) * 2

Same-precedence chains associate left-to-right, not right-to-left:

sma(50) - sma(20) - sma(10) > 0

parses as (sma(50) - sma(20)) - sma(10), not sma(50) - (sma(20) - sma(10)).

Arithmetic is valid anywhere a value expression already is: comparator operands, let right-hand sides, columns entries, and sort.

Unary minus only negates literals

-10 is a negative literal. -ema(50) is a parse error: unary minus does not negate an arbitrary expression. To negate a computed value, subtract it from a literal: 0 - ema(50).

Parenthesized Grouping

Any value expression can be wrapped in parentheses to reorder precedence:

(bb_upper(20, 2) - bb_lower(20, 2)) / bb_mid(20, 2) > 0.04

A parenthesized group is a compound unit: as a whole, it can carry a <timeframe>:: qualifier or a [-N] offset, which an unparenthesized arithmetic expression cannot (see Timeframes & Offsets):

d::(bb_upper(20, 2) - bb_lower(20, 2)) > 0
(close - sma(50))[-1] > 0

A single-atom group ((rsi(14))) is legal and semantically a no-op. Grouping nests to arbitrary depth.

Two kinds of parentheses

Value-expression grouping (this section) is distinct from the pre-existing boolean grouping used for (condition and condition). The parser disambiguates the two automatically: a genuine boolean sub-expression like (rsi(14) < 30 and volume > 0) still parses as boolean grouping, while something like (bb_upper(20, 2) - bb_lower(20, 2)) / bb_mid(20, 2) > 0.04 parses as a value-expression group, since its parenthesized content isn't itself a complete boolean expression. You don't need to write anything differently to get the right one.

Offset Indexing

Suffix any expression with [-N] to reach N bars back from the latest bar. An expression with no suffix defaults to offset 0.

rsi(14)[-1] < 30

[1] (no leading minus) is a parse error, not "1 bar ago": the minus sign is required.

Cross-Timeframe Qualification

Prefix any expression with timeframe:: to evaluate it against a different timeframe than the query's default:

h4::rsi(14) > 50

See Timeframes & Offsets for the full rules, including how offsets and timeframe qualifiers compose with arithmetic and parenthesized groups.

crossover, crossunder, cross

Three boolean primitives detect a value crossing another:

crossover(a, b)   // a crossed from at-or-below b to strictly above
crossunder(a, b)  // a crossed from at-or-above b to strictly below
cross(a, b)       // either direction

a and b are each any value expression (a series, a function call, or either qualified with <timeframe>::/[-N]). Cross primitives compose with and/or/not and parentheses, and each requires exactly two arguments.

Cross primitives are not value expressions

crossover/crossunder/cross can only appear where a boolean expression is valid: never inside columns, sort, or as a comparator operand, and never offset-indexed directly (crossover(a,b)[-1] is invalid). To check whether a crossover happened recently, bind it to a let first: see Cookbook for the full recipe.

columns and sort

columns accepts an array of value expressions, or boolean-typed let references (never a bare comparison or logical expression):

columns = [close, rsi(14), h4::rsi(14)]

sort accepts a single value expression (never boolean) followed by a required asc or desc:

sort = rsi(14) asc

sort is independent of columns: an expression doesn't need to appear in both.

limit

A single positive integer, capping the number of results kept after running the query:

limit = 20

On this page