XCREENER Docs
XQLLanguage Reference

Timeframes & Offsets

Cross-timeframe qualifiers, historical offsets, and the lookback ceiling.

Cross-Timeframe Qualification

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

timeframe = h1

h4::rsi(14) > 50

Here, rsi(14) is computed from h4 data even though the query's default timeframe is h1. A qualifier attaches to a single primary expression (a series reference, function call, or let reference) or to a parenthesized group as a whole:

d::(bb_upper(20, 2) - bb_lower(20, 2)) > 0

let references can't be re-qualified

A let-bound name's timeframe is fixed at the point of declaration. h4::myBinding is a parse error: qualify the timeframe inside the let's own right-hand side instead.

Offset Indexing

Suffix any expression with [-N] to reach N bars back from the latest bar:

rsi(14)[-1] < 30   // one bar back

An expression with no bracket suffix is offset 0 (the latest bar). Like timeframe qualifiers, an offset attaches to a single primary expression or a parenthesized group as a whole, not to a bare, unparenthesized arithmetic expression:

close - sma(50)[-1]

Here the [-1] binds only to sma(50), not to the subtraction as a whole. To offset the whole expression, either parenthesize it:

(close - sma(50))[-1] > 0

or bind it to a let first and offset the reference:

let diff = close - sma(50)
diff[-1] > 0

The 300-Bar Lookback Ceiling

Before a query runs, plan() computes the minimum bars of history (minLookback) every timeframe needs, and rejects the query outright if any timeframe's requirement exceeds 300 bars. This is a hard rejection at plan time, not a soft warning or silent truncation.

Roughly, per function family:

FunctionsMinimum lookback
sma/wma/stdev/max/min/sum/avg/bb_*length + offset
rsi/rma/atr/change/roc/momlength + 1 + offset
highest/lowestlength + 1 + offset (the window excludes the current bar)
Bare series (close, open, …)1 + offset
macd_line(fast, slow, signal)max(fast, slow) + offset
macd_signal/macd_hist(fast, slow, signal)max(fast, slow) + signal - 1 + offset

A crossover/crossunder/cross call adds one extra bar beyond what each operand already requires, for the "one bar back" comparison the crossing check itself performs.

Mapping Calendar Phrasing to a Timeframe

"200-day moving average" or "52-week high" don't automatically mean "use d:: or w::": they mean "pick whichever timeframe keeps the bar count under 300 while staying natural."

A naive mapping can blow the ceiling

"52-week high" naively maps to d::highest(high, 365) (or ~252 trading days): that exceeds 300 bars and is rejected at plan time. The natural, correct mapping uses weekly bars instead:

close >= w::highest(high, 52)

52 weekly bars is comfortably under the ceiling, and arguably a more natural rendering of "52-week" anyway.

"200-day moving average" maps to d::sma(200), regardless of the query's own default timeframe: get this wrong and the result is silently incorrect (computed on the wrong timeframe), not rejected, since sma(200) on an hourly timeframe is syntactically valid, just not "200 days."

On this page