// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © HeWhoMustNotBeNamed //@version=5 indicator('Multi ZigZag Harmonic Patterns', shorttitle='MZigZag - HP', overlay=true, max_bars_back=1000, max_lines_count=500, max_labels_count=500) max_pivot_size = input.int(100, step=10) showZigZag1 = input(true) zigzag1Length = input.int(5, step=5, minval=1) zigzag1Color = input(color.teal) zigzag1Width = 1 zigzag1Style = line.style_solid showZigZag2 = input(true) zigzag2Length = input.int(10, step=5, minval=1) zigzag2Color = input(color.olive) zigzag2Width = 1 zigzag2Style = line.style_solid showZigZag3 = input(true) zigzag3Length = input.int(15, step=5, minval=1) zigzag3Color = input(color.lime) zigzag3Width = 1 zigzag3Style = line.style_solid showZigZag4 = input(true) zigzag4Length = input.int(20, step=5, minval=1) zigzag4Color = input(color.fuchsia) zigzag4Width = 1 zigzag4Style = line.style_solid abcdClassic = input(true) abEQcd = input(true) abcdExt = 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) doubleBottomTop = input(true) errorPercent = input.int(10, minval=5, step=5, maxval=20) MaxRiskPerReward = input.int(30, title='Max Risk Per Reward (Double Top/Bottom)', step=10, minval=0) showStatTable = false waitForConfirmation = input(true) bullishColor = input(color.green) bearishColor = input(color.red) err_min = (100 - errorPercent) / 100 err_max = (100 + errorPercent) / 100 var zigzagpivots1 = array.new_float(0) var zigzagpivotbars1 = array.new_int(0) var zigzagpivotdirs1 = array.new_int(0) var zigzagpivots2 = array.new_float(0) var zigzagpivotbars2 = array.new_int(0) var zigzagpivotdirs2 = array.new_int(0) var zigzagpivots3 = array.new_float(0) var zigzagpivotbars3 = array.new_int(0) var zigzagpivotdirs3 = array.new_int(0) var zigzagpivots4 = array.new_float(0) var zigzagpivotbars4 = array.new_int(0) var zigzagpivotdirs4 = array.new_int(0) var wmlines1 = array.new_line(8) var wmtype1 = array.new_int(2, 1) var wmLabels1 = array.new_bool(13, false) var wmLabel1 = array.new_label(1) var wmlines2 = array.new_line(8) var wmtype2 = array.new_int(2, 1) var wmLabels2 = array.new_bool(13, false) var wmLabel2 = array.new_label(1) var wmlines3 = array.new_line(8) var wmtype3 = array.new_int(2, 1) var wmLabels3 = array.new_bool(13, false) var wmLabel3 = array.new_label(1) var wmlines4 = array.new_line(8) var wmtype4 = array.new_int(2, 1) var wmLabels4 = array.new_bool(13, false) var wmLabel4 = array.new_label(1) pivots(length) => float phigh = ta.highestbars(high, length) == 0 ? high : na float plow = ta.lowestbars(low, length) == 0 ? low : na dir = 0 iff_1 = plow and na(phigh) ? -1 : dir[1] dir := phigh and na(plow) ? 1 : iff_1 [dir, phigh, plow] zigzag(length, zigzagpivots, zigzagpivotbars, zigzagpivotdirs) => [dir, phigh, plow] = pivots(length) dirchanged = ta.change(dir) if phigh or plow value = dir == 1 ? phigh : plow bar = bar_index newDir = dir if not dirchanged and array.size(zigzagpivots) >= 1 pivot = array.shift(zigzagpivots) pivotbar = array.shift(zigzagpivotbars) pivotdir = array.shift(zigzagpivotdirs) useNewValues = value * pivotdir < pivot * pivotdir value := useNewValues ? pivot : value bar := useNewValues ? pivotbar : bar bar if array.size(zigzagpivots) >= 2 LastPoint = array.get(zigzagpivots, 1) newDir := dir * value > dir * LastPoint ? dir * 2 : dir newDir array.unshift(zigzagpivots, value=value) array.unshift(zigzagpivotbars, bar) array.unshift(zigzagpivotdirs, newDir) if array.size(zigzagpivots) > max_pivot_size array.pop(zigzagpivots) array.pop(zigzagpivotbars) array.pop(zigzagpivotdirs) get_harmonic_label(wmLabels, dir, price, bar) => 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) isAbcd = array.get(wmLabels, 9) isAbEqCd = array.get(wmLabels, 10) isAbcdExt = array.get(wmLabels, 11) isDoubleTop = array.get(wmLabels, 12) and dir < 0 isDoubleBottom = array.get(wmLabels, 12) and dir > 0 labelText = isGartley ? 'Gartley' : '' labelText := labelText + (isCrab ? (labelText == '' ? '' : '\n') + 'Crab' : '') labelText := labelText + (isDeepCrab ? (labelText == '' ? '' : '\n') + 'Deep Crab' : '') labelText := labelText + (isBat ? (labelText == '' ? '' : '\n') + 'Bat' : '') labelText := labelText + (isButterfly ? (labelText == '' ? '' : '\n') + 'Butterfly' : '') labelText := labelText + (isShark ? (labelText == '' ? '' : '\n') + 'Shark' : '') labelText := labelText + (isCypher ? (labelText == '' ? '' : '\n') + 'Cypher' : '') labelText := labelText + (is3Drives ? (labelText == '' ? '' : '\n') + '3 Drive' : '') labelText := labelText + (isFiveZero ? (labelText == '' ? '' : '\n') + '5-0' : '') labelText := labelText + (isAbcd ? (labelText == '' ? '' : '\n') + 'ABCD' : '') labelText := labelText + (isAbEqCd ? (labelText == '' ? '' : '\n') + 'AB=CD' : '') labelText := labelText + (isAbcdExt ? (labelText == '' ? '' : '\n') + 'ABCD Extension' : '') labelText := labelText + (isDoubleTop ? (labelText == '' ? '' : '\n') + 'Double Top' : '') labelText := labelText + (isDoubleBottom ? (labelText == '' ? '' : '\n') + 'Double Bottom' : '') trendColor = dir > 0 ? bullishColor : bearishColor baseLabel = label.new(x=bar, y=price, text=labelText, yloc=yloc.price, color=trendColor, style=dir < 1 ? label.style_label_down : label.style_label_up, textcolor=color.black, size=size.normal) baseLabel detect_harmonic_pattern(zigzagpivots, zigzagpivotbars, zigzagpivotdirs, wmlines, wmlabel, wmtype, wmLabels, zigzagColor, zigzagWidth, zigzagStyle, showZigZag) => start = waitForConfirmation ? 1 : 0 wm_pattern = false abcd_pattern = false double_pattern = false if array.size(zigzagpivots) >= 6 + start and showZigZag d = array.get(zigzagpivots, start + 0) dBar = array.get(zigzagpivotbars, start + 0) dDir = array.get(zigzagpivotdirs, start + 0) c = array.get(zigzagpivots, start + 1) cBar = array.get(zigzagpivotbars, start + 1) cDir = array.get(zigzagpivotdirs, start + 1) b = array.get(zigzagpivots, start + 2) bBar = array.get(zigzagpivotbars, start + 2) bDir = array.get(zigzagpivotdirs, start + 2) a = array.get(zigzagpivots, start + 3) aBar = array.get(zigzagpivotbars, start + 3) aDir = array.get(zigzagpivotdirs, start + 3) x = array.get(zigzagpivots, start + 4) xBar = array.get(zigzagpivotbars, start + 4) xDir = array.get(zigzagpivotdirs, start + 4) y = array.get(zigzagpivots, start + 5) yBar = array.get(zigzagpivotbars, start + 5) yDir = array.get(zigzagpivotdirs, start + 5) highPoint = math.max(x, a, b, c, d) lowPoint = math.min(x, a, b, c, d) dir = c > d ? 1 : -1 xabRatio = math.abs(b - a) / math.abs(x - a) abcRatio = math.abs(c - b) / math.abs(a - b) bcdRatio = math.abs(d - c) / math.abs(b - c) xadRatio = math.abs(d - a) / math.abs(x - a) yxaRatio = math.abs(a - x) / math.abs(y - x) abTime = math.abs(aBar - bBar) cdTime = math.abs(cBar - dBar) abPrice = math.abs(a - b) cdPrice = math.abs(c - d) time_ratio = cdTime / abTime price_ratio = cdPrice / abPrice abcdDirection = 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 risk = math.abs(b - d) reward = math.abs(c - d) riskPerReward = risk * 100 / (risk + reward) 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(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(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(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(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(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(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(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(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(wmLabels, 8, true) else array.set(wmLabels, 8, false) //ABCD Classic 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 != 0 abcd_pattern := true array.set(wmLabels, 9, true) else array.set(wmLabels, 9, false) //AB=CD if abEQcd and time_ratio >= err_min and time_ratio <= err_max and price_ratio >= err_min and price_ratio <= err_max and abcdDirection != 0 abcd_pattern := true array.set(wmLabels, 10, true) else array.set(wmLabels, 10, false) //ABCD Ext 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 != 0 abcd_pattern := true array.set(wmLabels, 11, true) else array.set(wmLabels, 11, false) //Double Top/Bottom if doubleBottomTop and (dDir == 1 and bDir == 2 and cDir == -1 or dDir == -1 and bDir == -2 and cDir == 1) and riskPerReward < MaxRiskPerReward double_pattern := true array.set(wmLabels, 12, true) else array.set(wmLabels, 12, false) cancelW = false cancelA = false cancelD = false if wm_pattern[1] and x == x[1] and a == a[1] and b == b[1] and c == c[1] line.delete(array.get(wmlines, 0)) line.delete(array.get(wmlines, 1)) line.delete(array.get(wmlines, 2)) line.delete(array.get(wmlines, 3)) line.delete(array.get(wmlines, 4)) line.delete(array.get(wmlines, 5)) line.delete(array.get(wmlines, 6)) line.delete(array.get(wmlines, 7)) label.delete(array.get(wmlabel, 0)) cancelW := true cancelW if abcd_pattern[1] and a == a[1] and b == b[1] and c == c[1] line.delete(array.get(wmlines, 1)) line.delete(array.get(wmlines, 2)) line.delete(array.get(wmlines, 3)) label.delete(array.get(wmlabel, 0)) cancelA := true cancelA if double_pattern[1] and a == a[1] and b == b[1] and c == c[1] line.delete(array.get(wmlines, 5)) label.delete(array.get(wmlabel, 0)) cancelD := true cancelD if wm_pattern xa = line.new(y1=x, y2=a, x1=xBar, x2=aBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) ab = line.new(y1=a, y2=b, x1=aBar, x2=bBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) bc = line.new(y1=b, y2=c, x1=bBar, x2=cBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) cd = line.new(y1=c, y2=d, x1=cBar, x2=dBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) xb = line.new(y1=x, y2=b, x1=xBar, x2=bBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) bd = line.new(y1=b, y2=d, x1=bBar, x2=dBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) xd = line.new(y1=x, y2=d, x1=xBar, x2=dBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) ac = line.new(y1=a, y2=c, x1=aBar, x2=cBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) array.set(wmlines, 0, xa) array.set(wmlines, 1, ab) array.set(wmlines, 2, bc) array.set(wmlines, 3, cd) array.set(wmlines, 4, xb) array.set(wmlines, 5, bd) array.set(wmlines, 6, xd) array.set(wmlines, 7, ac) array.set(wmtype, 0, dir) if abcd_pattern and not wm_pattern ab = line.new(y1=a, y2=b, x1=aBar, x2=bBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) bc = line.new(y1=b, y2=c, x1=bBar, x2=cBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) cd = line.new(y1=c, y2=d, x1=cBar, x2=dBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) array.set(wmlines, 1, ab) array.set(wmlines, 2, bc) array.set(wmlines, 3, cd) array.set(wmtype, 0, dir) if double_pattern and not wm_pattern bd = line.new(y1=b, y2=d, x1=bBar, x2=dBar, color=zigzagColor, width=zigzagWidth, style=zigzagStyle) array.set(wmlines, 5, bd) array.set(wmtype, 0, dir) if wm_pattern or abcd_pattern or double_pattern array.set(wmlabel, 0, get_harmonic_label(wmLabels, dir, d, dBar)) pattern = wm_pattern and not wm_pattern[1] or abcd_pattern and not abcd_pattern[1] or double_pattern and not double_pattern[1] pattern zigzag(zigzag1Length, zigzagpivots1, zigzagpivotbars1, zigzagpivotdirs1) zigzag(zigzag2Length, zigzagpivots2, zigzagpivotbars2, zigzagpivotdirs2) zigzag(zigzag3Length, zigzagpivots3, zigzagpivotbars3, zigzagpivotdirs3) zigzag(zigzag4Length, zigzagpivots4, zigzagpivotbars4, zigzagpivotdirs4) wm_pattern1 = detect_harmonic_pattern(zigzagpivots1, zigzagpivotbars1, zigzagpivotdirs1, wmlines1, wmLabel1, wmtype1, wmLabels1, zigzag1Color, zigzag1Width, zigzag1Style, showZigZag1) wm_pattern2 = detect_harmonic_pattern(zigzagpivots2, zigzagpivotbars2, zigzagpivotdirs2, wmlines2, wmLabel2, wmtype2, wmLabels2, zigzag2Color, zigzag2Width, zigzag2Style, showZigZag2) wm_pattern3 = detect_harmonic_pattern(zigzagpivots3, zigzagpivotbars3, zigzagpivotdirs3, wmlines3, wmLabel3, wmtype3, wmLabels3, zigzag3Color, zigzag3Width, zigzag3Style, showZigZag3) wm_pattern4 = detect_harmonic_pattern(zigzagpivots4, zigzagpivotbars4, zigzagpivotdirs4, wmlines4, wmLabel4, wmtype4, wmLabels4, zigzag4Color, zigzag4Width, zigzag4Style, showZigZag4) alertcondition(wm_pattern1 or wm_pattern2 or wm_pattern3 or wm_pattern4, title='New harmonic pattern alert', message='New harmonic pattern detected on {{ticker}}') var stats = table.new(position=position.top_right, columns=8, rows=max_pivot_size + 2, border_width=1) if barstate.islast and showStatTable if showZigZag1 table.cell(table_id=stats, column=0, row=0, text='Zigzag ' + str.tostring(zigzag1Length), bgcolor=color.black, text_color=color.white) table.cell(table_id=stats, column=0, row=1, text='Price', bgcolor=color.black, text_color=color.white) table.cell(table_id=stats, column=1, row=1, text='BarIndex', bgcolor=color.black, text_color=color.white) for i = 0 to array.size(zigzagpivots1) - 1 by 1 bgcolor = array.get(zigzagpivotdirs1, i) == 1 ? color.lime : color.orange table.cell(table_id=stats, column=0, row=i + 2, text=str.tostring(array.get(zigzagpivots1, i)), bgcolor=bgcolor) table.cell(table_id=stats, column=1, row=i + 2, text=str.tostring(array.get(zigzagpivotbars2, i)), bgcolor=bgcolor) if showZigZag2 table.cell(table_id=stats, column=2, row=0, text='Zigzag ' + str.tostring(zigzag2Length), bgcolor=color.black, text_color=color.white) table.cell(table_id=stats, column=2, row=1, text='Price', bgcolor=color.black, text_color=color.white) table.cell(table_id=stats, column=3, row=1, text='BarIndex', bgcolor=color.black, text_color=color.white) for i = 0 to array.size(zigzagpivots2) - 1 by 1 bgcolor = array.get(zigzagpivotdirs2, i) == 1 ? color.lime : color.orange table.cell(table_id=stats, column=2, row=i + 2, text=str.tostring(array.get(zigzagpivots2, i)), bgcolor=bgcolor) table.cell(table_id=stats, column=3, row=i + 2, text=str.tostring(array.get(zigzagpivotbars2, i)), bgcolor=bgcolor) if showZigZag3 table.cell(table_id=stats, column=4, row=0, text='Zigzag ' + str.tostring(zigzag3Length), bgcolor=color.black, text_color=color.white) table.cell(table_id=stats, column=4, row=1, text='Price', bgcolor=color.black, text_color=color.white) table.cell(table_id=stats, column=5, row=1, text='BarIndex', bgcolor=color.black, text_color=color.white) for i = 0 to array.size(zigzagpivots3) - 1 by 1 bgcolor = array.get(zigzagpivotdirs3, i) == 1 ? color.lime : color.orange table.cell(table_id=stats, column=4, row=i + 2, text=str.tostring(array.get(zigzagpivots3, i)), bgcolor=bgcolor) table.cell(table_id=stats, column=5, row=i + 2, text=str.tostring(array.get(zigzagpivotbars3, i)), bgcolor=bgcolor) if showZigZag4 table.cell(table_id=stats, column=6, row=0, text='Zigzag ' + str.tostring(zigzag4Length), bgcolor=color.black, text_color=color.white) table.cell(table_id=stats, column=6, row=1, text='Price', bgcolor=color.black, text_color=color.white) table.cell(table_id=stats, column=7, row=1, text='BarIndex', bgcolor=color.black, text_color=color.white) for i = 0 to array.size(zigzagpivots4) - 1 by 1 bgcolor = array.get(zigzagpivotdirs4, i) == 1 ? color.lime : color.orange table.cell(table_id=stats, column=6, row=i + 2, text=str.tostring(array.get(zigzagpivots4, i)), bgcolor=bgcolor) table.cell(table_id=stats, column=7, row=i + 2, text=str.tostring(array.get(zigzagpivotbars4, i)), bgcolor=bgcolor)