VP Script
Other symbols and timeframes
A script can read another instrument or another resolution. Both are shaped so that a value cannot appear on a bar before it was knowable, because the alternative is a study that backtests beautifully and fails live.
Another symbol or timeframe
indicator('Daily trend on any chart') const dailyMa = request.security('', '1D', (ctx) => ta.sma(ctx.close, 50))plot(dailyMa, { title: '50 SMA (1D)', width: 2 })Pass an empty symbol for the chart’s own instrument, or a ticker to read a different one. The compute callback runs over the foreign bars and returns a series in foreign space; the result is then projected back onto your chart. Omit it to get the foreign close.
Why the last value looks “late”
A foreign bar’s value appears only from the chart bar that opened after that foreign bar closed. On a 5-minute chart, today’s daily value does not exist until today’s daily bar has closed.
This is the whole point. The still-forming daily bar knows the day’s high so far, and letting that into an earlier 5-minute bar would let a study act on information from its own future.
Why there is no `lookahead_on`
Pine offers lookahead_on, which makes a higher-timeframe value available from the START of that period using the value it will only have at its CLOSE. On a daily chart, every day of a week would show that week’s final close, including Monday, before the week has happened.
It can only do that on history, because live the future has not occurred yet. So the indicator draws one way on historical bars and another way in real time: that is repainting, and it is why a strategy built on it backtests beautifully and does not match when traded.
Asking for enough history
A study with a long lookback needs foreign bars from BEFORE the visible range, or its first value appears somewhere in the middle of the chart. The host already asks for a generous default; say so explicitly when yours needs more.
// Monthly Ichimoku: 52 bars of Senkou B, plus 26 of displacement.const monthly = request.security('', '1M', (ctx) => ta.sma(ctx.close, 52), { warmupBars: 78,})It is a request, not a guarantee: the datafeed decides what it will serve, so a script must still handle nulls. If history looks short and the warm-up was asked for, the limit is the data provider rather than the language.
Hiding a layer finer than the chart
A 60-minute study on a daily chart is not a view of anything, and a multi-timeframe indicator normally declines to draw one. timeframe is how a script asks:
indicator('MTF layer') const tf = input.string('Timeframe', '1D', { options: ['60', '1D', '1W', '1M'] }) // -1 means the requested layer is FINER than the chart. Skip it.if (timeframe.compare(tf, timeframe.period) >= 0) { plot(request.security('', tf, (ctx) => ta.sma(ctx.close, 20)), { title: `SMA ${tf}` })}A resolution neither side can parse compares equal, so an unfamiliar one is shown rather than silently dropped.
timeframe.secondsOf converts any resolution, not just the chart’s, which is useful when a study needs to know how many chart bars a foreign period spans, and it saves carrying your own table of resolution strings.
Inside the bar
request.security_lower_tf answers the questions aggregation destroys: order and timing within a bar. It returns one array per chart bar, holding the finer bars inside it.
indicator('Did the low come first?', { pane: 'separate' }) const inside = request.security_lower_tf('', '5') const lowFirst = inside.map((fine) => { if (fine === null) return null // not covered: say so, do not guess let hi = -Infinity, lo = Infinity, hiAt = -1, loAt = -1 for (let i = 0; i < fine.length; i++) { if (fine[i].high > hi) { hi = fine[i].high; hiAt = i } if (fine[i].low < lo) { lo = fine[i].low; loAt = i } } return loAt < hiAt ? 1 : 0}) plot(lowFirst, { style: 'histogram' })A bar the finer feed does not fully span is null, never an empty array. The two are different facts: an empty array means covered and nothing traded, which is a halt, while null means we could not see all of it. A high taken from half a bar’s duration is wrong in a direction no reader could detect, so it is not offered.
Depth is limited by how far the datafeed will page backwards, so on a long chart the finer data covers the recent stretch and is null before it. Handle the null branch and your study degrades honestly instead of quietly describing a shorter period than the reader assumes.