You are on page 1of 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