Crossover conditions and alert timing

TradingView crossover alerts: EMA, MACD and RSI

An EMA crossover alert detects the fast average moving from at or below the slow average on the previous bar to above it now. Use ta.crossover() for that event and ta.crossunder() for the reverse. MACD and RSI alerts use the same comparison with different series or levels. Wait for bar close when you need a confirmed chart-bar crossing; an intrabar crossing can disappear before close.

Build crossover conditions

How crossover and crossunder handle equality

For available values, ta.crossover(a, b) tests a > b now and a[1] <= b[1]. ta.crossunder(a, b) tests a < b now and a[1] >= b[1]. The comparison uses the previous bar, not the previous realtime tick. These illustrative values show why touching a line and crossing it are different events.

  • A crossing can remain true across several updates of the same open bar while the previous-bar comparison stays unchanged. Alert frequency controls eligible notifications.
  • Calculate indicator series and crossings on every bar, then apply confirmation or direction filters. Wait for sufficient history before interpreting a signal.
Previous a / bCurrent a / bCross aboveCross below
10 / 1011 / 10TrueFalse
9 / 1010 / 10FalseFalse
11 / 1010 / 10FalseFalse
10 / 109 / 10FalseTrue
11 / 1012 / 10False: already aboveFalse

Choose an EMA, MACD or RSI crossing

The choices below describe conditions, not trade recommendations. The example uses close as its source. Match indicator lengths, source and timeframe when comparing its markers with another indicator.

  • MACD signal-line and zero-line crossings answer different questions. Select MACD signal for the MACD/signal intersection; select MACD zero for a change in the sign of MACD.
  • RSI crossing above 30 is an exit from the lower band. RSI crossing below 30 is entry into it. Neither condition proves a price reversal or a higher win rate.
  • The EMA lengths 9 and 21 and MACD lengths 12, 26 and 9 are editable example settings, not optimized parameters.
ModeUp eventDown event
EMAFast EMA crosses above slow EMAFast EMA crosses below slow EMA
MACD signalMACD line crosses above its signal lineMACD line crosses below its signal line
MACD zeroMACD line crosses above zeroMACD line crosses below zero
RSI level exitsRSI crosses above the lower level, default 30RSI crosses below the upper level, default 70
RSI signalRSI crosses above its smoothing SMARSI crosses below its smoothing SMA

Why an intrabar alert can lose its chart marker

On an open bar, the latest price changes EMA, MACD and RSI values. A crossing can become true, trigger an early alert, then become false before the closing update. Reloading a chart does not reconstruct all those intrabar states. A notification already sent is not withdrawn when the condition changes.

  • alert.freq_once_per_bar_close permits a call executed on the closing update. It does not queue a crossing from earlier in the bar.
  • The script below gates both markers and conditions with barstate.isconfirmed. This waits for the chart bar to close; it does not establish a universal no-repainting guarantee for other scripts or data sources.
  • A confirmed crossing can still be a losing trading signal. Closing confirmation also delays the notification compared with an intrabar trigger.
Illustrative updateCross above?Once per barOnce per bar close
Previous bar ends with fast below slowFalseNo eventNo event
Fast rises above slow during the open barTrueAn eligible call can triggerNo event yet
Fast ends below slow at closeFalseEarlier notification remainsNo closing call, so no event

Complete Pine v6 crossover alert indicator

Paste this indicator into Pine Editor and add it to a standard candlestick chart. Select a mode and direction before creating the alert. EMA lines appear in EMA mode; Up and Down markers show confirmed events in every mode. The script reports conditions and does not place orders.

Configurable crossings with confirmed markers and two alert creation optionspinescript
//@version=6
indicator("Crossover alerts [Pineify]", overlay=true)

mode = input.string("EMA", "Mode", options=["EMA", "MACD signal", "MACD zero", "RSI level exits", "RSI signal"])
direction = input.string("Both", "Direction", options=["Both", "Up", "Down"])
fastLength = input.int(9, "Fast EMA length", minval=1)
slowLength = input.int(21, "Slow EMA length", minval=1)
macdFast = input.int(12, "MACD fast length", minval=1)
macdSlow = input.int(26, "MACD slow length", minval=1)
macdSignal = input.int(9, "MACD signal length", minval=1)
rsiLength = input.int(14, "RSI length", minval=1)
rsiSignalLength = input.int(14, "RSI SMA length", minval=1)
lowerLevel = input.float(30, "RSI lower level", minval=0, maxval=49)
upperLevel = input.float(70, "RSI upper level", minval=51, maxval=100)

fast = ta.ema(close, fastLength)
slow = ta.ema(close, slowLength)
[macd, signal, histogram] = ta.macd(close, macdFast, macdSlow, macdSignal)
rsi = ta.rsi(close, rsiLength)
rsiAverage = ta.sma(rsi, rsiSignalLength)

// Calculate each crossing on every bar before selecting a mode.
emaUp = ta.crossover(fast, slow)
emaDown = ta.crossunder(fast, slow)
macdUp = ta.crossover(macd, signal)
macdDown = ta.crossunder(macd, signal)
zeroUp = ta.crossover(macd, 0.0)
zeroDown = ta.crossunder(macd, 0.0)
rsiExitUp = ta.crossover(rsi, lowerLevel)
rsiExitDown = ta.crossunder(rsi, upperLevel)
rsiUp = ta.crossover(rsi, rsiAverage)
rsiDown = ta.crossunder(rsi, rsiAverage)

crossUp = switch mode
    "EMA" => emaUp
    "MACD signal" => macdUp
    "MACD zero" => zeroUp
    "RSI level exits" => rsiExitUp
    => rsiUp
crossDown = switch mode
    "EMA" => emaDown
    "MACD signal" => macdDown
    "MACD zero" => zeroDown
    "RSI level exits" => rsiExitDown
    => rsiDown

confirmedUp = barstate.isconfirmed and crossUp and direction != "Down"
confirmedDown = barstate.isconfirmed and crossDown and direction != "Up"
plot(mode == "EMA" ? fast : na, "Fast EMA", color.blue)
plot(mode == "EMA" ? slow : na, "Slow EMA", color.orange)
plotshape(confirmedUp, "Confirmed up", shape.triangleup, location.belowbar, color.teal, text="Up")
plotshape(confirmedDown, "Confirmed down", shape.triangledown, location.abovebar, color.red, text="Down")

// Select these named conditions for separate direction alerts.
alertcondition(confirmedUp, "Confirmed up cross", "Up cross on {{ticker}} at {{close}}")
alertcondition(confirmedDown, "Confirmed down cross", "Down cross on {{ticker}} at {{close}}")

// Or select Any alert() function call for a dynamic message.
if confirmedUp or confirmedDown
    side = confirmedUp ? "up" : "down"
    message = mode + " cross " + side + " | " + syminfo.tickerid + " | " + timeframe.period + " | close=" + str.tostring(close, format.mintick)
    alert(message, alert.freq_once_per_bar_close)

Create the alert and preserve its settings

Adding an indicator does not create a running alert. Open Create Alert after choosing the chart symbol, timeframe and script inputs. Choose one of the following methods for each intended notification; enabling overlapping alerts can produce duplicate notifications.

  • For an EMA alert, select EMA mode and set both lengths. For a MACD alert, choose signal or zero mode. For RSI, choose level exits or the RSI/SMA crossing.
  • Create the alert, then inspect its settings and alert log when a new realtime crossing occurs. Historical markers do not retroactively send notifications.
  • TradingView saves a snapshot of the script, inputs, symbol and timeframe. Delete and recreate the alert after changing these settings; editing the chart alone does not update it.
  • This example uses an indicator. alertcondition() calls in strategies do not expose usable alert conditions, even though the calls can compile.
MethodCondition selectionMessage and frequency
alertcondition()Confirmed up cross or Confirmed down crossConstant template with supported placeholders; choose Once Per Bar Close in the dialog.
alert()Any alert() function callDynamic text and closing frequency come from the script. Direction is controlled by the input.

Check the condition separately from delivery

Use these checks before connecting notifications to another system. They are a verification procedure, not a claim that a live delivery test has been performed.

  • Compare a confirmed marker with the selected indicator values on the same symbol, timeframe and source. For a touch without a cross, expect no marker.
  • After changing the mode, create a replacement alert and confirm the old alert is no longer active if it is no longer wanted.
  • If a marker appears without a notification, check whether the alert existed before that realtime event, its saved settings and its log. A historical marker alone is not proof of delivery.
  • Webhook use needs a payload that matches the receiving service and a separate receiver check. The text message here is not a broker order schema and does not guarantee delivery or execution.
Where Pineify fits

Visual Pine Script Editor

Build indicator conditions visually and inspect the generated Pine Script. Check the crossing direction and confirmation rule before creating a TradingView alert.

Also useful: Pine Script AI Coding Agent. Describe the series, thresholds and timing you need, then review the generated script and test its alert behavior in TradingView.

Build crossover conditions

Frequently asked questions

Educational programming example, not investment advice. A crossover describes an indicator condition and does not guarantee a price move, notification delivery or order execution.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.