How to Code a Combined RSI + MACD Indicator in Pine Script v6
By HorizonAI Team · 12 min read · Intermediate
Combined RSI and MACD Indicator for Pine Script v6
An RSI reading below 30 can look like a buy, while MACD still shows downside momentum. If you view those signals in separate panes, it’s easy to cherry-pick the one that matches the trade you already want. A combined indicator fixes that by defining exactly when the two tools agree and when they don’t.
Short answer: combine RSI and MACD by keeping RSI on its natural 0-100 scale, normalizing the MACD histogram into that same pane, and triggering a signal only when an RSI recovery from an extreme and MACD confirmation occur within a small bar window. The Pine Script below uses a 3-bar window, a -2 to +2 confluence score, confirmed alerts, and fresh signal markers.
The practical rule is deliberately asymmetric from a simple “RSI low plus MACD bullish” checklist. A long requires RSI to cross back above 35, which shows recovery from weakness, and MACD histogram to be above zero within three bars of that recovery. A short mirrors it: RSI crosses below 65 and the histogram is below zero. That creates a repeatable decision rule rather than two indicators competing for attention.
What you’re building and why the scaling choice matters
This is one oscillator pane, not RSI and MACD pasted on top of each other. RSI remains the primary line because its fixed 0-100 range makes 30, 50, and 70 meaningful. The MACD histogram is normalized around 50, then plotted as a histogram so positive momentum appears above the midpoint and negative momentum below it.
MACD values are expressed in price units, so their raw size changes dramatically between symbols and timeframes. Plotting raw MACD beside RSI makes one of them unreadable. There are two reasonable approaches:
- Normalize MACD into the RSI pane, preserving a visual view of momentum strength.
- Keep RSI as the only line and show MACD state through background color and markers.
This build uses the first option and still adds color and markers for fast scanning. If you need the indicator theory before coding, compare the jobs of RSI and MACD: RSI identifies relative stretch and recovery, while MACD helps confirm the momentum direction.
The normalization here uses a rolling mean and standard deviation of the histogram. A histogram at its 20-bar average maps to 50. One standard deviation above maps near 70, and one standard deviation below maps near 30, subject to a 0-100 clamp. This is a display transform, not a second trading rule. The actual confirmation still uses the unmodified MACD histogram above or below zero.
Set the confluence rule before writing code
A combined indicator needs a rule that settles disagreement. These are the defaults used throughout this guide:
| Component | Bullish condition | Bearish condition | Default |
|---|---|---|---|
| RSI event | Crosses back above the lower threshold | Crosses back below the upper threshold | 35 / 65 |
| MACD confirmation | Histogram above zero | Histogram below zero | 12 / 26 / 9 |
| Timing | Both conditions align within the allowed window | Both conditions align within the allowed window | 3 bars |
| Fresh signal | First bar of valid confluence | First bar of valid confluence | Enabled |
Why 35 and 65 rather than the textbook 30 and 70? The slightly earlier recovery thresholds often make the event less rare while still requiring RSI to leave a stretched area. They are inputs, not universal constants. On a volatile 5-minute index chart, 30/70 may filter more noise. On a 4-hour chart, 35/65 can produce more usable swings.
The three-bar confirmation window matters more than most traders expect. Requiring both crossings on the exact same candle throws away setups where RSI reacts first and MACD confirms one or two bars later. Making the window too wide turns the confirmation into stale information. Start with three bars, then test two through five rather than guessing.
Full Pine Script v6 combined indicator
Paste this complete script into a new TradingView Pine Editor indicator. TradingView documents ta.rsi(), ta.macd(), plots, and input functions in its Pine Script v6 reference.
//@version=6
indicator("Combined RSI + MACD Confluence", shorttitle="RSI MACD Combo", overlay=false)
// RSI settings
rsiLength = input.int(14, "RSI Length", minval=2)
rsiLongLevel = input.float(35.0, "RSI Long Recovery Level", minval=1.0, maxval=49.0, step=0.5)
rsiShortLevel = input.float(65.0, "RSI Short Rejection Level", minval=51.0, maxval=99.0, step=0.5)
// MACD settings
macdFastLength = input.int(12, "MACD Fast Length", minval=1)
macdSlowLength = input.int(26, "MACD Slow Length", minval=2)
macdSignalLength = input.int(9, "MACD Signal Length", minval=1)
normalizationLength = input.int(50, "MACD Normalization Length", minval=10)
// Signal settings
confirmationBars = input.int(3, "RSI-to-MACD Confirmation Window", minval=0, maxval=10)
showBackground = input.bool(true, "Show Confluence Background")
showMarkers = input.bool(true, "Show Fresh Signal Markers")
// Core values
rsiValue = ta.rsi(close, rsiLength)
[macdLine, signalLine, histogram] = ta.macd(close, macdFastLength, macdSlowLength, macdSignalLength)
// Normalize MACD histogram to RSI's 0-100 display range.
histogramMean = ta.sma(histogram, normalizationLength)
histogramDeviation = ta.stdev(histogram, normalizationLength)
normalizedHistogramRaw = histogramDeviation != 0.0 ? 50.0 + 20.0 * (histogram - histogramMean) / histogramDeviation : 50.0
normalizedHistogram = math.max(0.0, math.min(100.0, normalizedHistogramRaw))
// A -2 to +2 state score: one point from RSI direction, one from MACD direction.
rsiScore = rsiValue > 50.0 ? 1 : rsiValue < 50.0 ? -1 : 0
macdScore = macdLine > signalLine ? 1 : macdLine < signalLine ? -1 : 0
confluenceScore = rsiScore + macdScore
// RSI must recover from an extreme. MACD may confirm within the chosen window.
rsiBullishRecovery = ta.crossover(rsiValue, rsiLongLevel)
rsiBearishRejection = ta.crossunder(rsiValue, rsiShortLevel)
barsSinceBullishRecovery = nz(ta.barssince(rsiBullishRecovery), 10000)
barsSinceBearishRejection = nz(ta.barssince(rsiBearishRejection), 10000)
macdBullish = histogram > 0.0
macdBearish = histogram < 0.0
bullishConfluence = barsSinceBullishRecovery <= confirmationBars and macdBullish
bearishConfluence = barsSinceBearishRejection <= confirmationBars and macdBearish
// Mark only the first valid bar, not every bar the condition remains true.
freshBullishSignal = bullishConfluence and not bullishConfluence[1]
freshBearishSignal = bearishConfluence and not bearishConfluence[1]
// Visuals
hline(70.0, "Overbought", color=color.new(color.red, 55), linestyle=hline.style_dashed)
hline(50.0, "Midline", color=color.new(color.gray, 55))
hline(30.0, "Oversold", color=color.new(color.lime, 55), linestyle=hline.style_dashed)
histogramColor = normalizedHistogram >= 50.0 ? color.new(color.teal, 20) : color.new(color.orange, 20)
plot(normalizedHistogram, "Normalized MACD Histogram", style=plot.style_histogram, color=histogramColor, linewidth=2)
plot(rsiValue, "RSI", color=color.white, linewidth=2)
plot(confluenceScore * 10.0 + 50.0, "Confluence Score", color=color.new(color.blue, 0), linewidth=1)
backgroundColor = bullishConfluence ? color.new(color.green, 86) : bearishConfluence ? color.new(color.red, 86) : na
bgcolor(showBackground ? backgroundColor : na, title="Confluence Background")
plotshape(showMarkers and freshBullishSignal, title="Bullish Confluence", style=shape.triangleup, location=location.bottom, color=color.lime, size=size.tiny, text="BUY")
plotshape(showMarkers and freshBearishSignal, title="Bearish Confluence", style=shape.triangledown, location=location.top, color=color.red, size=size.tiny, text="SELL")
// Alert conditions trigger only after the bar closes.
bullishConfirmed = bullishConfluence and barstate.isconfirmed
bearishConfirmed = bearishConfluence and barstate.isconfirmed
alertcondition(bullishConfirmed, title="Bullish RSI + MACD Confluence", message="Bullish RSI + MACD confluence confirmed on {{ticker}} at {{close}}.")
alertcondition(bearishConfirmed, title="Bearish RSI + MACD Confluence", message="Bearish RSI + MACD confluence confirmed on {{ticker}} at {{close}}.")
// Choose "Any alert() function call" when creating an alert to receive these dynamic messages.
if bullishConfirmed
alert("Bullish RSI + MACD confluence on " + syminfo.ticker + " at " + str.tostring(close, format.mintick), alert.freq_once_per_bar_close)
if bearishConfirmed
alert("Bearish RSI + MACD confluence on " + syminfo.ticker + " at " + str.tostring(close, format.mintick), alert.freq_once_per_bar_close)
The white line is RSI. Teal and orange columns are the normalized MACD histogram. The thin blue line is the score transformed from -2 through +2 to 30 through 70, so it stays meaningful in the same pane: 70 means both directional states are bullish, 30 means both are bearish, and 50 means mixed or neutral.
Do not read the normalized column height as an absolute MACD value. It only tells you how unusual the current histogram is versus its recent history. The zero-line test in the signal rule stays anchored to real MACD data.
Read a long and short signal correctly
A bullish confluence begins after RSI crosses above 35, then remains eligible for three bars. If MACD histogram is above zero during that window, the background turns green and the script prints one BUY marker. The marker won’t repeat unless the condition first resets.
A bearish confluence works in reverse. RSI must cross down through 65 and MACD histogram must be below zero within the window. This avoids labeling every merely high RSI reading as a short signal.
The score can add context, but it shouldn’t override the event rule. For example, a +2 score says RSI is above 50 and MACD line is above its signal line. That can occur long after the recovery event, when the initial entry may already be old. Use the marker for fresh setup timing and the score for state awareness.
Configure TradingView alerts without intrabar noise
Create an alert from the indicator, then choose either the named bullish/bearish condition or Any alert() function call for the dynamic messages. Both paths are gated by barstate.isconfirmed, so a live candle has to close before an alert can fire. TradingView’s alerts documentation explains the difference between alertcondition() selections and alerts created by alert() calls.
That close-only gate is important. A MACD histogram can move above zero during a forming candle and finish below it. If you remove confirmation, the chart can look clean in hindsight while realtime alerts arrive and disappear intrabar. For a deeper treatment of that mismatch, see why Pine Script scripts repaint.
Turn the indicator rule into a testable strategy
An indicator tells you when your conditions align. A strategy needs entry, exit, and sizing rules. The compact example below uses the same RSI and MACD timing rule, enters on confirmed confluence, then sets an ATR-based stop and a 2R target. It is intentionally separate from the indicator so the visual tool stays readable.
//@version=6
strategy("RSI + MACD Confluence Strategy", overlay=true, pyramiding=0, process_orders_on_close=true)
rsiLength = input.int(14, "RSI Length", minval=2)
rsiLongLevel = input.float(35.0, "RSI Long Recovery Level", minval=1.0, maxval=49.0)
rsiShortLevel = input.float(65.0, "RSI Short Rejection Level", minval=51.0, maxval=99.0)
macdFastLength = input.int(12, "MACD Fast Length", minval=1)
macdSlowLength = input.int(26, "MACD Slow Length", minval=2)
macdSignalLength = input.int(9, "MACD Signal Length", minval=1)
confirmationBars = input.int(3, "Confirmation Window", minval=0, maxval=10)
atrLength = input.int(14, "ATR Length", minval=1)
atrMultiplier = input.float(1.5, "ATR Stop Multiplier", minval=0.1, step=0.1)
riskReward = input.float(2.0, "Reward-to-Risk Multiple", minval=0.5, step=0.25)
rsiValue = ta.rsi(close, rsiLength)
[macdLine, signalLine, histogram] = ta.macd(close, macdFastLength, macdSlowLength, macdSignalLength)
atrValue = ta.atr(atrLength)
bullishRecovery = ta.crossover(rsiValue, rsiLongLevel)
bearishRejection = ta.crossunder(rsiValue, rsiShortLevel)
bullishRecent = nz(ta.barssince(bullishRecovery), 10000) <= confirmationBars
bearishRecent = nz(ta.barssince(bearishRejection), 10000) <= confirmationBars
longSignal = bullishRecent and histogram > 0.0 and barstate.isconfirmed
shortSignal = bearishRecent and histogram < 0.0 and barstate.isconfirmed
if longSignal and strategy.position_size <= 0
strategy.entry("Long", strategy.long)
if shortSignal and strategy.position_size >= 0
strategy.entry("Short", strategy.short)
longStop = strategy.position_avg_price - atrValue * atrMultiplier
longTarget = strategy.position_avg_price + atrValue * atrMultiplier * riskReward
shortStop = strategy.position_avg_price + atrValue * atrMultiplier
shortTarget = strategy.position_avg_price - atrValue * atrMultiplier * riskReward
if strategy.position_size > 0
strategy.exit("Long Exit", from_entry="Long", stop=longStop, limit=longTarget)
if strategy.position_size < 0
strategy.exit("Short Exit", from_entry="Short", stop=shortStop, limit=shortTarget)
The point of that strategy is not to declare the settings optimal. It gives you a complete object to test: a defined trigger, a 1.5 ATR initial stop, and a 2.0 reward-to-risk target. Use TradingView’s Strategy Tester to compare symbols and timeframes, and audit the result with a disciplined strategy backtesting process. TradingView’s strategy concepts cover how orders, fills, and strategy calculations work.
Tune parameters by market and timeframe
Start with the standard MACD 12/26/9 and RSI 14. Change one family of settings at a time, then compare enough trades to see whether you improved the rule or merely fitted a small sample.
For liquid intraday futures, forex, or major crypto pairs on 5- to 15-minute charts, test RSI 30/70 and a two- or three-bar window first. The stricter extreme filter can help when price crosses the RSI midpoint repeatedly. For 1-hour to 4-hour swing setups, compare 35/65 against 30/70 and allow three to five bars for MACD confirmation.
MACD needs enough bars for its slow average to mean something. A 12/26/9 MACD on a one-minute chart can flip direction frequently, especially around session opens and thin periods. If you trade from a very low execution timeframe, use a higher-timeframe bias or test slower MACD values such as 18/39/9 before adding more filters.
Avoid adding an indicator just because the chart feels too permissive. A third filter should perform a distinct job, such as defining trend regime with a 200 EMA or sizing the stop with ATR. It shouldn’t simply restate momentum in another formula. For indicator ideas that suit a systematic test, browse these Pine Script strategy patterns.
Common RSI and MACD combination mistakes
❌ Mistake: Counting MACD twice. Treating “MACD line above signal” and “histogram above zero” as two separate confirmations can overweight one calculation.
✅ Do this: Use one MACD condition for the entry rule, such as histogram above or below zero. Keep the line-versus-signal relationship as a score or visual context, not a second mandatory filter.
❌ Mistake: Demanding both indicator events on exactly one bar. That looks precise but can reject a valid RSI recovery that MACD confirms on the next candle.
✅ Do this: Set a small, testable window. Start at three bars, test two through five, and keep the same window across your comparison set.
❌ Mistake: Using a 12/26/9 MACD unchanged on a one-minute chart and expecting swing-quality confirmation. The indicator can react to microstructure noise rather than the move you intend to trade.
✅ Do this: Trade the signal on 5 minutes or higher, or test a slower MACD and a higher-timeframe trend filter. Record the change before assuming it helped.
❌ Mistake: Triggering alerts on the live bar. A signal can appear while the candle is open, then vanish at its close.
✅ Do this: Keep barstate.isconfirmed in the signal condition and create close-confirmed alerts. The one-bar delay is a defined trade-off for stable signals.
Pro tips for making confluence more useful
Separate setup from permission. The RSI crossing above 35 is the setup event. MACD histogram above zero is permission to act within the next three bars. That vocabulary makes it easier to debug why a marker did or didn’t print.
Use the 50 level as a regime check. A bullish recovery that quickly carries RSI above 50 while MACD remains positive is usually stronger context than a recovery that stalls under 50. Don’t convert that observation into another hard filter until you’ve tested it.
Save parameter sets by market. Keep a named TradingView layout for each instrument group, such as BTCUSD 1-hour with 14 RSI, 35/65 thresholds, and 12/26/9 MACD. A single “best setting” across unrelated markets is usually a maintenance problem disguised as simplicity.
Generating this without writing the code yourself
If you want the same artifact but need a different confirmation rule, describe it precisely in HorizonAI. It generates Pine Script v6, MQL5, and NinjaScript from chat, and can compile-check and help edit the script. HorizonAI writes the code; you still add it to TradingView and decide how to use it.
Create a Pine Script v6 indicator in one separate pane. Plot RSI 14 as a white line with 30, 50, and 70 levels. Calculate MACD 12, 26, 9, normalize its histogram with a 50-bar rolling mean and standard deviation into a 0-100 range, and plot it as teal/orange columns. Trigger a bullish marker only when RSI crosses above 35 and MACD histogram is above zero within 3 bars. Mirror it for shorts with RSI below 65 and histogram below zero. Add a -2 to +2 confluence score, close-confirmed background colors, and alertcondition plus dynamic alert messages.
Convert the RSI and MACD confluence rule into a Pine Script v6 strategy with
process_orders_on_close=true, a 14-period ATR stop at 1.5 ATR, and a 2R profit target. Keep entries close-confirmed and prevent pyramiding.
You’ll get working, compile-checked code you can adjust in chat, including inputs if you want to test alternate thresholds or windows. Try it free →
FAQs
What are the best RSI and MACD settings for this combined indicator?
Start with RSI 14 and MACD 12/26/9, then use RSI 35/65 and a three-bar confirmation window. Compare 30/70 and 35/65 on the same symbol and timeframe rather than changing every setting at once.
Which timeframe works best for RSI and MACD confluence?
Five-minute through four-hour charts are sensible starting points because the MACD slow average has enough price movement to process. The appropriate timeframe depends on your holding period, so test the full entry and exit rule rather than the indicator alone.
Does adding a third indicator improve RSI and MACD signals?
Only if it adds information that RSI and MACD don’t already measure. ATR for risk distance or a long-term moving average for regime can be useful candidates, while another momentum oscillator often duplicates the same input.
Why does the script wait for the candle to close before alerting?
Close confirmation prevents alerts from firing on a temporary intrabar condition that disappears before the bar finishes. It makes the signal stable, at the cost of waiting for the completed candle.
Final thoughts
A combined RSI and MACD indicator earns its place when it converts visual agreement into a rule: RSI recovers from an extreme, MACD confirms direction, and the two occur close enough together to matter. Keep the oscillator readable by normalizing MACD only for display, while using the real histogram for the trade condition.
The best next test is simple: hold MACD at 12/26/9, compare a two-, three-, and four-bar window on one market and timeframe, and log every other setting unchanged. That tells you whether timing tolerance is helping the rule or just generating more signals.
Related articles
- RSI vs MACD — Which Indicator Is Better for Trading? — Understand the distinct role each oscillator plays before combining them.
- Pine Script Repainting: Why It Happens and How to Fix It — Build close-confirmed signals that behave consistently in realtime.
- 7 Best Pine Script Strategies for TradingView — Find structured strategy ideas to test alongside this confluence rule.
- How to Backtest a Trading Strategy — Turn entries, exits, and sizing into a repeatable test.
- ATR Indicator Explained — Use volatility-based distances for stops and targets.
- Build a WaveTrend Oscillator in Pine Script v6 — Code another momentum oscillator with clear visual states.
- Build a Squeeze Momentum Indicator in Pine Script v6 — Add a volatility-and-momentum study to your Pine toolkit.
- Pine Script Tutorial for Beginners — Learn the core Pine structure behind indicators and strategies.
Questions about combined RSI and MACD indicators? Join our Discord to discuss with other traders!
