Multi-timeframe analysis is one of the most useful things you can add to a TradingView indicator. Knowing whether the daily trend confirms your 15-minute entry is genuinely valuable context. The function that enables all of it is request.security, and it is also the single function most commonly implemented incorrectly. The mistake is almost always invisible until you watch your indicator live — and then you see signals appear mid-bar and vanish when the higher-timeframe bar closes.
This post is a precise account of what request.security actually does: what it fetches, which bar it evaluates, and exactly how to use it so your indicator behaves identically on historical bars and in live trading.
What request.security does
request.security(symbol, timeframe, expression) evaluates the given expression in the context of the specified symbol and timeframe and returns the result as a series on your current chart. It runs on every bar of your chart and returns the value of the expression as it was at the corresponding point in the higher timeframe.
The question of which higher-timeframe bar is returned on any given lower-timeframe bar is the source of almost every repainting bug written with this function. When your 15-minute chart is on the 14th bar of a given trading day, there is both a completed previous daily bar and a current, unfinished daily bar that has been building all day. request.security by default gives you the current, still-open bar. That value changes on every tick. On historical bars, where the daily bar is always closed, you get the final settled value. The historical and live charts are showing you different things.
//@version=6
indicator("Basic request.security", overlay=true)
// Fetches the daily close value onto any lower timeframe chart.
// Without [1], this reads the CURRENT unfinished daily bar — it repaints.
dailyClose = request.security(syminfo.tickerid, "D", close)
plot(dailyClose, "Daily close (repaints)", color=color.orange)The confirmed-bar pattern
The fix is to request the previous bar's value by adding [1]inside the expression argument. This offsets the series by one barwithin the higher timeframe before fetching, so you always receive the last fully closed bar's value. It never updates once that bar is done, which means historical and live behavior match perfectly.
//@version=6
indicator("Confirmed request.security", overlay=true)
// [1] shifts to the previous bar of the expression BEFORE fetching.
// This means we always read the last fully CLOSED daily bar.
// The value is stable — it will not change once that daily bar closes.
dailyClose = request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_off)
plot(dailyClose, "Confirmed daily close", color=color.aqua)The lookahead=barmerge.lookahead_off parameter is the default and is redundant here, but stating it explicitly makes the intent clear to anyone reading the code later. The dangerous setting, barmerge.lookahead_on, is covered separately in the lookahead explainer. For now: never use it.
Fetching computed series, not just price
The expression argument does not have to be a simple price series like close or high. It can be any Pine Script expression: an RSI, a moving average, a boolean condition. This is where the confirmed-bar pattern trips up most developers, because the [1] offset must be applied inside the expression, not outside the function call.
//@version=6
indicator("Fetching a computed series", overlay=false)
// The expression argument can be any series — not just a built-in price.
// WRONG: passes a locally-computed series without confirming the bar.
// The daily RSI will use the current unfinished daily bar.
dailyRsiWrong = request.security(syminfo.tickerid, "D", ta.rsi(close, 14))
// RIGHT: [1] is applied inside the expression, before the fetch.
// This is the only correct way to pass a computed series.
dailyRsiRight = request.security(syminfo.tickerid, "D", ta.rsi(close, 14)[1], lookahead=barmerge.lookahead_off)
plot(dailyRsiWrong, "Daily RSI (repaints)", color=color.red, linewidth=1)
plot(dailyRsiRight, "Daily RSI (confirmed)", color=color.aqua, linewidth=2)The wrong version passes ta.rsi(close, 14) without an offset. Pine Script evaluates this expression in the daily context but still uses the current, unfinished daily bar's close. The right version offsets the result by [1] inside the expression before returning it, so the RSI is always computed from the last closed daily bar.
The quick-reference for every pattern
| Pattern | Repaints? | When to use |
|---|---|---|
| request.security(sym, tf, close) | Yes | Never in a final indicator |
| request.security(sym, tf, close[1], lookahead=off) | No | Always — this is the correct pattern |
| request.security(sym, tf, ta.rsi(close,14)) | Yes | Never — expression uses current bar |
| request.security(sym, tf, ta.rsi(close,14)[1], lookahead=off) | No | Always — offset inside the expression |
| lookahead=barmerge.lookahead_on | Yes (future leak) | Never |
Fetching multiple values efficiently
Pine Script allows up to 40 unique request.security contexts per script. Each call with a unique symbol-timeframe combination uses one slot. Fetching four separate values from the same daily context in four separate calls uses one slot, not four, because the context is the same. But to keep code clean and efficient, you can return multiple values from a single call using a tuple expression.
//@version=6
indicator("Tuple request — fetch multiple values in one call", overlay=false)
// A single request.security call can return multiple values as a tuple.
// This avoids the 40-call limit and is more efficient than two separate calls.
[htfRsi, htfMacd, htfSignal] = request.security(
syminfo.tickerid,
"D",
[ta.rsi(close, 14)[1],
ta.macd(close, 12, 26, 9)[0][1],
ta.macd(close, 12, 26, 9)[1][1]],
lookahead=barmerge.lookahead_off
)
plot(htfRsi, "Daily RSI", color=color.aqua)
plot(htfMacd, "Daily MACD line", color=color.green)
plot(htfSignal, "Daily signal line", color=color.red)This pattern is more readable and scales well. All three values come from one call, all use the confirmed-bar offset, and the tuple destructuring assigns them to named variables in one line.
Fetching from a different symbol
The first argument to request.security does not have to be syminfo.tickerid. You can fetch data from any symbol TradingView supports: indices, sector ETFs, correlated instruments, volatility measures. The confirmed-bar pattern is identical regardless of the symbol.
//@version=6
indicator("Fetching a different symbol", overlay=false)
// You can fetch data from any symbol, not just the current one.
// Here we fetch the VIX close as a market sentiment context indicator.
// Same confirmed-bar pattern applies: [1] prevents repainting.
vixClose = request.security("VIX", "D", close[1], lookahead=barmerge.lookahead_off)
plot(vixClose, "VIX daily close", color=color.purple)This opens up a class of indicators that are genuinely hard to build in most tools: correlation and relative-strength indicators that compare your instrument against a benchmark or sector index, or use an external volatility measure like the VIX as a regime filter. For a full treatment of multi-symbol scanning using Pine Script v6's dynamic requests, see the guide on dynamic requests in Pine Script v6.
Why getting this right by hand is where most bugs happen
The request.security confirmed-bar pattern is four characters — [1] and off — but the consequences of getting them wrong are invisible until you run the indicator live. A backtest on a repainting indicator shows clean signals; a live chart shows signals that move. The Pine Script repainting checker scans for the most common patterns, including bare request.security calls without the confirmed-bar offset.
When describing a multi-timeframe indicator to PineScripter in plain English, the generated code applies the confirmed-bar pattern by default. The Pine Script v6 manual is built into the AI through retrieval, so it writes against the real function signature and the correct lookahead semantics rather than pattern-matching from older training examples. For the full picture of how multi-timeframe indicators should be built from the ground up, the multi-timeframe indicator build guide walks through a complete working strategy using all of these patterns.
Disclaimer: PineScripter is a coding tool for Pine Script development. It does not provide financial advice and does not guarantee trading profits. Always backtest strategies thoroughly and understand the risks before live trading.