TradingView indicator guides

MACD Leader Pine Script: Residual EMAs and Signal Boundaries

MACD Leader adds a smoothed price-minus-EMA residual to each side of a fast/slow comparison. The name describes an indicator variant, not access to future prices. The script here plots only the resulting leader line; its returned standard MACD and SMA signal values are not plotted.

Inspect the Pine Script

Origin and the implementation being examined

LazyBear’s published implementation credits Giorgos E. Siligardos with the MACD Leader concept. The author’s script includes options for displaying standard MACD and its signal. This Pineify example keeps the same residual-EMA calculation but has fixed 12/26/9 lengths and only one plotted output. Do not assume controls from the author’s script are present here.

The second EMA smooths a residual

For each length N, calculate E = EMA(source,N), R = EMA(source − E,N), and A = E + R. Leader = Afast − Aslow. The function also returns ordinary MACD M = Efast − Eslow and S = SMA(M,9). S is an average of ordinary MACD, not an average of Leader. Changing its length does not alter Leader or M.

With compatible initialization and the same valid source samples, EMA linearity gives R = EMA(source) − EMA(EMA(source)). Therefore A = 2×EMA(source) − EMA(EMA(source)), the DEMA combination. This is not simply the twice-smoothed EMA by itself: the subtraction changes the response and permits overshoot. Different seeds or missing-value handling must be checked before treating two implementations as numerically identical.

Leader uses price units. Positive Leader means the adjusted fast leg exceeds the adjusted slow leg. It does not imply ordinary MACD is positive, that price just rose, or that a future crossover will occur within a fixed number of bars.

A flat-price counterexample to guaranteed confirmation

This synthetic test starts with a long constant price of 100, both EMA legs at 100 and both residual averages at zero. Price then moves to 110 and remains there. Each bar updates E with coefficient 2/(N+1), then updates R from the current residual source − E. These are formula outputs, not observed trades.

Bars after stepStandard MACDLeader
10.7977211.413625
52.4682603.284175
102.7504822.213962
201.791496−0.297807
300.927172−0.973824

Both lines become positive on the very first step bar: Leader has no earlier positive zero-cross event in this example. Later Leader becomes negative while price is still 110 and standard MACD stays positive. Standard MACD approaches zero from above; it does not have to cross below zero to confirm that negative Leader. A larger first response is not a guaranteed time lead or a predicted price decline.

Define the comparison event before measuring a lead

A Leader zero cross, a standard MACD zero cross, a standard MACD–signal cross, and a local maximum are distinct events. Comparing one with another without stating the rule produces an ambiguous lead-time claim. The returned signal here is an SMA; the conventional EMA-based signal in ta.macd is a different reference.

For a timing study, define event direction, confirmed-bar timing, matching window and what happens to unmatched events before examining results. Count signals with no later match instead of dropping them. A rule that waits for a later confirmation cannot enter at the earlier Leader event in a historical test.

A divergence study additionally needs explicit pivot widths and pairing rules. Pivots requiring future right-hand bars become available only after those bars have closed. This example implements neither event matching nor a divergence detector.

What the copied script displays

The fixed call uses close, fast length 12, slow length 26 and signal length 9 on the chart timeframe. The pane contains a gray zero line and a red Leader plot. Ordinary MACD and the SMA signal are calculated but have no plot calls: they cannot be enabled with a visibility checkbox in this version. Add explicit plots to compare them.

There are no input declarations, alert conditions, orders or bar-close gate. The bundled time helpers are unused by this indicator; they do not apply a session filter or change the chart timeframe. Current-bar values may change as close changes. A confirmed-bar adaptation should calculate its series consistently and apply confirmation to event handling, rather than imply the entire live plot is fixed.

An adaptation worth checking

First add ordinary MACD and its SMA signal as separately named plots, then verify the synthetic step table against the implementation. If replacing the SMA signal with an EMA, label that as a different comparison. For a trading experiment, separately specify entries, exits, fills and costs; a visual lead alone supplies none of those rules.

Standard MACD, this residual-adjusted Leader and the high/low-channel-based Impulse MACD use different calculations. Their names do not establish a universal speed ranking, a best timeframe or a measured win rate.

Pine Script v6: implementation and limits

The complete existing Pine Script retains the residual-EMA function and fixed parameters. Its additional common time helpers are not called by the indicator. Only Leader is plotted; the returned MACD and signal are not hidden optional plots.

Pine Script v6
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify

//======================================================================//
//                    ____  _            _  __                          //
//                   |  _ \(_)_ __   ___(_)/ _|_   _                    //
//                   | |_) | | '_ \ / _ \ | |_| | | |                   //
//                   |  __/| | | | |  __/ |  _| |_| |                   //
//                   |_|   |_|_| |_|\___|_|_|  \__, |                   //
//                                             |___/                    //
//======================================================================//

//@version=6
indicator(title="MACD Leader", overlay=false, max_labels_count=500)

//#region —————————————————————————————————————————————————— Custom Code

//#endregion ————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Common Dependence

p_comm_time_range_to_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int start_unix_time = na
    int end_unix_time = na
    int start_time_hour = na
    int start_time_minute = na
    int end_time_hour = na
    int end_time_minute = na
    if str.length(time_range) == 11
        // Format: hh:mm-hh:mm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
    else if str.length(time_range) == 9
        // Format: hhmm-hhmm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
    start_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)
    end_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)
    [start_unix_time, end_unix_time]

p_comm_time_range_to_start_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int start_time_hour = na
    int start_time_minute = na
    if str.length(time_range) == 11
        // Format: hh:mm-hh:mm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
    else if str.length(time_range) == 9
        // Format: hhmm-hhmm
        start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
        start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
    timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)

p_comm_time_range_to_end_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
    int end_time_hour = na
    int end_time_minute = na
    if str.length(time_range) == 11
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
    else if str.length(time_range) == 9
        end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
        end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
    timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)

p_comm_timeframe_to_seconds(simple string tf) =>
    float seconds = 0
    tf_lower = str.lower(tf)
    value = str.tonumber(str.substring(tf_lower, 0, str.length(tf_lower) - 1))
    if str.endswith(tf_lower, 's')
        seconds := value
    else if str.endswith(tf_lower, 'd')
        seconds := value * 86400
    else if str.endswith(tf_lower, 'w')
        seconds := value * 604800
    else if str.endswith(tf_lower, 'm')
        seconds := value * 2592000
    else
        seconds := str.tonumber(tf_lower) * 60
    seconds

p_custom_sources() =>
    [open, high, low, close, volume]

//#endregion —————————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Ta Dependence

p_ta_macd_leader(series float src, simple int shortLength, simple int longLength, simple int sigLength) =>
    sema = ta.ema(src, shortLength)
    lema = ta.ema(src, longLength)
    i1 = sema + ta.ema(src - sema, shortLength)
    i2 = lema + ta.ema(src - lema, longLength)
    macdl = i1 - i2
    macd = sema - lema
    signal = ta.sma(macd, sigLength)
    [macdl, macd, signal]

//#endregion —————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Constants

// Input Groups
string P_GP_1      =      ""

//#endregion —————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Inputs

//#endregion ———————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Price Data



//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Indicators

[p_ind_1_macdLeader, p_ind_1_macd, p_ind_1_signal]      =      p_ta_macd_leader(close, 12, 26, 9) // MACDL


//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Conditions

//#endregion ———————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Indicator Plots

// MACDL
hline(0, "MACDL - Zero Line", color=color.new(#787B86, 50))
plot(p_ind_1_macdLeader, title="MACDL - MACD Leader", color=color.rgb(178, 34, 34, 0), linewidth=2)

//#endregion ————————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Custom Plots

//#endregion —————————————————————————————————————————————————————————————


//#region —————————————————————————————————————————————————— Alert

//#endregion ——————————————————————————————————————————————————————
Existing indicator chart preview
Existing visual preview. Verify exact values against the source; the image is not a performance result.

Specify a checkable adaptation in Pineify

Ask Pine Script AI Coding Agent to add named Leader, ordinary MACD and SMA-signal plots with explicit inputs and confirmed-bar alerts. Review the generated formula, check syntax and compare the same source and initialization before treating the adaptation as equivalent.

Open Pine Script AI Coding Agent

Educational indicator analysis. No measured trading performance is established here. Syntax validation does not prove formula equivalence, execution assumptions or profitability.

Implementation questions

LazyBear published a TradingView implementation and credits Giorgos E. Siligardos with the concept. This page distinguishes the concept from that implementation.

No fixed lead is guaranteed. In the synthetic step example both become positive on the same bar, and Leader later becomes negative without a standard MACD negative zero cross.

No. This function returns a nine-period SMA of ordinary MACD. The Leader calculation does not use that signal.

Not in this copied example. Those values are returned but have no plot calls or visibility inputs. Add the plots explicitly.

No. Each leg adds a smoothed residual to its first EMA. Under matching initialization this is the DEMA combination 2×EMA(source) − EMA(EMA(source)).

No. It only plots Leader and a zero line. Any alerts, confirmation rules or strategy orders require an explicit adaptation and validation.