A signal generated when one indicator line crosses another — simple to code, easy to overuse. Raw crossovers whipsaw badly in choppy markets. They need context to be useful.
A crossover is the exact bar where one value crosses above or below another. That sounds obvious, but the key word is exact. A crossover is a one-bar event — it is not a condition that stays true. `ta.crossover(fast, slow)` returns `true` on precisely one bar: the bar where `fast` moved from being below `slow` to being above it. On every subsequent bar, even if `fast` is still above `slow`, it returns `false`.
This is the most common conceptual trap beginners fall into when writing their first strategy. EMAs, MACD lines, RSI levels — any two series can produce a crossover signal, and they all follow the same one-bar rule.
The critical distinction is between `ta.crossover(fast, slow)` and `fast > slow`. They look similar but mean completely different things:
If you accidentally use `fast > slow` inside a strategy entry condition where you meant `ta.crossover`, you will re-enter the trade on every bar while fast leads. With pyramiding off, Pine Script prevents duplicate entries in the same direction — but the intent is wrong and the behaviour is fragile. With pyramiding on, you would be compounding into a position on every bar.
Understanding this distinction is the difference between a working strategy and one that produces results that make no sense on the chart.
//@version=6
indicator("Crossover Demo", overlay=true)
fast = ta.ema(close, 9)
slow = ta.ema(close, 21)
// ta.crossover fires on ONE bar — when fast moves from below to above slow
bullSignal = ta.crossover(fast, slow)
bearSignal = ta.crossunder(fast, slow)
// fast > slow is true on EVERY bar where fast leads — very different meaning
inUptrend = fast > slow
// Signal markers — appear only on the crossover bar
plotshape(bullSignal, style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, title="Bull Cross")
plotshape(bearSignal, style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, title="Bear Cross")
// Line colour reflects ongoing trend state (not just the signal bar)
plot(fast, color=inUptrend ? color.green : color.red, linewidth=2, title="Fast EMA")
plot(slow, color=color.gray, linewidth=1, title="Slow EMA")
Key takeaway: `ta.crossover()` fires once. If you write `if ta.crossover(fast, slow)` inside a strategy, the entry triggers on exactly one bar. If you write `if fast > slow` instead, you attempt to re-enter on every bar fast leads. That is not a style choice — it changes the strategy entirely.