Tutorial

MACD Strategy in Pine Script: A Step-by-Step Guide

What the MACD line, signal line, and histogram each tell you, why the indicator is so popular, and how to build a MACD indicator and strategy in Pine Script v6.

11 min read

The MACD, short for Moving Average Convergence Divergence, is one of the most widely used momentum indicators on any charting platform, and it has a reputation for looking more complicated than it is. It is really just moving averages arranged to show how momentum is shifting, presented as three related pieces: a MACD line, a signal line, and a histogram. Once you see how those three are built from each other, the whole indicator clicks into place. This guide explains what each part measures, why traders rely on the MACD, and how to build it from scratch in Pine Script v6 on TradingView, as both an indicator and a backtestable strategy.

This is a coding tutorial, not trading advice. The MACD is a way to express a question about momentum in code, and building it yourself is the best way to understand both its strengths and the situations where it lets you down.

What the MACD is made of

The MACD starts with two exponential moving averages of the closing price, a fast one over 12 bars and a slow one over 26 bars by default. The MACD line is simply the fast EMA minus the slow EMA. When the fast average is above the slow one, the MACD line is positive, meaning recent prices are pulling up relative to the longer trend. When it is below, the line is negative. The distance from zero tells you how far apart the two averages have drifted, which is a rough measure of momentum strength.

The signal line is a 9-bar EMA of the MACD line itself, a smoothed version that lags slightly behind. The histogram is the difference between the MACD line and the signal line, drawn as bars. When the MACD line is above the signal line, the histogram is positive and growing; when it dips below, the histogram turns negative. That histogram is the part traders watch most closely, because it turns before the lines actually cross and gives an earlier read on momentum shifting.

MACD LineSignal LineHistogram
What it isFast EMA minus slow EMAEMA of the MACD lineMACD line minus signal line
ReadsMomentum directionSmoothed momentumMomentum acceleration
Common useZero-line crossesSignal-line crossesEarly turn detection
Pine sourceta.macd() first valueta.macd() second valueta.macd() third value

Why traders use it

The appeal of the MACD is that it packs three related momentum readings into one compact pane. A trader can watch the MACD line cross zero to judge whether momentum has flipped from negative to positive, watch the MACD line cross the signal line for a faster entry cue, and watch the histogram shrink toward zero as an early warning that a move is losing steam, all at once. It also lends itself to divergence analysis in the same way the RSI does: if price makes a new high but the MACD makes a lower high, the momentum behind the move is fading.

The honest caveat is that the MACD is built entirely from lagging moving averages, so every signal arrives after the move has already begun. In a choppy, sideways market the line and signal cross back and forth repeatedly, producing a stream of false signals. Like most momentum tools, it shines in trends and struggles in ranges.

Building the indicator

Pine Script gives you the MACD as a single built-in function that returns all three components at once, so you unpack them with a tuple. Here is the complete v6 code.

pine
//@version=6
indicator("MACD", overlay = false)

fastLen   = input.int(12, "Fast Length")
slowLen   = input.int(26, "Slow Length")
signalLen = input.int(9, "Signal Length")

[macdLine, signalLine, histLine] = ta.macd(close, fastLen, slowLen, signalLen)

plot(macdLine, "MACD", color = color.blue, linewidth = 2)
plot(signalLine, "Signal", color = color.orange, linewidth = 2)
plot(histLine, "Histogram", color = color.gray, style = plot.style_columns)
hline(0, "Zero", color = color.gray, linestyle = hline.style_dotted)

The interesting line is [macdLine, signalLine, histLine] = ta.macd(...). The ta.macd() function returns three series at once, and Pine Script's tuple syntax lets you assign all three to named variables in one statement. From there it is presentation: plot() draws the two lines, and passing style = plot.style_columns draws the histogram as bars instead of a line. Setting overlay = false keeps everything in its own pane, since the MACD values are small numbers unrelated to the price scale.

Turning it into a backtestable strategy

The most common MACD strategy enters when the MACD line crosses above the signal line and exits when it crosses back below. Here is that logic as a v6 strategy.

pine
//@version=6
strategy("MACD Strategy", overlay = true, margin_long = 100, margin_short = 100)

fastLen   = input.int(12, "Fast Length")
slowLen   = input.int(26, "Slow Length")
signalLen = input.int(9, "Signal Length")

[macdLine, signalLine, _] = ta.macd(close, fastLen, slowLen, signalLen)

if ta.crossover(macdLine, signalLine)
    strategy.entry("Long", strategy.long)

if ta.crossunder(macdLine, signalLine)
    strategy.close("Long")

Notice the third element of the tuple is assigned to _, an underscore, which is Pine's convention for a value you are unpacking but do not intend to use, in this case the histogram. The ta.crossover() and ta.crossunder() built-ins fire on the exact bar where the MACD line crosses the signal line, which is the moment momentum shifts. The order calls sit inside if blocks, required in v6 where the old when parameter has been removed, something we cover alongside the other changes in our v5 to v6 migration guide.

Zero-line crosses versus signal-line crosses

The signal-line cross above is the faster, more frequent entry. A more conservative variant waits for the MACD line itself to cross above zero, which only happens when the fast EMA moves above the slow EMA, a stronger confirmation that the trend has actually turned. The trade-off is the usual one: the zero-line cross produces fewer signals and enters later, sacrificing some of the early move in exchange for filtering out more of the noise. You can combine both, requiring the MACD line to be above zero before acting on a signal-line cross, which is a simple change.

pine
//@version=6
strategy("MACD With Zero-Line Filter", overlay = true, margin_long = 100, margin_short = 100)

[macdLine, signalLine, _] = ta.macd(close, 12, 26, 9)

crossUp   = ta.crossover(macdLine, signalLine)
aboveZero = macdLine > 0

if crossUp and aboveZero
    strategy.entry("Long", strategy.long)

if ta.crossunder(macdLine, signalLine)
    strategy.close("Long")

The crossUp variable is assigned on its own line before it is used in the if condition. That is deliberate. In v6 the and operator evaluates lazily, so the right side can be skipped when the left already decides the result, and functions like ta.crossover() must run every bar to track their state correctly. Hoisting the call into its own variable guarantees it executes on every bar. It is a small habit that prevents a subtle class of bugs.

The honest pitfalls

The MACD's weakness is the flip side of its construction. Because it is built from moving averages, it lags, and because it lags, it whipsaws in sideways markets, producing crossovers that reverse almost immediately. Every one of those crossovers would trigger a trade in the strategy version, which is why the trade list in the Strategy Tester matters more than any single headline number. A handful of trades tells you almost nothing; judge a MACD strategy across dozens of trades over a few years and across more than one market.

The other common mistake is over-tuning the three lengths. The 12, 26, and 9 defaults are conventional, not sacred, but nudging them until the backtest looks perfect on one chart usually produces numbers that fall apart on the next symbol. Treat them as settings to reason about rather than dials to optimize blindly. If you want to see exactly how the MACD line, signal line, and histogram are computed from a price series, our MACD calculator shows the math with the Pine Script equivalent beside it.

Building it faster

Coding this by hand is the best way to internalize how the three components relate. If you already know the exact rules you want, though, describing them in plain English is quicker than wiring the script together line by line. A tool like PineScripter generates the v6 code and, when it does not compile, reads TradingView's error and fixes it automatically rather than making you paste the error back and forth. You can compare that workflow against other options in our roundup of the best AI Pine Script generators, and if the MACD is your first indicator, our moving average crossover guide covers the moving-average building blocks the MACD is made from.

The takeaway

The MACD is two EMAs subtracted to make a line, a smoothed version of that line, and the gap between them drawn as a histogram, all delivered by one function, ta.macd(). It is a compact, useful momentum gauge that reads best in trends and worst in ranges. Build it, decide whether you want the faster signal-line cross or the more conservative zero-line filter, and test it across a real sample rather than a lucky handful of crosses.


Disclaimer: PineScripter is a coding tool for Pine Script development. It does not provide financial advice and does not guarantee trading profits. Always backtest strategies thoroughly and understand the risks before live trading.