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.
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.
//@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.
//@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
alertcondition | alert() | |
|---|---|---|
| Where it runs | Declared at top level | Called in execution flow |
| Message | Static + placeholders | Fully dynamic string |
| Multiple distinct alerts | One per condition | One handler, many call sites |
| Works in strategies | No (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:
//@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.