DayTraderScripts
← Back to blog
Pine ScriptToolingAlerts

Multi-timeframe alerts in Pine Script: the right way and the wrong way

How to fire alerts based on conditions from a higher timeframe without repainting, lag, or duplicate fires.

DayTraderScripts Desk·May 7, 2026·2 min read

Multi-timeframe (MTF) logic is the most-asked Pine Script question — and the most-broken Pine Script feature. Half the published "MTF indicators" repaint, fire duplicate alerts, or silently lag a bar. Here's how to do it correctly.

The repaint trap

The most common bug:

//@version=5
indicator("Bad MTF", overlay=true)
htf_close = request.security(syminfo.tickerid, "60", close)
alertcondition(close > htf_close, "Above hourly close")

This fires alerts that don't reflect what actually happened. The 60-minute close keeps updating inside the current hourly bar, so the alert fires, un-fires, and re-fires as the higher timeframe candle forms.

This is repainting. It's the reason published Pine scripts get one-star reviews.

The fix: barmerge.lookahead_off + closed-bar values

The correct version:

//@version=5
indicator("Good MTF", overlay=true)
htf_close = request.security(syminfo.tickerid, "60", close[1], lookahead=barmerge.lookahead_off)
alertcondition(close > htf_close, "Above last closed hourly")

Two changes:

  1. close[1] — reference the prior bar's close on the higher timeframe. That bar is already finished and cannot repaint.
  2. lookahead=barmerge.lookahead_off — explicitly forbid Pine from using future data.

This is the boilerplate. Use it for every MTF reference. Every time.

Higher-TF EMA without repainting

Same pattern, applied to a derived value:

//@version=5
indicator("Daily 50EMA on intraday chart", overlay=true)

dailyEMA = request.security(
    syminfo.tickerid,
    "D",
    ta.ema(close, 50)[1],
    lookahead=barmerge.lookahead_off
)

plot(dailyEMA, color=color.orange, linewidth=2)

ta.ema(close, 50)[1] is the daily 50EMA from the prior day's close. It updates once per day at the daily close. No repaint.

Avoiding duplicate alert fires

The next bug: an alert that fires on every intraday bar while the condition is true, not just on the bar where it becomes true.

Wrong:

alertcondition(close > dailyEMA, "Above daily EMA")

Right:

crossedUp = ta.crossover(close, dailyEMA)
alertcondition(crossedUp, "Crossed above daily EMA")

alertcondition fires every bar the input is true. ta.crossover is only true on the bar where the cross happens. Use cross functions for state-change events; use raw conditions only for things you genuinely want to know about every bar.

The pattern we use for entries

A typical confirmed-entry pattern:

//@version=5
indicator("MTF Confirmed Entry", overlay=true)

// Higher TF bias
dailyEMA = request.security(
    syminfo.tickerid, "D",
    ta.ema(close, 50)[1],
    lookahead=barmerge.lookahead_off
)
dailyBias = close > dailyEMA  // long bias if true

// Intraday trigger
hourlyHigh = request.security(
    syminfo.tickerid, "60",
    high[1],
    lookahead=barmerge.lookahead_off
)
triggerLong = ta.crossover(close, hourlyHigh)

// Combined alert
longEntry = dailyBias and triggerLong and volume > ta.sma(volume, 20)
alertcondition(longEntry, "MTF long entry")
plotshape(longEntry, style=shape.triangleup, location=location.belowbar, color=color.green)

Three layers:

  • Daily bias (50EMA reclaim) — slow filter.
  • Hourly trigger (break of prior hour's high) — fast trigger.
  • Intraday volume confirmation — execution filter.

Each layer references the prior bar of its respective timeframe. No repaint. Single alert fire per genuine event.

When to break the rule

There's one case where it's OK to read the in-flight higher-TF bar: when you're intentionally showing the user a live read and you've labeled it as such (e.g., "live daily VWAP"). Even then, do not fire alerts on it.

TL;DR

  1. Always pass lookahead=barmerge.lookahead_off.
  2. Reference [1] (the prior bar) when you want a closed value.
  3. Use ta.crossover / ta.crossunder for alerts. Don't fire on raw conditions.
  4. Combine bias + trigger + confirmation; don't put everything in one boolean.

That's it. Four rules. They'd save 80% of broken Pine scripts on TradingView.

Keep reading