A practical guide to request.security_lower_tf() in Pine Script v6 — the rarely-explained sibling of request.security() that returns arrays of intrabar values from a lower timeframe. Covers a complete intrabar volume-delta example, why older bars come back with empty arrays, how the realtime bar behaves, and why intrabar precision is still an approximation rather than ground truth. Educational material only, not financial advice; intrabar data does not make a strategy profitable, and trading carries a real risk of loss.
request.security_lower_tf() fetches data from a timeframe lower than the chart's and returns it as an array — one element for every intrabar that fits inside the current chart bar. On a 1-hour chart requesting "1" (one minute), each chart bar yields an array of up to 60 values: your expression evaluated on each 1-minute intrabar. That is the direct answer: where request.security() returns a single value per chart bar, request.security_lower_tf() returns the set of values that composed it.
The array return type is what surprises people coming from request.security(). A single 4-hour bar can contain 240 one-minute intrabars, so a scalar cannot carry the result; Pine hands you an array<float> (or an array of whatever type your expression produces) and leaves the aggregation to you. You can also request a tuple of expressions — [open, close, volume] — and receive a tuple of arrays back, which is the idiomatic way to reconstruct intrabar candles.
Typical uses are things a chart-timeframe script simply cannot see: intrabar volume delta (how much volume printed on up-candles versus down-candles inside a bar), finer estimates of when inside a bar a level broke, or counting intrabar swings. The signature mirrors its sibling: symbol, timeframe, expression. Two differences matter immediately. First, the requested timeframe must be lower than or equal to the chart's — request a higher one and you get a runtime error (or empty arrays if you pass ignore_invalid_timeframe = true). Second, there is no lookahead parameter at all, because the function is not merging a slower series into a faster one; the repainting story is different, and we cover it below.
Everything in this guide is educational only. Intrabar data sharpens measurement; it is not an edge, and no example here is a recommendation to trade.
Here is a complete, compilable v6 indicator that classifies each intrabar as up or down and signs its volume accordingly — a standard volume-delta approximation that chart-timeframe volume cannot give you, because volume alone tells you nothing about direction inside the bar.
//@version=6
// Educational only — validate before trading; not financial advice.
indicator("Intrabar volume delta", overlay = false)
lowerTf = input.timeframe("1", "Intrabar timeframe")
// One array per chart bar; one element per intrabar inside it.
[oArr, cArr, vArr] = request.security_lower_tf(syminfo.tickerid, lowerTf, [open, close, volume])
float delta = 0.0
if array.size(cArr) > 0
for i = 0 to array.size(cArr) - 1
dir = array.get(cArr, i) >= array.get(oArr, i) ? 1.0 : -1.0
delta += dir * array.get(vArr, i)
plot(delta, "Intrabar volume delta", style = plot.style_columns,
color = delta >= 0 ? color.new(color.teal, 0) : color.new(color.red, 0))Reading it: the tuple request returns three parallel arrays — intrabar opens, closes, and volumes. The loop walks them together, signs each intrabar's volume by whether it closed at or above its open, and accumulates the result. Note the two defensive habits baked in. The accumulator is explicitly typed and initialised (float delta = 0.0) so an empty-array bar yields a clean zero rather than na noise, and the loop is guarded by array.size(cArr) > 0 — never assume the array has elements, for reasons the next section makes concrete.
Parallel-array indexing is safe here because all three arrays come from the same tuple request, so they always have identical lengths. If you make separate request.security_lower_tf() calls instead, you lose that guarantee in spirit and pay for extra request slots — each call counts against the script's request.*() budget.
Scroll back far enough on any chart and your intrabar arrays go empty. This is not a bug in your script; it is a data-availability boundary. TradingView serves a limited depth of intrabar history — the exact number of chart bars that get intrabar coverage depends on the ratio between the chart timeframe and the requested timeframe, and on account plan. A 1-minute feed under a daily chart burns through the intrabar budget roughly 60 times faster than under a 1-hour chart, so the covered window shrinks dramatically as the timeframe gap widens.
When a chart bar predates the available intrabar history, request.security_lower_tf() returns an empty array (size 0), not na. Three consequences follow.
First, every consumer of the data must handle size-zero arrays. An unguarded array.get(arr, 0) on an old bar throws a runtime error and halts the script. The array.size() > 0 guard in the example above is mandatory, not stylistic.
Second, any cumulative calculation silently starts from the first covered bar, not from the start of the chart. A running sum of intrabar delta computed over five years of daily bars is really a running sum over however many recent months have intrabar data. If you compare it against a full-history cumulative series, the baselines do not line up — a subtle apples-to-oranges error.
Third, and most relevant to backtesting: if a strategy's logic depends on intrabar arrays, the strategy effectively has two regimes — old bars where the logic degrades (or is skipped) and recent bars where it runs fully. Any performance statistics blend the two. An honest report states the coverage boundary explicitly: on which date does intrabar data begin on this chart, and how many trades fall on each side of it? If you cannot answer that, the tester output is not interpretable.
On historical bars the returned arrays are complete: every intrabar of that bar is present, and re-running the script reproduces them. On the realtime bar the array holds only the intrabars formed so far and grows as new ones complete — on a 1-hour chart with 1-minute intrabars, twenty minutes into the hour the array has about twenty elements, and your aggregation is a running partial value.
This is the same historical-versus-realtime asymmetry that causes classic repainting, wearing a different outfit. A value computed from a partial array on the live bar will generally differ from the value the completed bar shows after the close, so any signal fired from it mid-bar cannot be reproduced from history. The discipline is the usual one: gate actions on bar confirmation.
//@version=6
// Educational only — validate before trading; not financial advice.
indicator("Delta flip signal", overlay = true)
[oArr, cArr, vArr] = request.security_lower_tf(syminfo.tickerid, "1", [open, close, volume])
float delta = 0.0
if array.size(cArr) > 0
for i = 0 to array.size(cArr) - 1
delta += (array.get(cArr, i) >= array.get(oArr, i) ? 1.0 : -1.0) * array.get(vArr, i)
flipUp = delta > 0 and nz(delta[1]) <= 0
plotshape(flipUp and barstate.isconfirmed, "Delta flip", style = shape.triangleup,
location = location.belowbar, color = color.new(color.teal, 0))The barstate.isconfirmed gate means the signal only exists once the bar — and therefore its intrabar array — is final. Note also what this function does not have: a lookahead parameter. The infamous barmerge.lookahead_on future-leak of request.security() (which is only safe with a [1] offset and barmerge.lookahead_off in normal use) has no equivalent here, because lower-timeframe data never arrives from the future. The risks are instead the two covered in this article: partial arrays on the live bar, and missing arrays in deep history.
The honest closing point: intrabar data upgrades your resolution, but it does not give you the truth of what happened. Each 1-minute intrabar is itself an OHLC summary. You know its open, high, low, and close — you do not know the path. Did the high print before the low? Did price cross your level once or five times inside that minute? The data cannot say. Every problem that intrabar analysis solves at the chart-bar level reappears, smaller, one level down. You have shrunk the uncertainty box, not eliminated it.
This is exactly the caveat that applies to TradingView's Bar Magnifier, the Strategy Tester feature that uses lower-timeframe data to fill orders more precisely. It is a genuinely better approximation than the tester's default single-bar OHLC assumptions — and it is still an approximation, subject to the same intrabar-history depth limits described earlier, and still blind to the tick sequence inside each intrabar. Fills through gaps, spread, and queue position remain unmodelled.
There is also a subtler consistency issue: aggregating intrabar values does not always reproduce the chart-bar value exactly. Feed adjustments, session boundaries, and the occasional data discrepancy between the two resolutions mean your sum of 1-minute volumes can differ slightly from the bar's reported volume. Treat small mismatches as expected noise; treat large ones as a prompt to check session settings and symbol data rather than to trust either number blindly.
So use request.security_lower_tf() for what it is: a measurement instrument with a documented error bar. Validate the mechanics — empty-array guards, confirmed-bar gating, coverage boundaries — before believing any number it feeds into a backtest. None of this is financial advice; a higher-resolution backtest is still a description of the past, not a forecast, and trading carries a real risk of loss.
Educational only — not financial advice. Trading involves substantial risk of loss.