Skip to content
FactorQX
intermediatepine-scriptalertsautomation

Creating Alerts in Pine Script

Understand the difference between alertcondition and the alert function in Pine Script v5, and how to fire reliable notifications without acting on unconfirmed bars.

3 min read

This is educational material about programming. Nothing here is investment advice, a trading signal, or a recommendation.

Alerts let a Pine Script emit an event when a coded condition becomes true. Pine Script v5 offers two mechanisms — alertcondition() and alert() — that differ in how they are configured and how flexibly they fire. This article explains both, when to use each, and the bar-confirmation pitfall that trips up most newcomers. If you are new to the language, read Getting Started with Pine Script first.

alertcondition: declarative alert slots

alertcondition() declares a named condition at compile time. It does not fire anything on its own. Instead, it registers an alert slot that the user wires up through TradingView's Create Alert dialog after adding the script to a chart.

alertcondition.pine
//@version=5
indicator("EMA Cross Alert", overlay = true)
 
fast = ta.ema(close, 9)
slow = ta.ema(close, 21)
 
crossUp = ta.crossover(fast, slow)
crossDown = ta.crossunder(fast, slow)
 
plot(fast, color = color.aqua)
plot(slow, color = color.orange)
 
alertcondition(crossUp, title = "Cross Up", message = "Fast EMA crossed above slow EMA")
alertcondition(crossDown, title = "Cross Down", message = "Fast EMA crossed below slow EMA")

Each alertcondition must be at the top level of the script. The message is a static string shown when the alert fires; you can interpolate dynamic values using placeholders like {{close}} and {{ticker}} that TradingView substitutes at runtime. The user must select which condition to use and the alert frequency in the dialog — the script cannot choose for them.

alert: imperative, in-code firing

alert() is called inside the execution flow, typically guarded by an if. It fires whenever the code reaches it during a real-time bar, and the message can be built dynamically with normal string concatenation — no template placeholders required.

alert-function.pine
//@version=5
indicator("RSI Threshold Alert")
 
rsi = ta.rsi(close, 14)
 
if ta.crossover(rsi, 70)
    alert("RSI crossed above 70 on " + syminfo.ticker + " at " + str.tostring(close), alert.freq_once_per_bar_close)
 
plot(rsi)
hline(70)

The second argument controls frequency:

  • alert.freq_once_per_bar — fires the first time the condition is true within a bar.
  • alert.freq_once_per_bar_close — fires only when the bar closes, evaluating the final confirmed values.
  • alert.freq_all — fires on every qualifying tick (noisy; use sparingly).

To use alert(), the user still creates a single alert in TradingView with the condition set to Any alert() function call — but they no longer pick a specific named slot.

Which to use

alertconditionalert()
Where it runsDeclared at top levelCalled in execution flow
MessageStatic + placeholdersFully dynamic string
Multiple distinct alertsOne per conditionOne handler, many call sites
Works in strategiesNo (indicators only)Yes (indicators and strategies)

Reach for alertcondition when you want the user to choose between named, self-documenting alerts in the dialog. Reach for alert() when the message needs runtime data or when you fire from many code paths.

The unconfirmed-bar caveat

During a live bar, indicator values update on every tick and can flip back and forth before the bar closes. An alert that fires mid-bar may reference a condition that is no longer true once the bar finalizes — a phenomenon related to repainting.

For notifications you intend to rely on, prefer confirmation:

confirmed-alert.pine
//@version=5
indicator("Confirmed Cross", overlay = true)
 
fast = ta.ema(close, 9)
slow = ta.ema(close, 21)
crossUp = ta.crossover(fast, slow)
 
// Only act on closed-bar state
if crossUp and barstate.isconfirmed
    alert("Confirmed EMA cross up", alert.freq_once_per_bar_close)

barstate.isconfirmed is true only on the final tick of a bar. Combined with alert.freq_once_per_bar_close, it ensures the alert reflects settled values rather than intrabar noise. This is an engineering safeguard against false events — not a statement that any particular event is meaningful to trade on.

Testing alerts

Because alerts only fire in real time, you cannot see them replayed on historical bars. Validate your logic by plotting the same condition with plotshape so you can visually confirm where it triggers across history, then attach the alert once the markers look correct. Keep messages concise and machine-parsable if a downstream webhook will consume them.

Where to go next

Many alerting strategies depend on data from a higher timeframe than the chart. To pull that in safely — without introducing lookahead bias — continue with Multi-Timeframe Data with request.security.

Educational content. This article covers software development and research methods only. It is not investment advice, a trading signal, or a recommendation. See our disclaimer.