Tutorial

Building a Repaint-Free Alert System in Pine Script

Pine Script has three ways to fire alerts, and they confuse everyone. Here is what each does, when to use it, and how to make sure your alerts never fire on a signal that later disappears.

11 min read

Alerts are how Pine Script reaches the outside world. They notify you, and on eligible plans they POST to a webhook that can drive external execution. But there are three different alert mechanisms, they behave differently, and the biggest risk with all of them is firing on a signal that has not been confirmed yet. This guide covers both: how to choose the mechanism and how to keep alerts repaint-free.

MechanismHow it firesBest for
alertcondition()User creates the alert from the conditionIndicators, simple conditions
alert()Called in code, with a dynamic messageFlexible, dynamic payloads
Strategy alertsFire on strategy order eventsAutomating strategy entries/exits

alertcondition: the indicator classic

alertcondition declares a condition in your indicator. It does not fire on its own; instead it makes the condition available in TradingView's Create Alert dialog, where the user picks it. The message is fixed at creation time. This is the simplest mechanism and it suits indicators where the user sets up the alert manually.

pine
//@version=6
indicator("alertcondition example", overlay = true)

crossUp = ta.crossover(close, ta.sma(close, 50))

// Makes the condition selectable in the alert dialog.
// Fires per the alert's own settings, so choose "Once Per Bar Close".
alertcondition(crossUp, title = "Cross up", message = "Price crossed above SMA50")

plot(ta.sma(close, 50))

alert(): flexible and dynamic

The alert() function is called directly in your code when a condition is true, and its message can be built dynamically at runtime. That makes it the right choice when you need the payload to contain live values, like the symbol, price, or a computed size, which is essential for webhook automation. You control exactly when it fires and what it says.

pine
//@version=6
indicator("alert() with dynamic payload", overlay = true)

crossUp = ta.crossover(close, ta.sma(close, 50))

// Fire only on a confirmed bar so the alert cannot repaint
if crossUp and barstate.isconfirmed
    msg = '{"action":"buy","symbol":"' + syminfo.ticker +
         '","price":' + str.tostring(close) + '}'
    alert(msg, alert.freq_once_per_bar_close)

plot(ta.sma(close, 50))

Strategy alerts: order-event driven

When you write a strategy rather than an indicator, the entries and exits themselves can generate alerts. You attach alert messages to your strategy.entryand strategy.exit calls, and TradingView fires them when those orders trigger. This is the cleanest path when the thing you want to automate is literally the strategy's own trades, because the alert is tied to the order rather than a separate condition you have to keep in sync.

PineScripter generates confirmed-bar alerts and webhook-ready JSON payloads by default

The repainting trap in alerts

Here is the part that bites people. On the current forming bar, a condition can become true and then false again before the bar closes, because price is still moving. If your alert fires the instant the condition turns true, you can send a signal, or an order, that later evaporates. That is the realtime-fluctuation kind of repainting from repainting explained, and in an automated context it means phantom trades.

The fix is twofold. Gate the condition on barstate.isconfirmed so it can only fire once the bar has closed, and set the alert frequency to once per bar close (alert.freq_once_per_bar_close) so TradingView does not re-fire it intrabar. Together, these guarantee the alert reflects a finished, real signal.

Formatting webhook JSON

If the alert feeds a webhook, the message should be valid JSON that your receiver expects. Build it as a string with the fields your external service needs, action, symbol, quantity, and any risk parameters, and keep it minimal and unambiguous. The receiver, not Pine Script, connects to the broker; that division is explained in can Pine Script connect to an API, broker, or webhook. Because the webhook may place real orders, correctness of the payload and the confirmed-bar gating are safety-critical, not cosmetic.

A reliable alert checklist

Before you trust an alert system, confirm four things. You picked the right mechanism: alertcondition for simple indicator alerts, alert() for dynamic payloads, strategy alerts for automating a strategy's trades. Your conditions are gated on barstate.isconfirmed. Your alert frequency is once per bar close. And, if using a webhook, your JSON is valid and minimal. Get those right and your alerts will fire on real, confirmed signals only.

Wiring all of this correctly by hand is fiddly, and it is exactly where generic AI slips, because it cannot see that a mid-bar signal repaints. PineScripter generates confirmed-bar, repaint-free alerts and correctly formatted webhook payloads by default, so the alert you send is the signal you meant. If you are building an alert or automation pipeline, start from code you can trust at PineScripter.


Disclaimer: PineScripter is a coding tool for Pine Script development. It does not provide financial advice and does not guarantee trading profits. Always backtest strategies thoroughly and understand the risks before live trading.