0% found this document useful (0 votes)
1K views13 pages

Zigzag Harmonic Patterns

This document contains source code for an indicator that detects multi-level zigzag harmonic patterns in price data. It defines inputs for configuring different zigzag levels and harmonic patterns to detect. Arrays are used to store pivot points, bars, and directions for each zigzag level. Functions are defined to calculate pivots, detect pattern matches, and draw the zigzag lines and labels on a chart.

Uploaded by

Viswanath Palyam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1K views13 pages

Zigzag Harmonic Patterns

This document contains source code for an indicator that detects multi-level zigzag harmonic patterns in price data. It defines inputs for configuring different zigzag levels and harmonic patterns to detect. Arrays are used to store pivot points, bars, and directions for each zigzag level. Functions are defined to calculate pivots, detect pattern matches, and draw the zigzag lines and labels on a chart.

Uploaded by

Viswanath Palyam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 13

// 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 Level ZigZag Harmonic Patterns', shorttitle='ML - ZigZag - HP',
overlay=true, max_bars_back=1000, max_lines_count=500, max_labels_count=500)

max_array_size = input.int(200, step=20)


useZigZagChain = input(false)
showZigZag1 = input(true)
zigzag1Length = input.int(10, step=5, minval=3)
zigzag1Color = input(color.teal)
zigzag1Width = 1
zigzag1Style = line.style_solid

showZigZag2 = input(true)
zigzag2Length = input.int(2, step=1, minval=2)
zigzag2Color = input(color.olive)
zigzag2Width = 1
zigzag2Style = line.style_solid

showZigZag3 = input(true)
zigzag3Length = input.int(3, step=1, minval=2)
zigzag3Color = input(color.lime)
zigzag3Width = 1
zigzag3Style = line.style_solid

showZigZag4 = input(true)
zigzag4Length = input.int(4, step=1, minval=2)
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)

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, bar_index, bar_index]

outerpivots(length, zigzagpivots, zigzagpivotbars) =>


zigzagminimal = array.slice(zigzagpivots, 0, length - 1)
lastPivot = array.get(zigzagpivots, 0)
lastPivotBar = array.get(zigzagpivotbars, 0)
highestPivot = array.max(zigzagminimal)
lowestPivot = array.min(zigzagminimal)
float phigh = lastPivot == highestPivot ? lastPivot : na
float plow = lastPivot == lowestPivot ? lastPivot : na
int phighbar = lastPivot == highestPivot ? lastPivotBar : na
int plowbar = lastPivot == lowestPivot ? lastPivotBar : na
zdir = 0
iff_1 = plow and na(phigh) ? -1 : zdir[1]
zdir := phigh and na(plow) ? 1 : iff_1
[zdir, phigh, plow, phighbar, plowbar]
zigzagcore(dir, phigh, plow, phighbar, plowbar, zigzagpivots, zigzagpivotbars,
zigzagpivotdirs) =>
dirchanged = ta.change(dir)
newZG = false
if phigh or plow
value = dir == 1 ? phigh : plow
bar = phigh ? phighbar : plowbar
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)
newZG := true
if array.size(zigzagpivots) > max_array_size
array.pop(zigzagpivots)
array.pop(zigzagpivotbars)
array.pop(zigzagpivotdirs)
newZG

zigzag(length, zigzagpivots, zigzagpivotbars, zigzagpivotdirs) =>


[dir, phigh, plow, phighbar, plowbar] = pivots(length)
zigzagcore(dir, phigh, plow, phighbar, plowbar, zigzagpivots, zigzagpivotbars,
zigzagpivotdirs)

outerzigzag(outerzigzagLength, zigzagpivots, zigzagpivotbars, outerzigzagpivots,


outerzigzagpivotbars, outerzigzagpivotdirs) =>
newOuterZG = false
if array.size(zigzagpivots) >= outerzigzagLength * 2
[zdir, phigh, plow, phighbar, plowbar] = outerpivots(outerzigzagLength * 2,
zigzagpivots, zigzagpivotbars)
newOuterZG := zigzagcore(zdir, phigh, plow, phighbar, plowbar,
outerzigzagpivots, outerzigzagpivotbars, outerzigzagpivotdirs)
newOuterZG
newOuterZG

draw_zigzag(zigzaglines, zigzagpivots, zigzagpivotbars, zigzagcolor, zigzagwidth,


zigzagstyle, showZigZag) =>
if array.size(zigzagpivots) >= 2 and showZigZag
y1 = array.get(zigzagpivots, 0)
y2 = array.get(zigzagpivots, 1)
x1 = array.get(zigzagpivotbars, 0)
x2 = array.get(zigzagpivotbars, 1)

zline = line.new(x1=x1, y1=y1, x2=x2, y2=y2, color=zigzagcolor,


width=zigzagwidth, style=zigzagstyle)
if array.size(zigzaglines) >= 1
lastLine = array.get(zigzaglines, 0)
if x2 == line.get_x2(lastLine) and y2 == line.get_y2(lastLine)
line.delete(lastLine)
array.unshift(zigzaglines, zline)

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

level1ZG = zigzag(zigzag1Length, zigzagpivots1, zigzagpivotbars1, zigzagpivotdirs1)


level2ZG = outerzigzag(zigzag2Length, zigzagpivots1, zigzagpivotbars1,
zigzagpivots2, zigzagpivotbars2, zigzagpivotdirs2)
level3ZG = outerzigzag(useZigZagChain ? zigzag3Length : zigzag2Length +
zigzag3Length, useZigZagChain ? zigzagpivots2 : zigzagpivots1, useZigZagChain ?
zigzagpivotbars2 : zigzagpivotbars1, zigzagpivots3, zigzagpivotbars3,
zigzagpivotdirs3)
level4ZG = outerzigzag(useZigZagChain ? zigzag4Length : zigzag2Length +
zigzag3Length + zigzag4Length, useZigZagChain ? zigzagpivots3 : zigzagpivots1,
useZigZagChain ? zigzagpivotbars3 : zigzagpivotbars1, 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}}')

//end of this part

//@version=5
// This source code is subject to the terms of the Mozilla Public License 2.0 at
https://mozilla.org/MPL/2.0/
// � ZenAndTheArtOfTrading / Pine Script Mastery Course
// Dynamic Structure Indicator v2.0
// Last Updated: 8th January, 2021
//indicator('Dynamic Structure Indicator v2.0', shorttitle='DSI', overlay=true)

// Get user input


atrMovement = input(title='ATR Movement Required', defval=1.0)
lookback = input.int(title='High/Low Lookback', defval=25, step=5)
maxZoneSize = input.float(title='Max Zone Size (Compared to ATR)', defval=2.5,
step=0.5)
newStructureReset = input.int(title='Zone Update Count Before Reset', defval=25,
step=5)
drawPreviousStructure = input(title='Draw Previous Structure', defval=true)

// Get current ATR value


atr = ta.atr(14)

// Get highest body and lowest body for the current candle
highestBody = open > close ? open : close
lowestBody = open > close ? close : open

// Set up our persistent S&R variables (1 = the wick and 2 = the body)
var res1 = 0.0
var res2 = 0.0
var sup1 = 0.0
var sup2 = 0.0
var lookForNewResistance = true
var lookForNewSupport = true

// Set up our *previous* support & resistance variables (for drawing support-
turned-resistance etc)
var previousRes1 = 0.0
var previousRes2 = 0.0
var previousSup1 = 0.0
var previousSup2 = 0.0

// Set up our ATR variables (for identifying significant declines/rallies to


validate S&R zones)
var atrSaved = 0.0
var potentialR1 = 0.0
var potentialR2 = 0.0
var potentialS1 = 0.0
var potentialS2 = 0.0

// Detect fractal swing highs for resistance


// We're looking for this pattern: .|.
if high[1] == ta.highest(high, lookback) and high < high[1] and
lookForNewResistance
r1 = high[1]
r2 = highestBody[2] > highestBody[1] ? highestBody[2] : highestBody >
highestBody[1] ? highestBody : highestBody[1]
if (r1 - r2) / atr <= maxZoneSize
lookForNewResistance := false
potentialR1 := r1
potentialR2 := r2
atrSaved := atr
atrSaved

// Detect fractal swing lows for support


// We're looking for this pattern: *|*
if low[1] == ta.lowest(low, lookback) and low > low[1] and lookForNewSupport
s1 = low[1]
s2 = lowestBody[2] < lowestBody[1] ? lowestBody[2] : lowestBody < lowestBody[1]
? lowestBody : lowestBody[1]
if (s2 - s1) / atr <= maxZoneSize
lookForNewSupport := false
potentialS1 := s1
potentialS2 := s2
atrSaved := atr
atrSaved

// Check if potential resistance zone has already been violated. If it has, reset
our potential R1 & R2
if close > potentialR1 and barstate.isconfirmed
potentialR1 := na
potentialR2 := na
potentialR2

// Check if potential support zone has already been violated. If it has, reset our
potential S1 & S2
if close < potentialS1 and barstate.isconfirmed
potentialS1 := na
potentialS2 := na
potentialS2

// Check if we've had a significant decline since detecting swing high


if potentialR1 - low >= atrSaved * atrMovement
previousRes1 := na(previousRes1) ? potentialR1 : previousRes1 // Store
previous resistance if we're not already drawing it
previousRes2 := na(previousRes2) ? potentialR2 : previousRes2
res1 := potentialR1
res2 := potentialR2
potentialR1 := na
potentialR2 := na
potentialR2

// Check if we've had a significant rally since detecting swing low


if high - potentialS1 >= atrSaved * atrMovement
previousSup1 := na(previousSup1) ? potentialS1 : previousSup1 // Store
previous support if we're not already drawing it
previousSup2 := na(previousSup2) ? potentialS2 : previousSup2
sup1 := potentialS1
sup2 := potentialS2
potentialS1 := na
potentialS2 := na
potentialS2

// Declare support & resistance update counters


// This is used for forcing a zone reset if a zone is not violated within a
reasonable period of time
var supCount = 0
var resCount = 0

// If the previous resistance high has been violated then begin searching for a new
resistance zone
if close >= res1 and barstate.isconfirmed
lookForNewResistance := true
lookForNewSupport := true
resCount += 1
resCount

// If the previous support low has been violated then begin searching for a new
support zone
if close <= sup1 and barstate.isconfirmed
lookForNewSupport := true
lookForNewResistance := true
supCount += 1
supCount

// If our current resistance zone has been violated, store its values to draw new
*potential* support zone
// The idea being that once a major resistance zone is violated it often becomes
future support
// But we only save previous S&R if we don't already have one saved (or our zone
update count exceeds newStructureReset)
if close > res1 and na(previousRes1) and barstate.isconfirmed or previousRes1 ==
0.0 or supCount >= newStructureReset
previousRes1 := res1
previousRes2 := res2
supCount := 0
supCount

// If our current support zone has been violated, store its values to draw new
*potential* resistance zone
// The idea being that once a major support zone is violated it often becomes
future resistance
// But we only save previous S&R if we don't already have one saved (or our zone
update count exceeds newStructureReset)
if close < sup1 and na(previousSup1) and barstate.isconfirmed or previousSup1 ==
0.0 or resCount >= newStructureReset
previousSup1 := sup1
previousSup2 := sup2
resCount := 0
resCount

// If our resistance-turned-support zone has been violated, reset our saved


resistance variables
if close < previousRes2 and barstate.isconfirmed
previousRes1 := na
previousRes2 := na
previousRes2

// If our support-turned-resistance zone has been violated, reset our saved support
variables
if close > previousSup2 and barstate.isconfirmed
previousSup1 := na
previousSup2 := na
previousSup2

// Draw our current resistance zone


r1 = plot(res1 == res1[1] ? res1 : na, color=close >= res1 ? color.green :
color.red, style=plot.style_linebr, title='R1')
r2 = plot(res1 == res1[1] ? res2 : na, color=close >= res1 ? color.green :
color.red, style=plot.style_linebr, title='R2')
fill(r1, r2, color=close > res1 ? color.green : color.red, title='Resistance Zone',
transp=50)

// Draw our current support zone


s1 = plot(sup1 == sup1[1] ? sup1 : na, color=close < sup1 ? color.red :
color.green, style=plot.style_linebr, title='S1')
s2 = plot(sup1 == sup1[1] ? sup2 : na, color=close < sup1 ? color.red :
color.green, style=plot.style_linebr, title='S2')
fill(s1, s2, color=close < sup1 ? color.red : color.green, title='Support Zone',
transp=50)

// Draw our previous support zone (turned potential resistance)


ps1 = plot(previousSup1 == previousSup1[1] and previousSup1 != sup1 and
drawPreviousStructure ? previousSup1 : na, color=color.new(color.red, 0),
style=plot.style_linebr, title='PS1')
ps2 = plot(previousSup1 == previousSup1[1] and previousSup1 != sup1 and
drawPreviousStructure ? previousSup2 : na, color=color.new(color.red, 0),
style=plot.style_linebr, title='PS2')
fill(ps1, ps2, color=color.new(color.red, 10), title='Previous Support Zone')

// Draw our previous resistance zone (turned potential support)


pr1 = plot(previousRes1 == previousRes1[1] and previousRes1 != res1 and
drawPreviousStructure ? previousRes1 : na, color=color.new(color.green, 0),
style=plot.style_linebr, title='PR1')
pr2 = plot(previousRes1 == previousRes1[1] and previousRes1 != res1 and
drawPreviousStructure ? previousRes2 : na, color=color.new(color.green, 0),
style=plot.style_linebr, title='PR2')
fill(pr1, pr2, color=color.new(color.green, 10), title='Previous Resistance Zone')

// Check alert conditions


alertResistance = high >= res2
alertSupport = low <= sup2
alertResistanceBO = close >= res1
alertSupportBO = close <= sup1

// Trigger alerts
alertcondition(alertResistance or alertSupport or alertResistanceBO or
alertSupportBO, title='[DSI] Alert!', message='DSI alert for {{ticker}}')
alertcondition(alertResistance, title='[DSI] Resistance Alert', message='Price has
entered current potential resistance zone for {{ticker}}')
alertcondition(alertSupport, title='[DSI] Support Alert', message='Price has
entered current potential support zone for {{ticker}}')
alertcondition(alertResistanceBO, title='[DSI] Resistance Breakout', message='Price
has broken past potential resistance zone for {{ticker}}')
alertcondition(alertSupportBO, title='[DSI] Support Breakout', message='Price has
broken past potential support zone for {{ticker}}')

You might also like