The script
Five kinds of condition side by side: the cross event, the above/below state, a fixed level, a recent cross and a combination of them. Paste it into a new indicator in the Pine Editor, then save and add it to the chart.
//@version=6
indicator("Crossover conditions", overlay = true)
fastLen = input.int(10, "Fast SMA", minval = 1)
slowLen = input.int(30, "Slow SMA", minval = 2)
rsiLen = input.int(14, "RSI length", minval = 2)
within = input.int(5, "Recent cross = within N bars", minval = 1)
fast = ta.sma(close, fastLen)
slow = ta.sma(close, slowLen)
rsi = ta.rsi(close, rsiLen)
// 1. Events: true on the single bar of the cross
bullCross = ta.crossover(fast, slow)
bearCross = ta.crossunder(fast, slow)
anyCross = ta.cross(fast, slow)
// 2. State: true on every bar while the fast average is above
fastAbove = fast > slow
// 3. A line crossing a fixed level: RSI back above 30
rsiUp30 = ta.crossover(rsi, 30)
// 4. Recency: bars since the last bullish cross (na before the first one)
sinceBull = ta.barssince(bullCross)
recentBull = not na(sinceBull) and sinceBull <= within
// 5. Combined: RSI leaves oversold shortly after a bullish cross, while it still holds
setup = rsiUp30 and recentBull and fastAbove
plot(fast, "Fast SMA", color.teal)
plot(slow, "Slow SMA", color.orange)
bgcolor(fastAbove ? color.new(color.teal, 93) : color.new(color.red, 93), title = "State")
plotshape(bullCross, "Bullish cross", shape.triangleup, location.belowbar, color.teal, size = size.small)
plotshape(bearCross, "Bearish cross", shape.triangledown, location.abovebar, color.red, size = size.small)
plotshape(setup, "Setup", shape.diamond, location.bottom, color.blue, size = size.small)
plot(anyCross ? 1 : 0, "Any cross", display = display.data_window)
alertcondition(setup, "Setup", "RSI left oversold after a recent bullish SMA cross on {{ticker}}")How it works
- What a crossover is.
ta.crossover(fast, slow)equalsfast > slow and fast[1] <= slow[1]: the fast line is above now and wasn't on the previous bar.ta.crossunder()isfast < slow and fast[1] >= slow[1], andta.cross()is either one. - Event or state.
bullCrossis true on one bar;fastAboveis true for as long as the trend lasts. The background shows the state and the triangles show the events, so the difference is visible on the chart. - Crossing a number. The second argument can be a constant:
ta.crossover(rsi, 30)fires when RSI moves from 30 or below to above 30. ta.barssince(bullCross)returns 0 on the cross bar, 1 on the next, andnauntil the first cross ever. Thenot na(...)check keepsrecentBullfalse until there is a cross to count from.- Every condition has its own line. All the
ta.*calls run at the top level on every bar, and the combinations only use their results. That keeps v6's lazyandfrom skipping them (see the pitfalls).
Pitfalls
- Entering on a state. In a strategy,
if fast > slowfollowed bystrategy.entry()fires on every bar of the trend: after a stop-out the strategy re-enters on the next bar.ta.crossover()waits for a new cross. The EMA crossover strategy puts this into a complete strategy. - A cross inside and/or. In v6,
andandorstop at the first operand that decides the result, so inclose > open and ta.crossover(fast, slow)the crossover isn't evaluated on every bar and its result can be wrong. TradingView warns should be called on each calculation. Assign the cross to a variable first, as the script does. - Touching from one side. The previous bar only needs
a <= b. If the lines are below, equal for one bar, then below again,ta.crossunder()fires although the fast line was never above. On flat markets, require a gap (fast > slow * 1.001) or a cross that holds for a few bars. - Crosses on the live bar. During the live bar a cross can exist at one tick and be gone at the close. Add
barstate.isconfirmedfor markers and alerts that must not vanish (barstate.isconfirmed example).
Variations
A cross that has held for N bars, and a price level crossed by the close or only by the wick. ta.barssince(not fastAbove) == holdBars is true once, on the bar where the fast line has been above for exactly holdBars bars.
//@version=6
indicator("Cross variations", overlay = true)
holdBars = input.int(3, "Bars the cross must hold", minval = 1)
level = input.float(0.0, "Price level (0 = previous 20-bar high)", minval = 0)
fast = ta.ema(close, 10)
slow = ta.ema(close, 30)
hi20 = ta.highest(high, 20)[1]
fastAbove = fast > slow
// Fires once, on the holdBars-th bar in a row with the fast EMA above
heldBull = ta.barssince(not fastAbove) == holdBars
float lvl = level > 0 ? level : hi20
closeAbove = ta.crossover(close, lvl)
// The wick reached the level, whether or not the bar closed above it
wickTouch = high >= lvl and high[1] < lvl[1]
plot(lvl, "Level", color.gray, style = plot.style_stepline)
plotshape(heldBull, "Cross held", shape.labelup, location.belowbar, color.teal, text = "held", textcolor = color.white, size = size.tiny)
plotshape(closeAbove, "Close above level", shape.triangleup, location.belowbar, color.blue, size = size.small)
plotshape(wickTouch and not closeAbove, "Wick only", shape.xcross, location.abovebar, color.gray, size = size.tiny)
Questions
What is the difference between crossover and crossunder in Pine Script?
ta.crossover(a, b) is true when a moves from at or below b to above it; ta.crossunder(a, b) when a moves from at or above b to below it. Both are true on that one bar only. ta.cross(a, b) is true for either direction.
How do I check if a crossover happened in the last N bars?
Count the bars since it with ta.barssince(): recent = not na(ta.barssince(cross)) and ta.barssince(cross) <= 5. Better, store ta.barssince(cross) in a variable on its own line and test the variable.
How do I detect price crossing a horizontal line?
Use the level as the second argument: ta.crossover(close, level), where level is a number, an input or a series. For a line drawn with line.new(), read its price on the current bar with myLine.get_price(bar_index).
Related pages
Every script on this page was checked with Edgoro Pine Check (Pine Script v6 rules). Pine Check is a static checker, not TradingView's compiler: TradingView compiles the script when you save it in the Pine Editor. The scripts are examples to learn from, not trading systems. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.