MACD Divergences
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © undershot
// Free Trading Movement

//@version=5
indicator("MACD Divergences", overlay=false)

fast_length = input(title="Fast Length", defval=12)
slow_length = input(title="Slow Length", defval=26)
src = input(title="Source", defval=close)
signal_length = input.int(title="Signal Smoothing",  minval = 1, maxval = 50, defval = 9)

[macd, macd_s, macd_h] = ta.macd(src, fast_length, slow_length, signal_length)

col_macd = input(#2962FF, "MACD Line  ", group="Color Settings", inline="MACD")
col_signal = input(#FF6D00, "Signal Line  ", group="Color Settings", inline="Signal")
col_grow_above = input(#26A69A, "Above   Grow", group="Histogram", inline="Above")
col_fall_above = input(#B2DFDB, "Fall", group="Histogram", inline="Above")
col_grow_below = input(#FFCDD2, "Below Grow", group="Histogram", inline="Below")
col_fall_below = input(#FF5252, "Fall", group="Histogram", inline="Below")

plot(macd, title="MACD", color=col_macd)
plot(macd_s, title="Signal", color=col_signal)
plot(macd_h, title="Histogram", style=plot.style_columns, color=(macd_h>=0 ? (macd_h[1] < macd_h ? col_grow_above : col_fall_above) : (macd_h[1] < macd_h ? col_grow_below : col_fall_below)))

bullish() =>
    price_nearest = 99999.9
    macd_nearest = 0.0
    macd_nearest_idx = 0
    
    price_furthest = 99999.9
    macd_furthest = 0.0
    macd_furthest_idx = 0
    
    if (macd_h[1] < 0 and macd_h >= 0) or (macd_h[1] < 0 and barstate.islast)
        i = 1
        if barstate.islast
            i := 0
            
        while macd_h[i] < 0
            price_nearest := math.min(low[i], price_nearest)
            macd_nearest := math.min(macd_h[i], macd_nearest)
            if macd_h[i] == macd_nearest
                macd_nearest_idx := i
            i += 1
        while macd_h[i] >= 0
            i += 1
        while macd_h[i] < 0
            price_furthest := math.min(low[i], price_furthest)
            macd_furthest := math.min(macd_h[i], macd_furthest)
            if macd_h[i] == macd_furthest
                macd_furthest_idx := i
            i += 1
            
        divergence = 'none'
        if price_nearest > price_furthest == false and macd_nearest > macd_furthest == true
            divergence := 'classic'
        if price_nearest > price_furthest == true and macd_nearest > macd_furthest == false
            divergence := 'hidden'
        
        [divergence, macd_nearest_idx, macd_furthest_idx]
        

bearish() =>
    price_nearest = 0.0
    macd_nearest = 0.0
    macd_nearest_idx = 0
    
    price_furthest = 0.0
    macd_furthest = 0.0
    macd_furthest_idx = 0
    
    if (macd_h[1] > 0 and macd_h <= 0) or (macd_h[1] > 0 and barstate.islast)
        i = 1
        if barstate.islast
            i := 0
            
        while macd_h[i] > 0
            price_nearest := math.max(high[i], price_nearest)
            macd_nearest := math.max(macd_h[i], macd_nearest)
            if macd_h[i] == macd_nearest
                macd_nearest_idx := i
            i += 1
        while macd_h[i] <= 0
            i += 1
        while macd_h[i] > 0
            price_furthest := math.max(high[i], price_furthest)
            macd_furthest := math.max(macd_h[i], macd_furthest)
            if macd_h[i] == macd_furthest
                macd_furthest_idx := i
            i += 1
            
        divergence = 'none'
        if price_nearest > price_furthest == true and macd_nearest > macd_furthest == false
            divergence := 'classic'
        if price_nearest > price_furthest == false and macd_nearest > macd_furthest == true
            divergence := 'hidden'
        
        [divergence, macd_nearest_idx, macd_furthest_idx]

        
[a_divergence, a_idx_1, a_idx_2] = bullish()

if a_divergence == 'classic'
    line.new(x1=bar_index[a_idx_1], y1=macd_h[a_idx_1], x2=bar_index[a_idx_2], y2=macd_h[a_idx_2], color=color.green, width=1)
    label.new(bar_index[a_idx_2 - (a_idx_2 - a_idx_1)/2], math.max(macd_h[a_idx_1], macd_h[a_idx_2]) - (math.max(macd_h[a_idx_1], macd_h[a_idx_2]) - math.min(macd_h[a_idx_1], macd_h[a_idx_2])) / 2, color=color.green, style=label.style_label_up, text="Classic bullish", size=size.small)
    
if a_divergence == 'hidden'
    line.new(x1=bar_index[a_idx_1], y1=macd_h[a_idx_1], x2=bar_index[a_idx_2], y2=macd_h[a_idx_2], color=color.red, width=1)
    label.new(bar_index[a_idx_2 - (a_idx_2 - a_idx_1)/2], math.max(macd_h[a_idx_1], macd_h[a_idx_2]) - (math.max(macd_h[a_idx_1], macd_h[a_idx_2]) - math.min(macd_h[a_idx_1], macd_h[a_idx_2])) / 2, color=color.red, style=label.style_label_up, text="Hidden bullish", size=size.small)
    
    
[divergence, idx_1, idx_2] = bearish()

if divergence == 'hidden'
    line.new(x1=bar_index[idx_1], y1=macd_h[idx_1], x2=bar_index[idx_2], y2=macd_h[idx_2], color=color.red, width=1)
    label.new(bar_index[idx_2 - (idx_2 - idx_1)/2], math.max(macd_h[idx_1], macd_h[idx_2]) - (math.max(macd_h[idx_1], macd_h[idx_2]) - math.min(macd_h[idx_1], macd_h[idx_2])) / 2, color=color.red, style=label.style_label_down, text="Hidden bearish", size=size.small)
    
if divergence == 'classic'
    line.new(x1=bar_index[idx_1], y1=macd_h[idx_1], x2=bar_index[idx_2], y2=macd_h[idx_2], color=color.green, width=1)
    label.new(bar_index[idx_2 - (idx_2 - idx_1)/2], math.max(macd_h[idx_1], macd_h[idx_2]) - (math.max(macd_h[idx_1], macd_h[idx_2]) - math.min(macd_h[idx_1], macd_h[idx_2])) / 2, color=color.green, style=label.style_label_down, text="Classic bearish", size=size.small)
    
alertcondition(barstate.islast and (macd_h >= 0 and macd_h[1] < 0) and a_divergence == 'classic', title='Classic bullish', message='Classic bullish MACD divergence')
alertcondition(barstate.islast and (macd_h >= 0 and macd_h[1] < 0) and a_divergence == 'hidden', title='Hidden bullish', message='Hidden bullish MACD divergence')
alertcondition(barstate.islast and (macd_h <= 0 and macd_h[1] > 0) and divergence == 'classic', title='Classic bearish', message='Classic bearish MACD divergence')
alertcondition(barstate.islast and (macd_h <= 0 and macd_h[1] > 0) and divergence == 'hidden', title='Hidden bearish', message='Hidden bearish MACD divergence')
alertcondition(barstate.islast and ((macd_h >= 0 and macd_h[1] < 0) or (macd_h <= 0 and macd_h[1] > 0)) and (a_divergence == 'classic' or a_divergence == 'hidden' or divergence == 'classic' or divergence == 'hidden'), title='Any divergence', message='MACD divergence')