har

🧩 Syntax:
//@version=5
indicator('Auto Harmonic Patterns - Open Source', shorttitle='Auto Patterns', overlay=true, max_bars_back=1000, max_lines_count=500, max_labels_count=500)
Length = input.int(10, minval=0, step=5)
DeviationThreshold = 0
errorPercent = input.int(10, minval=5, step=5, maxval=20)
showPivots = input(false)
showRatios = input(false)
showZigZag = input(false)
bullishColor = input(color.green)
bullTrapColor = input(color.orange)
bearishColor = input(color.red)
bearTrapColor = input(color.lime)
MaxRiskPerReward = input.int(40, title='Max Risk Per Reward (Double Top/Bottom)', step=10, minval=0)

abcdClassic = input(true)
abEQcd = input(true)
abcdExt = input(true)
doubleTopBottom = input(true)
gartley = input(true)
crab = input(true)
deepCrab = input(true)
bat = input(true)
butterfly = input(true)
shark = input(true)
cypher = input(true)
threeDrives = input(true)
fiveZero = input(true)

var abcdlines = array.new_line(3)
var abcdtype = array.new_int(2, 1)

var wmlines = array.new_line(4)
var wmtype = array.new_int(2, 1)
var wmLabels = array.new_bool(9, false)

var patterncount = array.new_int(26, 0)


var zigzaglines = array.new_line(0)
var zigzaglabels = array.new_label(0)
var zigzagdir = array.new_int(0)
var zigzagratios = array.new_float(0)

int max_array_size = 10
transparent = color.new(#FFFFFF, 100)
err_min = (100 - errorPercent) / 100
err_max = (100 + errorPercent) / 100
pivots(length) =>
    float ph = ta.highestbars(high, length) == 0 ? high : na
    float pl = ta.lowestbars(low, length) == 0 ? low : na
    dir = 0
    iff_1 = pl and na(ph) ? -1 : dir[1]
    dir := ph and na(pl) ? 1 : iff_1
    [dir, ph, pl]

get_edir(dir, y2) =>
    eDir = dir
    if array.size(zigzaglines) > 0
        lastLine = array.get(zigzaglines, 0)
        lastPivot = line.get_y1(lastLine)
        eDir := (dir * y2 > dir * lastPivot ? 2 : 1) * dir
        eDir
    lineColor = eDir == 2 ? bullishColor : eDir == 1 ? bullTrapColor : eDir == -1 ? bearTrapColor : bearishColor
    [eDir, lineColor]

add_to_zigzaglines(x1, y1, x2, y2, dir) =>
    [eDir, lineColor] = get_edir(dir, y2)
    color = showZigZag ? lineColor : color.new(#FFFFFF, 100)
    zline = line.new(x1=x1, y1=y1, x2=x2, y2=y2, color=color, width=2, style=line.style_solid)
    array.unshift(zigzaglines, zline)

add_to_zigzaglabels(x1, x2, y1, y2, dir) =>
    [eDir, lineColor] = get_edir(dir, y2)
    pivotLabel = eDir == 2 ? 'HH' : eDir == 1 ? 'LH' : eDir == -1 ? 'HL' : 'LL'
    lastLineLen = 0.0
    currentLineLen = math.abs(y2 - y1)
    if array.size(zigzaglines) > 0
        lastLine = array.get(zigzaglines, 0)
        lastLineLen := math.abs(line.get_y2(lastLine) - line.get_y1(lastLine))
        lastLineLen

    ratio = math.round(lastLineLen != 0 ? currentLineLen / lastLineLen : 0, 3)
    labelText = (showPivots ? pivotLabel : '') + (showPivots and showRatios ? ' - ' : '') + (showRatios ? str.tostring(ratio) : '')
    yloc = dir > 0 ? yloc.abovebar : yloc.belowbar
    labelStyle = dir > 0 ? label.style_label_down : label.style_label_up
    labelSize = showRatios and showPivots ? size.normal : size.small
    zlabel = label.new(x=x2, y=y2, text=labelText, xloc=xloc.bar_index, yloc=yloc, color=lineColor, size=labelSize, style=labelStyle)

    array.unshift(zigzaglabels, zlabel)
    array.unshift(zigzagdir, eDir)
    array.unshift(zigzagratios, ratio)
    if not showRatios and not showPivots
        label.delete(zlabel)

add_to_zigzag(dir, dirchanged, ph, pl, index) =>
    value = dir == 1 ? ph : pl

    y1 = dir == 1 ? ta.lowest(Length) : ta.highest(Length)
    x1 = bar_index + (dir == 1 ? ta.lowestbars(Length) : ta.highestbars(Length))
    x2 = index
    y2 = value
    skip = false
    if array.size(zigzaglines) > 0
        if not dirchanged
            lastline = array.get(zigzaglines, 0)
            lasty2 = line.get_y2(lastline)
            if lasty2 * dir > y2 * dir
                skip := true
                skip
            else
                line.delete(array.shift(zigzaglines))
                label.delete(array.shift(zigzaglabels))
                array.shift(zigzagdir)
                array.shift(zigzagratios)
                skip := false
                skip

        if array.size(zigzaglines) > 0
            lastLine = array.get(zigzaglines, 0)
            x1 := line.get_x2(lastLine)
            y1 := line.get_y2(lastLine)
            y1

    outsideDeviationThreshold = math.abs(y1 - y2) * 100 / y1 > DeviationThreshold
    if outsideDeviationThreshold and not skip
        add_to_zigzaglabels(x1, x2, y1, y2, dir)
        add_to_zigzaglines(x1, y1, x2, y2, dir)
    if array.size(zigzaglines) > max_array_size
        array.pop(zigzaglines)
        array.pop(zigzaglabels)
        array.pop(zigzagdir)
        array.pop(zigzagratios)

zigzag(length, DeviationThreshold) =>
    [dir, ph, pl] = pivots(length)
    dirchanged = ta.change(dir)
    if ph or pl
        add_to_zigzag(dir, dirchanged, ph, pl, bar_index)

calculate_abcd() =>
    abcd = false

    if array.size(zigzagratios) >= 3 and array.size(zigzaglines) >= 4
        abcRatio = array.get(zigzagratios, 2)
        bcdRatio = array.get(zigzagratios, 1)

        ab = array.get(zigzaglines, 3)
        bc = array.get(zigzaglines, 2)
        cd = array.get(zigzaglines, 1)

        ab_time = math.abs(line.get_x1(ab) - line.get_x2(ab))
        ab_price = math.abs(line.get_y1(ab) - line.get_y2(ab))

        cd_time = math.abs(line.get_x1(cd) - line.get_x2(cd))
        cd_price = math.abs(line.get_y1(cd) - line.get_y2(cd))

        a = line.get_y1(ab)
        b = line.get_y2(ab)

        c = line.get_y1(cd)
        d = line.get_y2(cd)

        abcdDirection = a < b and a < c and c < b and c < d and a < d and b < d or a > b and a > c and c > b and c > d and a > d and b > d
        dir = a < b and a < c and c < b and c < d and a < d and b < d ? 1 : a > b and a > c and c > b and c > d and a > d and b > d ? -1 : 0
        time_ratio = cd_time / ab_time
        price_ratio = cd_price / ab_price

        // if (ab == ab[1] and bc == bc[1] and cd == cd[1])
        //     abcd := false
        if abcdClassic and abcRatio >= 0.618 * err_min and abcRatio <= 0.786 * err_max and bcdRatio >= 1.272 * err_min and bcdRatio <= 1.618 * err_max and abcdDirection
            abcd := true
            array.set(abcdtype, 0, 1)
        if abEQcd and time_ratio >= err_min and time_ratio <= err_max and price_ratio >= err_min and price_ratio <= err_max and abcdDirection
            abcd := true
            array.set(abcdtype, 0, 2)
        if abcdExt and price_ratio >= 1.272 * err_min and price_ratio <= 1.618 * err_max and abcRatio >= 0.618 * err_min and abcRatio <= 0.786 * err_max and abcdDirection
            abcd := true
            array.set(abcdtype, 0, 3)
        if abcd
            array.set(abcdlines, 0, ab)
            array.set(abcdlines, 1, bc)
            array.set(abcdlines, 2, cd)
            array.set(abcdtype, 1, dir)
    abcd

draw_abcd() =>
    abcd = calculate_abcd()
    if array.size(abcdlines) > 2 and array.size(zigzaglines) >= 4
        ab = array.get(abcdlines, 0)
        bc = array.get(abcdlines, 1)
        cd = array.get(abcdlines, 2)

        abcd_type = array.get(abcdtype, 0)
        dir = array.get(abcdtype, 1)

        labelColor = dir > 0 ? bearishColor : bullishColor
        labelStyle = dir > 0 ? label.style_label_down : label.style_label_up
        yloc = dir > 0 ? yloc.abovebar : yloc.belowbar

        labelText = abcd_type == 1 ? 'ABCD' : abcd_type == 2 ? 'AB=CD' : abcd_type == 3 ? 'ABCD Ext' : ''
        ab_zg = array.get(zigzaglines, 3)
        bc_zg = array.get(zigzaglines, 2)
        cd_zg = array.get(zigzaglines, 1)
        count_index = abcd_type * 2 - 2 + (dir > 0 ? 1 : 0)
        abcdLabel = label.new(x=line.get_x2(cd), y=line.get_y2(cd), text=labelText, xloc=xloc.bar_index, yloc=yloc, color=labelColor, size=size.normal, style=labelStyle)
        array.set(patterncount, count_index, array.get(patterncount, count_index) + 1)

        line.set_color(ab, labelColor)
        line.set_color(bc, labelColor)
        line.set_color(cd, labelColor)

        if abcd[1] and bc == bc_zg and ab == ab_zg
            label.delete(abcdLabel[1])
            array.set(patterncount, count_index, array.get(patterncount, count_index) - 1)
            line.set_color(ab, transparent)
            line.set_color(bc, transparent)
            line.set_color(cd, transparent)

        if not abcd or label.get_x(abcdLabel) == label.get_x(abcdLabel[1]) and label.get_y(abcdLabel) == label.get_y(abcdLabel[1])
            label.delete(abcdLabel)
            array.set(patterncount, count_index, array.get(patterncount, count_index) - 1)
        else
            line.set_color(ab, labelColor)
            line.set_color(bc, labelColor)
            line.set_color(cd, labelColor)

calculate_double_pattern() =>
    doubleTop = false
    doubleBottom = false
    if array.size(zigzagdir) >= 4 and doubleTopBottom
        value = line.get_y2(array.get(zigzaglines, 1))
        highLow = array.get(zigzagdir, 1)

        lvalue = line.get_y2(array.get(zigzaglines, 2))
        lhighLow = array.get(zigzagdir, 2)

        llvalue = line.get_y2(array.get(zigzaglines, 3))
        llhighLow = array.get(zigzagdir, 3)

        risk = math.abs(value - llvalue)
        reward = math.abs(value - lvalue)
        riskPerReward = risk * 100 / (risk + reward)

        if highLow == 1 and llhighLow == 2 and lhighLow == -1 and riskPerReward < MaxRiskPerReward
            doubleTop := true
            doubleTop
        if highLow == -1 and llhighLow == -2 and lhighLow == 1 and riskPerReward < MaxRiskPerReward
            doubleBottom := true
            doubleBottom

    [doubleTop, doubleBottom]


draw_double_pattern(doubleTop, doubleBottom) =>
    if array.size(zigzagdir) >= 4
        line1 = array.get(zigzaglines, 1)
        line2 = array.get(zigzaglines, 2)

        x1 = line.get_x1(line2)
        y1 = line.get_y1(line2)
        x2 = line.get_x2(line1)
        y2 = line.get_y2(line1)

        midline = line.get_y1(line1)
        midlineIndex = line.get_x1(line1)
        risk = math.abs(y2 - y1)
        reward = math.abs(y2 - midline)
        riskPerReward = math.round(risk * 100 / (risk + reward), 2)

        base = line.new(x1=x1, y1=y1, x2=x2, y2=y2, color=doubleTop ? bearishColor : bullishColor, width=2, style=line.style_solid)

        count_index = doubleTop ? 7 : 6
        labelText = (doubleTop ? 'DT - ' : 'DB - ') + str.tostring(riskPerReward)
        array.set(patterncount, count_index, array.get(patterncount, count_index) + 1)
        baseLabel = label.new(x=x2, y=y2, text=labelText, yloc=doubleTop ? yloc.abovebar : yloc.belowbar, color=doubleTop ? bearishColor : bullishColor, style=doubleTop ? label.style_label_down : label.style_label_up, textcolor=color.black, size=size.normal)
        if line.get_x1(base) == line.get_x1(base[1])
            line.delete(base[1])
            label.delete(baseLabel[1])
            array.set(patterncount, count_index, array.get(patterncount, count_index) - 1)

        if not(doubleTop or doubleBottom)
            line.delete(base)
            label.delete(baseLabel)
            array.set(patterncount, count_index, array.get(patterncount, count_index) - 1)

calculate_wm_patterns() =>
    wm_pattern = false
    if array.size(zigzagdir) >= 5
        yxaRatio = array.get(zigzagratios, 4)
        xabRatio = array.get(zigzagratios, 3)
        abcRatio = array.get(zigzagratios, 2)
        bcdRatio = array.get(zigzagratios, 1)

        xa = array.get(zigzaglines, 4)
        ab = array.get(zigzaglines, 3)
        bc = array.get(zigzaglines, 2)
        cd = array.get(zigzaglines, 1)

        x = line.get_y1(xa)
        a = line.get_y2(xa)
        b = line.get_y2(ab)
        c = line.get_y1(cd)
        d = line.get_y2(cd)

        xadRatio = math.round(math.abs(a - d) / math.abs(x - a), 3)
        dir = a > d ? 1 : -1

        maxP1 = math.max(x, a)
        maxP2 = math.max(c, d)
        minP1 = math.min(x, a)
        minP2 = math.min(c, d)

        highPoint = math.min(maxP1, maxP2)
        lowPoint = math.max(minP1, minP2)

        if b < highPoint and b > lowPoint
            //gartley
            if gartley and xabRatio >= 0.618 * err_min and xabRatio <= 0.618 * err_max and abcRatio >= 0.382 * err_min and abcRatio <= 0.886 * err_max and (bcdRatio >= 1.272 * err_min and bcdRatio <= 1.618 * err_max or xadRatio >= 0.786 * err_min and xadRatio <= 0.786 * err_max)
                wm_pattern := true
                array.set(wmtype, 1, 0)
                array.set(wmLabels, 0, true)
            else
                array.set(wmLabels, 0, false)
            //Crab
            if crab and xabRatio >= 0.382 * err_min and xabRatio <= 0.618 * err_max and abcRatio >= 0.382 * err_min and abcRatio <= 0.886 * err_max and (bcdRatio >= 2.24 * err_min and bcdRatio <= 3.618 * err_max or xadRatio >= 1.618 * err_min and xadRatio <= 1.618 * err_max)
                wm_pattern := true
                array.set(wmtype, 1, 1)
                array.set(wmLabels, 1, true)
            else
                array.set(wmLabels, 1, false)
            //Deep Crab
            if deepCrab and xabRatio >= 0.886 * err_min and xabRatio <= 0.886 * err_max and abcRatio >= 0.382 * err_min and abcRatio <= 0.886 * err_max and (bcdRatio >= 2.00 * err_min and bcdRatio <= 3.618 * err_max or xadRatio >= 1.618 * err_min and xadRatio <= 1.618 * err_max)
                wm_pattern := true
                array.set(wmtype, 1, 2)
                array.set(wmLabels, 2, true)
            else
                array.set(wmLabels, 2, false)
            //Bat
            if bat and xabRatio >= 0.382 * err_min and xabRatio <= 0.50 * err_max and abcRatio >= 0.382 * err_min and abcRatio <= 0.886 * err_max and (bcdRatio >= 1.618 * err_min and bcdRatio <= 2.618 * err_max or xadRatio >= 0.886 * err_min and xadRatio <= 0.886 * err_max)
                wm_pattern := true
                array.set(wmtype, 1, 3)
                array.set(wmLabels, 3, true)
            else
                array.set(wmLabels, 3, false)
            //Butterfly
            if butterfly and xabRatio >= 0.786 * err_min and xabRatio <= 0.786 * err_max and abcRatio >= 0.382 * err_min and abcRatio <= 0.886 * err_max and (bcdRatio >= 1.618 * err_min and bcdRatio <= 2.618 * err_max or xadRatio >= 1.272 * err_min and xadRatio <= 1.618 * err_max)
                wm_pattern := true
                array.set(wmtype, 1, 4)
                array.set(wmLabels, 4, true)
            else
                array.set(wmLabels, 4, false)
            //Shark
            if shark and abcRatio >= 1.13 * err_min and abcRatio <= 1.618 * err_max and bcdRatio >= 1.618 * err_min and bcdRatio <= 2.24 * err_max and xadRatio >= 0.886 * err_min and xadRatio <= 1.13 * err_max
                wm_pattern := true
                array.set(wmtype, 1, 5)
                array.set(wmLabels, 5, true)
            else
                array.set(wmLabels, 5, false)
            //Cypher
            if cypher and xabRatio >= 0.382 * err_min and xabRatio <= 0.618 * err_max and abcRatio >= 1.13 * err_min and abcRatio <= 1.414 * err_max and (bcdRatio >= 1.272 * err_min and bcdRatio <= 2.00 * err_max or xadRatio >= 0.786 * err_min and xadRatio <= 0.786 * err_max)
                wm_pattern := true
                array.set(wmtype, 1, 6)
                array.set(wmLabels, 6, true)
            else
                array.set(wmLabels, 6, false)
        //3 drive
        if threeDrives and yxaRatio >= 0.618 * err_min and yxaRatio <= 0.618 * err_max and xabRatio >= 1.27 * err_min and xabRatio <= 1.618 * err_max and abcRatio >= 0.618 * err_min and abcRatio <= 0.618 * err_max and bcdRatio >= 1.27 * err_min and bcdRatio <= 1.618 * err_max
            wm_pattern := true
            array.set(wmtype, 1, 7)
            array.set(wmLabels, 7, true)
        else
            array.set(wmLabels, 7, false)
        //5-0
        if fiveZero and xabRatio >= 1.13 * err_min and xabRatio <= 1.618 * err_max and abcRatio >= 1.618 * err_min and abcRatio <= 2.24 * err_max and bcdRatio >= 0.5 * err_min and bcdRatio <= 0.5 * err_max
            wm_pattern := true
            array.set(wmtype, 1, 8)
            array.set(wmLabels, 8, true)
        else
            array.set(wmLabels, 8, false)
        if wm_pattern
            array.set(wmlines, 0, xa)
            array.set(wmlines, 1, ab)
            array.set(wmlines, 2, bc)
            array.set(wmlines, 3, cd)
            array.set(wmtype, 0, dir)
    wm_pattern

draw_wm_patterns() =>
    wm_pattern = calculate_wm_patterns()
    if array.size(wmlines) >= 4 and array.size(zigzaglines) >= 5
        xa = array.get(wmlines, 0)
        ab = array.get(wmlines, 1)
        bc = array.get(wmlines, 2)
        cd = array.get(wmlines, 3)

        dir = array.get(wmtype, 0)
        type = array.get(wmtype, 1)
        trendColor = dir > 0 ? bullishColor : bearishColor
        x = line.get_y1(xa)
        xbar = line.get_x1(xa)

        a = line.get_y2(xa)
        abar = line.get_x2(xa)

        b = line.get_y2(ab)
        bbar = line.get_x2(ab)

        c = line.get_y2(bc)
        cbar = line.get_x2(bc)

        d = line.get_y2(cd)
        dbar = line.get_x2(cd)

        line.set_color(xa, trendColor)
        line.set_color(ab, trendColor)
        line.set_color(bc, trendColor)
        line.set_color(cd, trendColor)
        ac = line.new(x1=abar, y1=a, x2=cbar, y2=c, color=trendColor, width=2, style=line.style_solid)
        xb = line.new(x1=xbar, y1=x, x2=bbar, y2=b, color=trendColor, width=2, style=line.style_solid)
        xd = line.new(x1=xbar, y1=x, x2=dbar, y2=d, color=trendColor, width=2, style=line.style_solid)
        bd = line.new(x1=bbar, y1=b, x2=dbar, y2=d, color=trendColor, width=2, style=line.style_solid)

        isGartley = array.get(wmLabels, 0)
        isCrab = array.get(wmLabels, 1)
        isDeepCrab = array.get(wmLabels, 2)
        isBat = array.get(wmLabels, 3)
        isButterfly = array.get(wmLabels, 4)
        isShark = array.get(wmLabels, 5)
        isCypher = array.get(wmLabels, 6)
        is3Drives = array.get(wmLabels, 7)
        isFiveZero = array.get(wmLabels, 8)

        labelText = isGartley ? 'Gartley' : ''
        labelText += (isCrab ? (labelText == '' ? '' : '\n') + 'Crab' : '')
        labelText += (isDeepCrab ? (labelText == '' ? '' : '\n') + 'Deep Crab' : '')
        labelText += (isBat ? (labelText == '' ? '' : '\n') + 'Bat' : '')
        labelText += (isButterfly ? (labelText == '' ? '' : '\n') + 'Butterfly' : '')
        labelText += (isShark ? (labelText == '' ? '' : '\n') + 'Shark' : '')
        labelText += (isCypher ? (labelText == '' ? '' : '\n') + 'Cypher' : '')
        labelText += (is3Drives ? (labelText == '' ? '' : '\n') + '3 Drive' : '')
        labelText += (isFiveZero ? (labelText == '' ? '' : '\n') + '5-0' : '')

        baseLabel = label.new(x=bbar, y=b, text=labelText, yloc=dir < 1 ? yloc.abovebar : yloc.belowbar, color=trendColor, style=dir < 1 ? label.style_label_down : label.style_label_up, textcolor=color.black, size=size.normal)

        xa_zg = array.get(zigzaglines, 4)
        ab_zg = array.get(zigzaglines, 3)
        bc_zg = array.get(zigzaglines, 2)
        cd_zg = array.get(zigzaglines, 1)

        for i = 0 to 8 by 1
            count_index = i * 2 + 8 + (dir > 0 ? 0 : 1)
            if array.get(wmLabels, i) and xa != xa[1] and ab != ab[1] and bc != bc[1]
                array.set(patterncount, count_index, array.get(patterncount, count_index) + 1)
            if array.get(wmLabels, i)[1] and xa == xa_zg and ab == ab_zg and bc == bc_zg
                array.set(patterncount, count_index, array.get(patterncount, count_index) - 1)

        if wm_pattern[1] and not wm_pattern and xa == xa_zg and ab == ab_zg and bc == bc_zg
            line.delete(ac[1])
            line.delete(xb[1])
            line.delete(xd[1])
            line.delete(bd[1])
            line.set_color(xa[1], transparent)
            line.set_color(ab[1], transparent)
            line.set_color(bc[1], transparent)
            line.set_color(cd[1], transparent)
            label.delete(baseLabel[1])
            // array.set(patterncount, count_index, array.get(patterncount, count_index) - 1)

        if not wm_pattern or label.get_x(baseLabel) == label.get_x(baseLabel[1]) and label.get_y(baseLabel) == label.get_y(baseLabel[1])
            line.delete(ac)
            line.delete(xb)
            line.delete(xd)
            line.delete(bd)
            label.delete(baseLabel)

zigzag(Length, DeviationThreshold)
draw_abcd()

[doubleTop, doubleBottom] = calculate_double_pattern()
draw_double_pattern(doubleTop, doubleBottom)

draw_wm_patterns()