//@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()