Higher Timeframe Candle Overlay with Pine Script v5

Summary

Goal: Display the last four candles of a higher timeframe (e.g., 1‑hour or 4‑hour) in a small overlay window while the main chart remains on a 5‑minute chart.
Solution: Use Pine Script v5’s built‑in request.security() to fetch higher‑timeframe OHLC arrays, then plot them with plotcandle() inside a indicator() that draws on a separate pane.


Root Cause

  • Pine scripts run on the chart’s native timeframe; without explicit calls they cannot see data from other resolutions.
  • Attempting to draw higher‑timeframe candles directly on the main chart leads to misaligned bars and runtime errors.

Why This Happens in Real Systems

  • Single‑resolution execution: The Pine engine evaluates the script once per bar of the chart’s timeframe.
  • Security wrapper: request.security() is the only safe bridge to request data from a different timeframe.
  • Array limitations: Pine v5 introduced array support, allowing us to store the last N higher‑timeframe candles for custom rendering.

Real-World Impact

  • Incorrect visual cues: Without proper multi‑timeframe handling, traders may make decisions on malformed price data.
  • Performance penalties: Repeated request.security() calls per bar can degrade script execution speed, especially on lower timeframes.
  • User frustration: Misaligned candles or missing data cause confusion and erode trust in the indicator.

Example or Code (if necessary and relevant)

//@version=5
indicator('4‑Candle Higher‑TF Overlay', overlay=false, max_bars_back=500)

//--- Settings -------------------------------------------------
higherTF   = input.timeframe('60', 'Higher Timeframe')   // 1‑hour by default
candlesCnt = input.int(4, 'Number of Candles', minval=1, maxval=10)

//--- Fetch higher‑timeframe OHLC as series --------------------
[htOpen, htHigh, htLow, htClose] = request.security(syminfo.tickerid,
                                                   higherTF,
                                                   [open, high, low, close],
                                                   barmerge.gaps_off,
                                                   barmerge.lookahead_on)

//--- Store last N candles in arrays ---------------------------
var float[] oArr = array.new_float()
var float[] hArr = array.new_float()
var float[] lArr = array.new_float()
var float[] cArr = array.new_float()

if barstate.islast
    // Shift arrays to keep only the most recent `candlesCnt` values
    if array.size(oArr) >= candlesCnt
        array.shift(oArr)
        array.shift(hArr)
        array.shift(lArr)
        array.shift(cArr)
    // Append current higher‑tf candle
    array.push(oArr, htOpen)
    array.push(hArr, htHigh)
    array.push(lArr, htLow)
    array.push(cArr, htClose)

//--- Plot the stored candles ----------------------------------
for i = 0 to array.size(oArr) - 1
    plotcandle(array.get(oArr, i),
               array.get(hArr, i),
               array.get(lArr, i),
               array.get(cArr, i),
               offset = -(array.size(oArr) - 1 - i),
               title = 'HT Candle ' + str.tostring(i + 1))

How Senior Engineers Fix It

  • Use barmerge.lookahead_on to align higher‑timeframe data with the current bar, ensuring the most recent completed candle is shown.
  • Cache values in arrays only on the last bar of the higher timeframe to avoid unnecessary re‑allocation.
  • Limit max_bars_back to the exact number of candles needed, reducing memory footprint.
  • Separate pane (overlay=false) so the overlay does not clutter the main 5‑minute chart.
  • Add input validation (e.g., preventing candlesCnt > 10) to keep the script within Pine’s runtime limits.

Why Juniors Miss It

  • Assume request.security() returns a series that can be plotted directly without handling bar alignment.
  • Forget to store values in arrays, causing the script to display only the latest higher‑timeframe candle repeatedly.
  • Overlook performance costs, leading to laggy charts when the lower timeframe is very fast (e.g., 1‑minute).
  • Neglect Pine’s execution model, resulting in misplaced plotcandle() calls inside loops that run on every tick instead of once per higher‑timeframe candle.

Leave a Comment