Why Pine Script Alerts Fail, and the Fixes That Work

Why Pine Script Alerts Fail, and the Fixes That Work

By HorizonAI Team · 14 min read · Intermediate

Why Pine Script Alerts Fail, and the Fixes That Work

A marker appears on your TradingView chart, price crosses your level, and your phone stays silent. Or the alert fires one bar late, sends an old message, or never shows the condition you expected in the Create Alert dialog. Those are different failures, so they need different fixes.

Short answer: a Pine Script alert only works when three pieces line up: the script exposes an alert event, you create a running TradingView alert from that event, and the event occurs on the realtime bar under the alert’s selected frequency. If you edited the script or its inputs after creating the alert, delete and recreate that alert because TradingView runs a server-side snapshot of the earlier script and settings.

The fastest diagnosis is to identify the symptom first. Start with the Create Alert condition list, then verify the signal on the realtime bar, then inspect alert frequency, script errors, expiration, and delivery logs. The Pine v6 template below gives you separate long and short conditions, confirmed-bar control, and optional dynamic alert() messages without pretending an alert can place a trade.

Start with the symptom, not the code

TradingView alerts have two layers. Your indicator or strategy provides one or more alert events. You then use TradingView’s Create Alert dialog to create a running alert from a selected event. A script can expose the event, but it cannot create, start, or repair the running alert on your account.

Use this quick map before changing anything:

What you seeMost likely causeFirst fix
Your script is absent from the Condition listNo eligible alertcondition(), alert(), or strategy alert eventAdd the correct alert mechanism, save, add the script to the chart again
The condition is visible but never firesSignal only happened historically, frequency blocks it, or conditions are too strictWatch the realtime bar and test with a temporary visual marker
It fires late or intrabarThe alert frequency and bar-confirmation logic disagreeChoose close-only confirmation or allow intrabar behavior deliberately
It keeps using old inputs or an old messageAlert was created before the code/input editDelete it and create a fresh alert
TradingView records it as fired but no phone/email/webhook arrivesDelivery settings, endpoint, or notification channel issueInspect the alert log and test the destination outside Pine
It worked, then stoppedAlert expired, script hit a runtime error, or chart context changedCheck expiration, alert log, and script errors

TradingView’s own alert guide separates alert events, frequency, expiration, and notification delivery. Its support article on failed alerts also points to alert logs and script/runtime issues as practical checks. Pine Script alerts documentation and TradingView’s alert troubleshooting guide are the source of truth for the platform behavior.

Fix 1: Your script does not appear in Create Alert

If the script name is not selectable under Condition, solve that before thinking about RSI thresholds or notifications. The alert menu can only show alert events your active chart script makes available.

Use alertcondition() for named indicator choices

alertcondition() is usually the cleanest choice for an indicator with multiple signals. Each call creates a separately named item that the trader can choose in the Create Alert dialog. The condition must be a Boolean series. Its title and message are fixed strings, which makes it a strong fit for clear Long and Short menu entries.

For example, an RSI crossing up through 50 while price is above its EMA can expose a RSI/EMA Long condition. The opposite setup exposes RSI/EMA Short. The user chooses one condition per running alert, or creates two alerts if both directions matter.

Use alert() for a dynamic message from one alert

alert() triggers from code and can build a message with live values such as symbol, close, RSI, and EMA. In the Create Alert dialog, select the indicator and choose Any alert() function call. Do not expect individual alertcondition() entries to appear if your script only uses alert().

A common mistake is adding alertcondition() to a strategy and expecting its named choices to behave like indicator entries. For strategies, TradingView supplies alert types around order fills and any alert() calls, with options shown in the strategy alert interface. Read the platform’s strategy documentation and strategy-alert support page before wiring a strategy’s entries to alerts.

Fix checklist: Save the script, add or refresh it on the chart, open Create Alert, select the script under Condition, and choose the exact event type your code provides. If the event still is not there, confirm you are editing the same script instance that is on the chart.

Fix 2: The alert never fires even though the chart shows a signal

The historical chart can mislead you here. Most script alerts trigger only from executions on the realtime bar, not because a matching condition existed months ago in historical data. A plotted arrow proves your calculation found a historical setup. It does not prove that a live alert was armed when that setup occurred.

First, create a low-stakes test condition that you can see. Plot a shape on the exact Boolean expression used by the alert. Then wait for the current bar to satisfy it while the alert is active. Do not test by scrolling back to a prior crossover and expecting an old event to arrive.

Your condition can also be stricter than it looks. In the template later in this guide, a long setup requires all of these on the same bar:

  1. RSI crosses above 50.
  2. Close is above the selected EMA.
  3. The bar is confirmed if close-only mode is enabled.

On a 15-minute chart, that is not “RSI is above 50.” It is one specific crossing event that must survive to the bar close. If you want a pullback-style condition instead, use an explicit threshold rule such as rsiValue < 30 followed by a two-bar recovery confirmation. That is a different signal and should have a different alert name.

For visual debugging, compare this topic with a combined oscillator build like RSI + MACD in Pine Script v6. The useful habit is the same: plot each component before trusting the final Boolean expression.

Fix 3: It fires late, early, or more than once

An alert that fires “late” is often doing exactly what you told it to do. The important choice is whether the condition should count during the forming bar or only after that bar closes.

Confirmed-bar alerts are stable

When barstate.isconfirmed is part of the signal, the indicator waits for the bar close. On a 5-minute chart, a crossover that happens at 10:01 and remains true will alert at the close near 10:05. That delay is the cost of knowing the bar did not reverse before closing.

This is usually the right default for RSI and moving-average crossover alerts. It reduces false intrabar triggers and makes a chart marker, an alert, and a later review much easier to reconcile.

Intrabar alerts react sooner, but can disappear

With confirmation turned off, a realtime value can cross 50 mid-bar and cross back before the close. An intrabar alert can fire even if the final historical bar no longer shows the crossover. That behavior overlaps with repainting concerns, but the immediate fix is not to label every early alert a bug. Decide whether the system is meant to react intrabar or only after confirmation.

If your broader issue is a signal changing after the fact, use the dedicated guide on why Pine Script repaints. Keep the two diagnoses separate: alert frequency controls when an armed alert may fire, while repainting describes whether the underlying signal changes as data evolves.

Match alert frequency to the intended behavior

TradingView lets you select frequency in the alert dialog. For alert() calls, Pine can also choose a frequency mode. The useful defaults are:

  • Once per bar close: best for the confirmed RSI/EMA setup below.
  • Once per bar: sends the first qualifying event during each bar.
  • All: can send every qualifying intrabar call, which is rarely what a simple crossover alert needs.

A crossover is normally a one-event condition anyway. Repeated alerts more often come from a persistent rule such as rsiValue > 50 combined with intrabar calculations. Use ta.crossover() or ta.crossunder() when you need the transition itself, not the entire time spent above or below a level.

Fix 4: Strategy alerts need different expectations

Indicators and strategies can both create alerts, but their events are not interchangeable. An indicator often exposes named alertcondition() entries or alert() calls. A strategy can alert from alert() calls and from simulated order fills created by strategy.entry, strategy.exit, and related order functions.

An order-fill alert belongs to the strategy’s broker-emulator behavior. It is not proof of an order at a real broker, and it does not make a Pine strategy execute trades. If you are building a strategy, set entries and exits explicitly, then create the strategy alert with the appropriate event option in TradingView.

A second difference is execution timing. Strategies normally calculate at bar close. If you need a strategy to evaluate on every realtime update, its declaration can use calc_on_every_tick = true. That changes the behavior you are testing, so pair it with an explicit alert frequency and verify the result in realtime. It is not a magic switch that makes historical bars reproduce every intrabar tick.

For a complete example of a strategy that uses alerts as part of its workflow, see automating a Supertrend strategy with Pine alerts. Keep alert logic separate from position and exit logic. It makes failures much faster to isolate.

Build a reliable RSI/EMA crossover alert indicator

This indicator is designed as a diagnostic-friendly starting point. It includes visible long and short markers, separate alertcondition() entries, optional dynamic alert() calls, and settings that make the timing choice obvious.

Paste it into TradingView’s Pine Editor, save it, add it to a chart, then create an alert from either RSI/EMA Long or RSI/EMA Short. If you turn on dynamic alerts, create a separate alert using Any alert() function call.

//@version=6
indicator("RSI EMA Crossover Alerts", overlay = true)

// Core signal settings.
rsiLength = input.int(14, "RSI Length", minval = 2)
emaLength = input.int(50, "EMA Length", minval = 2)
confirmedBarsOnly = input.bool(true, "Only alert on confirmed bar closes")

// Optional dynamic alert() settings.
useDynamicAlerts = input.bool(false, "Enable dynamic alert() messages")
alertFrequency = input.string(
     "Once per bar close",
     "Dynamic alert frequency",
     options = ["Once per bar close", "Once per bar", "All"])

// Calculate the two filters.
rsiValue = ta.rsi(close, rsiLength)
emaValue = ta.ema(close, emaLength)
barIsReady = not confirmedBarsOnly or barstate.isconfirmed

// A signal requires a fresh RSI centerline crossover and EMA direction filter.
longSignal = ta.crossover(rsiValue, 50) and close > emaValue and barIsReady
shortSignal = ta.crossunder(rsiValue, 50) and close < emaValue and barIsReady

// Chart visuals make it easy to compare alert events with the source condition.
plot(emaValue, "EMA", color = color.orange, linewidth = 2)
plotshape(longSignal, title = "Long marker", style = shape.triangleup, location = location.belowbar, color = color.lime, size = size.small, text = "L")
plotshape(shortSignal, title = "Short marker", style = shape.triangledown, location = location.abovebar, color = color.red, size = size.small, text = "S")
bgcolor(longSignal ? color.new(color.lime, 88) : shortSignal ? color.new(color.red, 88) : na)

// These appear as separate choices in TradingView's Create Alert dialog.
alertcondition(longSignal, title = "RSI/EMA Long", message = "RSI crossed above 50 and close is above the EMA.")
alertcondition(shortSignal, title = "RSI/EMA Short", message = "RSI crossed below 50 and close is below the EMA.")

// Optional dynamic messages. Create an alert using "Any alert() function call" when enabled.
longMessage = "RSI/EMA LONG | " + syminfo.ticker + " | close=" + str.tostring(close, format.mintick) + " | RSI=" + str.tostring(rsiValue, "#.##")
shortMessage = "RSI/EMA SHORT | " + syminfo.ticker + " | close=" + str.tostring(close, format.mintick) + " | RSI=" + str.tostring(rsiValue, "#.##")

if useDynamicAlerts and longSignal
    if alertFrequency == "Once per bar close"
        alert(longMessage, alert.freq_once_per_bar_close)
    else if alertFrequency == "Once per bar"
        alert(longMessage, alert.freq_once_per_bar)
    else
        alert(longMessage, alert.freq_all)

if useDynamicAlerts and shortSignal
    if alertFrequency == "Once per bar close"
        alert(shortMessage, alert.freq_once_per_bar_close)
    else if alertFrequency == "Once per bar"
        alert(shortMessage, alert.freq_once_per_bar)
    else
        alert(shortMessage, alert.freq_all)

Test the template in the right order

  1. Set Only alert on confirmed bar closes to on.
  2. Leave Enable dynamic alert() messages off initially.
  3. Create one alert using RSI/EMA Long, set its dialog frequency to Once Per Bar Close, and keep the expiration date visible.
  4. Create a separate Short alert if you want both directions.
  5. On a liquid symbol and a 5-minute or 15-minute chart, compare the next live marker with the next alert-log entry.
  6. Only then enable dynamic messages and create a new Any alert() function call alert.

The two alert mechanisms are intentionally separate. If you enable dynamic alerts but leave a Long alertcondition() alert active too, both can fire on the same long signal. That is expected, not duplicate behavior from one alert.

Fix 5: Old settings, expired alerts, and delivery failures

TradingView stores a copy of the script, inputs, symbol, interval, and alert settings when you create an alert. Editing the Pine code later does not update that already-running copy. Changing RSI length from 14 to 7, toggling confirmation, or rewriting a message therefore requires deleting the old alert and creating a new one.

Use this reset procedure whenever you modify alert logic:

  1. Save the code and confirm the updated indicator is applied to the chart.
  2. Delete every existing alert made from the older version.
  3. Create replacement alerts from the correct condition and frequency.
  4. Give each one a descriptive name such as ES 15m RSI EMA long close-only.
  5. Trigger and inspect a live test before relying on it.

If TradingView’s alert log shows Fired but you received no notification, the Pine condition already did its job. Check the destination next: browser/app notification permission, email spam handling, SMS availability, or the receiving server for a webhook. For a webhook, a valid script alert does not guarantee the destination accepts the request. Check endpoint logs, expected payload format, response status, and whether the endpoint is publicly reachable.

Also inspect script errors. A runtime error can stop a script from calculating, which leaves an armed alert with no new qualifying evaluation. Open the chart’s script status and fix the line reported before recreating the alert.

Common alert mistakes that waste the most time

Mistake: Testing an alert against a historical arrow. A crossover printed last Tuesday cannot trigger a new alert today.

Do this: Create the alert before the next live setup. Use a 5-minute chart and a visible plotshape() tied to the same Boolean condition, then compare the marker with the alert log.

Mistake: Changing the RSI length and assuming the active alert follows it. The chart’s new settings and the server alert can now be running different logic.

Do this: After every code or input change, delete and recreate the alert. Put the timeframe, direction, and close-only choice in the alert name so you can audit it later.

Mistake: Selecting Once Per Bar Close while expecting a notification as soon as RSI briefly crosses 50. Those requirements conflict.

Do this: For stable alerts, use barstate.isconfirmed and Once Per Bar Close. For intrabar alerts, turn confirmation off and accept that a later closed bar may not retain the crossover.

Mistake: Using rsiValue > 50 when you mean “crossed above 50.” The first expression remains true across many bars.

Do this: Use ta.crossover(rsiValue, 50) for longs and ta.crossunder(rsiValue, 50) for shorts. Combine each with a direction filter, such as close above or below a 50 EMA.

Mistake: Treating an indicator alert as a trade execution. An alert is a notification event, not a broker order.

Do this: Treat the alert as one component of your workflow. If you are testing a ruleset, use a strategy with explicit entries, exits, position sizing, commission, and slippage assumptions, as covered in how to backtest a trading strategy.

Pro tips for alerts you can audit later

Give every condition one job. A named Long condition should not also secretly attempt to notify shorts or manage exits. Separate events make the Create Alert menu and the alert log readable.

Keep the chart context in the alert name. BTCUSD 15m RSI14 EMA50 long confirmed tells you more six weeks later than Alert 3. The script message can include price and RSI, but the alert name identifies the intended configuration.

Use one chart timeframe per alert. A 15-minute alert and a 1-hour alert can both use RSI 14, yet they are different systems. Create each intentionally rather than changing chart intervals under an existing alert.

Build visual evidence into the script. The EMA plot, triangle markers, and light background in the template are not decoration. They let you answer whether the calculation, the running alert, or the notification destination failed.

Generating this without writing the code yourself

HorizonAI can generate a Pine Script v6 indicator from a plain-English specification, then compile-check it and help you edit the result in chat. For this setup, give it the full alert behavior rather than asking vaguely for an “RSI alert.”

Build a Pine Script v6 overlay indicator named RSI EMA Crossover Alerts. Use RSI length 14 and EMA length 50 as inputs. Create a long signal when RSI crosses above 50 and close is above the EMA, and a short signal when RSI crosses below 50 and close is below the EMA. Add a toggle that requires confirmed bar closes by default. Plot the EMA, long and short markers, and background highlights. Add separate alertcondition entries named RSI/EMA Long and RSI/EMA Short. Also add an optional alert() mode with dynamic messages containing ticker, close, and RSI, plus a user-selectable frequency of once per bar close, once per bar, or all.

You’ll get code you can inspect and revise, then paste into TradingView’s Pine Editor and use to create the running alerts yourself. HorizonAI writes and checks the script; it does not place trades or create TradingView alerts on your behalf. Try it free →

FAQs

Why does my Pine Script alert show a condition but never trigger?

The alert must be active before the condition occurs on a realtime bar. Check that the selected frequency permits the event, that the condition is not blocked by confirmed-bar logic, and that the script has no runtime error.

Do I need to recreate TradingView alerts after editing my Pine Script?

Yes. TradingView runs an alert from a saved snapshot of the script and its inputs. Save the revised script, delete the old alert, and create a new alert from the updated chart instance.

What is the difference between alertcondition() and alert() in Pine Script?

alertcondition() creates named conditions you select individually in the Create Alert dialog, with static messages. alert() lets the script trigger a dynamic message, and you create one alert using Any alert() function call.

Why did my RSI crossover alert fire but the marker disappear after the candle closed?

Your alert likely evaluated intrabar while the candle was still forming, then RSI moved back across 50 before the close. Require barstate.isconfirmed and use Once Per Bar Close if you only want closed-bar signals.

Final thoughts

Most Pine Script alert failures come down to a mismatch between the script event, the running alert snapshot, and realtime timing. Solve the observable symptom first, then use a marker and the alert log to prove each layer works.

For this RSI/EMA setup, keep close-only confirmation on until you have a specific reason to act intrabar. A stable alert you can explain is more useful than a faster one you cannot reproduce.

Related articles

Questions about Pine Script alerts? Join our Discord to discuss with other traders!