// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © goodgains
//@version=5
indicator("GARY - Patterns", max_lines_count=500, max_labels_count=500, overlay=true, max_bars_back=2000)
// @keywords: wedge-patterns, zigzag-pivots, long-term-wedges, medium-term-wedges, short-term-wedges, pivot-high-low, trendline-wedges, pattern-detection, angle-analysis, ratio-wedges, type1-wedge, type2-wedge, overlap-avoidance, five-pivot, chart-patterns, price-swings, breakout-wedges, pattern-lines, multi-lookback, gary-patterns
//--PATTERN INPUTS--//
show_longterm_patterns = input.bool(true, title = 'Long-term Patterns ', group = 'Pattern Detection Settings', inline = '1')
css_longterm = input.color(color.yellow, title = '', group = 'Pattern Detection Settings', inline = '1')
style_longterm = input.string('Solid', title = '', options = ['Solid', 'Dotted', 'Dashed'], group = 'Pattern Detection Settings', inline = '1')
width_longterm = input.int(2, title = '', group = 'Pattern Detection Settings', inline = '1', minval = 1, maxval = 5)
show_mediumterm_patterns = input.bool(true, title = 'Medium-term Patterns', group = 'Pattern Detection Settings', inline = '2')
css_middleterm = input.color(color.orange, title = '', group = 'Pattern Detection Settings', inline = '2')
style_middleterm = input.string('Solid', title = '', options = ['Solid', 'Dotted', 'Dashed'], group = 'Pattern Detection Settings', inline = '2')
width_middleterm = input.int(2, title = '', group = 'Pattern Detection Settings', inline = '2', minval = 1, maxval = 5)
show_shortterm_patterns = input.bool(false, title = 'Short-term Patterns ', group = 'Pattern Detection Settings', inline = '3')
css_shortterm = input.color(color.blue, title = '', group = 'Pattern Detection Settings', inline = '3')
style_shortterm = input.string('Solid', title = '', options = ['Solid', 'Dotted', 'Dashed'], group = 'Pattern Detection Settings', inline = '3')
width_shortterm = input.int(1, title = '', group = 'Pattern Detection Settings', inline = '3', minval = 1, maxval = 5)
//--PATTERN INPUTS--//
//--GLOBAL VARS--//
avoidOverlap = true
lookback_longterm = 50
lookback_middleterm = 22
lookback_shortterm = 8
maxPatternsReference = 1
var aBarArray = array.new_int()
var bBarArray = array.new_int()
var cBarArray = array.new_int()
var dBarArray = array.new_int()
var eBarArray = array.new_int()
var fBarArray = array.new_int()
var xBarArray = array.new_int()
drawZigzag = false
//--GLOBAL VARS--//
//-----FUNCTIONS-----//
//--FUNCTION TO ADD NEW AND REMOVE LAST IN ARRAY--//
f_unshift_to_array(array, value, max_items) =>
array.insert(array, 0, value)
if(array.size(array) > max_items)
array.pop(array)
array
//--FUNCTION TO ADD NEW AND REMOVE LAST IN ARRAY--//
//--FUNCTION TO ADD NEW IN ARRAY--//
f_add_to_array(array, value, max_items) =>
array.insert(array, 0, value)
if array.size(array) > max_items
array.pop(array)
array
//--FUNCTION TO ADD NEW IN ARRAY--//
//--FUNCTION TO GET ANGLE OF TWO POINTS--//
f_angle(a, b, loopback) =>
rad2degree = 180 / math.pi
ang = rad2degree * math.atan((a - b)/(2*math.sum(ta.tr, loopback+1)/(loopback+1)))
ang
//--FUNCTION TO GET ANGLE OF TWO POINTS--//
//--FUNCTION TO GET PIVOTS--//
f_pivots(length, highSource=high, lowSource = low) =>
float phigh = ta.highestbars(highSource, length) == 0 ? highSource : na
float plow = ta.lowestbars(lowSource, length) == 0 ? lowSource : na
[phigh, plow, bar_index, bar_index]
//--FUNCTION TO GET PIVOTS--//
//--FUNCTION TO GET SENTIMENT MIGHT NOT NEED LOOKS TO BE USED FOR DIVERGENCE--//
f_getSentiment(pDir, oDir, sDir) =>
sentiment = pDir == oDir ? sDir == pDir or sDir * 2 == -pDir ? -sDir : sDir * 4 : sDir == pDir or sDir == -oDir ? 0 : (math.abs(oDir) > math.abs(pDir) ? sDir : -sDir) * (sDir == oDir ? 2 : 3)
sentiment
//--FUNCTION TO GET SENTIMENT MIGHT NOT NEED LOOKS TO BE USED FOR DIVERGENCE--//
f_addnewpivot(zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, zigzagoscillators,
zigzagoscillatordirs, zigzagtrendbias, zigzagdivergence, zigzagdoubledivergence,
value, bar, dir, numberOfPivots,
oscHigh, oscLow, directionBias, divergenceOption) =>
newDir = dir
oscDir = dir
lPDir = 0
lODir = 0
ratio = 1.0
oscillatorSource = dir > 0? oscHigh[bar_index - bar] : oscLow[bar_index - bar]
if array.size(zigzagpivots) >= 2
lastPoint = array.get(zigzagpivots, 0)
llastPoint = array.get(zigzagpivots, 1)
newDir := dir * value > dir * llastPoint ? dir * 2 : dir
ratio := math.round(math.abs(value-lastPoint)/math.abs(llastPoint-lastPoint), 2)
llastOsc = array.get(zigzagoscillators, 1)
oscDir := dir * oscillatorSource > dir * llastOsc ? dir * 2 : dir
if array.size(zigzagpivots) >=4
lllastPoint = array.get(zigzagpivots, 3)
lllastOsc = array.get(zigzagoscillators, 3)
lODir := dir * llastOsc > dir * lllastOsc ? dir * 2 : dir
lPDir := dir * llastPoint > dir * lllastPoint ? dir * 2 : dir
sentiment = f_getSentiment(newDir, oscDir, directionBias[bar_index-bar])
llastSentiment = array.size(zigzagdivergence) >=2 ? array.get(zigzagdivergence, 1): na
useHiddenDoubleD = divergenceOption == 1 or divergenceOption == 3
useRegularDoubleD = divergenceOption == 1 or divergenceOption == 2
doubleDivergence = (sentiment == llastSentiment and ((math.abs(sentiment) == 3 and useHiddenDoubleD) or (math.abs(sentiment) == 2 and useRegularDoubleD))) ? sentiment/math.abs(sentiment) : 0
f_add_to_array(zigzagpivots, value, numberOfPivots)
f_add_to_array(zigzagpivotbars, bar, numberOfPivots)
f_add_to_array(zigzagpivotdirs, newDir, numberOfPivots)
f_add_to_array(zigzagpivotratios, ratio, numberOfPivots)
f_add_to_array(zigzagoscillators, oscillatorSource, numberOfPivots)
f_add_to_array(zigzagoscillatordirs, oscDir, numberOfPivots)
f_add_to_array(zigzagtrendbias, directionBias[bar_index-bar], numberOfPivots)
f_add_to_array(zigzagdivergence, sentiment, numberOfPivots)
f_add_to_array(zigzagdoubledivergence, doubleDivergence, numberOfPivots)
f_zigzagcore(phigh, plow, phighbar, plowbar, numberOfPivots, oscHigh, oscLow, directionBias,
zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, zigzagoscillators,
zigzagoscillatordirs, zigzagtrendbias, zigzagdivergence, zigzagdoubledivergence, divergenceOption) =>
pDir = 1
newZG = false
doubleZG = phigh and plow
if array.size(zigzagpivots) >= 1
pDir := array.get(zigzagpivotdirs, 0)
pDir := pDir % 2 == 0 ? pDir / 2 : pDir
pDir
if (pDir == 1 and phigh or pDir == -1 and plow) and array.size(zigzagpivots) >= 1
pivot = array.remove(zigzagpivots,0)
pivotbar = array.remove(zigzagpivotbars,0)
pivotdir = array.remove(zigzagpivotdirs,0)
array.remove(zigzagpivotratios, 0)
array.remove(zigzagoscillators, 0)
array.remove(zigzagoscillatordirs, 0)
array.remove(zigzagtrendbias, 0)
array.remove(zigzagdivergence, 0)
array.remove(zigzagdoubledivergence, 0)
value = pDir == 1 ? phigh : plow
bar = pDir == 1 ? phighbar : plowbar
useNewValues = value * pivotdir > pivot * pivotdir
value := useNewValues ? value : pivot
bar := useNewValues ? bar : pivotbar
newZG := newZG or useNewValues
f_addnewpivot(zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, zigzagoscillators,
zigzagoscillatordirs, zigzagtrendbias, zigzagdivergence, zigzagdoubledivergence, value, bar, pDir, numberOfPivots,
oscHigh, oscLow, directionBias, divergenceOption)
if pDir == 1 and plow or pDir == -1 and phigh
value = pDir == 1 ? plow : phigh
bar = pDir == 1 ? plowbar : phighbar
dir = pDir == 1 ? -1 : 1
newZG := true
f_addnewpivot(zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, zigzagoscillators,
zigzagoscillatordirs, zigzagtrendbias, zigzagdivergence, zigzagdoubledivergence, value, bar, dir, numberOfPivots,
oscHigh, oscLow, directionBias, divergenceOption)
[newZG, doubleZG]
f_zigzag(int length, simple int numberOfPivots = 20, float highSource = high, float lowSource = low, float oscillatorHighSource = high, float oscillatorLowSource = low, int directionBias = 1, int divergenceOption=1) =>
var zigzagpivots = array.new_float(0)
var zigzagpivotbars = array.new_int(0)
var zigzagpivotdirs = array.new_int(0)
var zigzagpivotratios = array.new_float(0)
var zigzagoscillators = array.new_float(0)
var zigzagoscillatordirs = array.new_int(0)
var zigzagtrendbias = array.new_int(0)
var zigzagdivergence = array.new_int(0)
var zigzagdoubledivergence = array.new_int(0)
[phigh, plow, phighbar, plowbar] = f_pivots(length, highSource, lowSource)
[newPivot, doublePivot] = f_zigzagcore(phigh, plow, phighbar, plowbar, numberOfPivots,
oscillatorHighSource, oscillatorLowSource, directionBias,
zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, zigzagoscillators,
zigzagoscillatordirs, zigzagtrendbias, zigzagdivergence, zigzagdoubledivergence, divergenceOption)
[zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, zigzagoscillators,
zigzagoscillatordirs, zigzagtrendbias, zigzagdivergence, zigzagdoubledivergence,
newPivot, doublePivot]
f_add_new_wedge(a,b,c,d,e,f,aBar,bBar,cBar,dBar,eBar,fBar,l1Angle, l2Angle)=>
dir = a>b? 1: -1
f_unshift_to_array(fBarArray, fBar, maxPatternsReference)
f_unshift_to_array(aBarArray, aBar, maxPatternsReference)
f_unshift_to_array(bBarArray, bBar, maxPatternsReference)
f_unshift_to_array(cBarArray, cBar, maxPatternsReference)
f_unshift_to_array(dBarArray, dBar, maxPatternsReference)
f_unshift_to_array(eBarArray, eBar, maxPatternsReference)
if(drawZigzag)
ab = line.new(bBar, b, aBar, a)
bc = line.new(cBar, c, bBar, b)
cd = line.new(dBar, d, cBar, c)
de = line.new(eBar, e, dBar, d)
aLbl = label.new(aBar, a, '5', style=label.style_none, yloc=dir>0?yloc.abovebar:yloc.belowbar)
bLbl = label.new(bBar, b, '4', style=label.style_none, yloc=dir<0?yloc.abovebar:yloc.belowbar)
cLbl = label.new(cBar, c, '3', style=label.style_none, yloc=dir>0?yloc.abovebar:yloc.belowbar)
dLbl = label.new(dBar, d, '2', style=label.style_none, yloc=dir<0?yloc.abovebar:yloc.belowbar)
eLbl = label.new(eBar, e, '1', style=label.style_none, yloc=dir>0?yloc.abovebar:yloc.belowbar)
f_draw_wedge(l1StartX, l1StartY, l1EndX, l1EndY, l2StartX, l2StartY, l2EndX, l2EndY, css, linestyle, linewidth)=>
l1t = line.new(l1StartX, l1StartY, l1EndX, l1EndY, extend=extend.both)
l2t = line.new(l2StartX, l2StartY, l2EndX, l2EndY, extend=extend.both)
startBar = math.min(l2StartX, l1StartX)
endBar = l1EndX
l1Start = line.get_price(l1t, startBar)
l1End = line.get_price(l1t, endBar)
l2Start = line.get_price(l2t,startBar)
l2End = line.get_price(l2t, endBar)
line.set_extend(l1t, extend.none)
line.set_extend(l2t, extend.none)
line.set_x1(l1t, startBar)
line.set_y1(l1t, l1Start)
line.set_x2(l1t, endBar)
line.set_y2(l1t, l1End)
line.set_x1(l2t, startBar)
line.set_y1(l2t, l2Start)
line.set_x2(l2t, endBar)
line.set_y2(l2t, l2End)
l1Angle = f_angle(l1End, l1Start, endBar-startBar)
l2Angle = f_angle(l2End, l2Start, endBar-startBar)
l1Diff = math.abs(l1Start-l1End)
l2Diff = math.abs(l2Start-l2End)
// css
line.set_color(l1t, css)
line.set_color(l2t, css)
line.set_width(l1t, linewidth)
line.set_width(l2t, linewidth)
line.set_style(l1t, (linestyle == 'Solid' ? line.style_solid : linestyle == 'Dashed' ? line.style_dashed : linestyle == 'Dotted' ? line.style_dotted : line.style_solid) )
line.set_style(l2t, (linestyle == 'Solid' ? line.style_solid : linestyle == 'Dashed' ? line.style_dashed : linestyle == 'Dotted' ? line.style_dotted : line.style_solid) )
[l1t, l2t, l1Angle, l2Angle, l1Diff, l2Diff]
f_find_wedge(a,b,c,d,e,f,aBar,bBar,cBar,dBar,eBar,fBar,zigzagpivots, zigzagpivotbars, fIndex, css, linestyle, linewidth) =>
existingPattern = false
lastPivot = e
lastPivotBar = eBar
llastPivot = d
for i=0 to array.size(aBarArray)==0? na: array.size(aBarArray)-1
commonPivots = (array.get(aBarArray, i) == aBar ? 1 : 0) +
(array.get(bBarArray, i) == bBar ? 1 : 0) +
(array.get(cBarArray, i) == cBar ? 1 : 0) +
(array.get(dBarArray, i) == dBar ? 1 : 0) +
(array.get(eBarArray, i) == eBar ? 1 : 0)
if(commonPivots >=2) or (avoidOverlap and lastPivotBar < array.get(aBarArray,i) and lastPivotBar > array.get(fBarArray, i))
existingPattern := true
break
if(not existingPattern)
aRatio = math.abs(a-b)/math.abs(b-c)
bRatio = math.abs(b-c)/math.abs(c-d)
cRatio = math.abs(c-d)/math.abs(d-e)
dRatio = math.abs(d-e)/math.abs(e-f)
[l1t, l2t, l1Angle, l2Angle, l1Diff, l2Diff] = f_draw_wedge(eBar, e, aBar, a, dBar, d, bBar, b, css, linestyle, linewidth)
isType1Wedge = aRatio >=1 and bRatio < 1 and cRatio >= 1 and l1Diff < l2Diff
isType2Wedge = aRatio <1 and bRatio >= 1 and cRatio < 1 and l1Diff > l2Diff
angleDiff = math.abs(l1Angle-l2Angle)
isWedge = (isType1Wedge or isType2Wedge)
if(isWedge)
for i = aBar to lastPivotBar
l = low[bar_index-i]
h = high[bar_index-i]
l1Price = line.get_price(l1t, i)
l2Price = line.get_price(l2t, i)
if(h < math.min(l1Price, l2Price) or l > math.max(l1Price, l2Price))
isWedge := false
break
if(i == cBar and (l1Price > h or l1Price < l))
isWedge := false
break
if(i == dBar and (l2Price > h or l2Price < l))
isWedge := false
break
if isWedge
wedgeType = isType1Wedge? 1 : 2
f_add_new_wedge(a,b,c,d,e,f,aBar,bBar,cBar,dBar,eBar,fBar,l1Angle, l2Angle)
true
else
line.delete(l1t)
line.delete(l2t)
false
f_scan_patterns(startIndex, newPivot, zigzagpivots, zigzagpivotbars, zigzagpivotratios, zigzagpivotdirs, lastABar, css, linestyle, linewidth) =>
length = array.size(zigzagpivots)
numberOfPivots=5
newLastABar = lastABar
if(length >= startIndex+numberOfPivots+1 and newPivot)
a = array.get(zigzagpivots, startIndex)
aBar = array.get(zigzagpivotbars, startIndex)
aIndex = startIndex
lastDir = array.get(zigzagpivotdirs, startIndex)
if(aBar!=lastABar)
newLastABar := aBar
b = array.get(zigzagpivots, startIndex+1)
bBar = array.get(zigzagpivotbars, startIndex+1)
c = array.get(zigzagpivots, startIndex+2)
cBar = array.get(zigzagpivotbars, startIndex+2)
d = array.get(zigzagpivots, startIndex+3)
dBar = array.get(zigzagpivotbars, startIndex+3)
e = array.get(zigzagpivots, startIndex+4)
eBar = array.get(zigzagpivotbars, startIndex+4)
f = e
fBar = eBar
f_find_wedge(a,b,c,d,e,f,aBar,bBar,cBar,dBar,eBar,fBar,zigzagpivots, zigzagpivotbars, startIndex+5-1, css, linestyle, linewidth)
newLastABar
//-----FUNCTIONS-----//
// css_longterm = input.color(color.blue, title = '', group = 'Pattern Detection Settings', inline = '1')
// style_longterm = input.string('Solid', title = '', options = ['Solid', 'Dotted', 'Dashed'], group = 'Pattern Detection Settings', inline = '1')
// width_longterm = input.int(3, title = '', group = 'Pattern Detection Settings', inline = '1')
startIndex = 0
if(show_longterm_patterns)
[zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, _, _, _, _, _, newPivot, doublePivot] = f_zigzag(lookback_longterm)
var lastABar = 0
lastABar := f_scan_patterns(startIndex+1, doublePivot, zigzagpivots, zigzagpivotbars, zigzagpivotratios, zigzagpivotdirs, lastABar, css_longterm, style_longterm, width_longterm)
lastABar := f_scan_patterns(startIndex, newPivot, zigzagpivots, zigzagpivotbars, zigzagpivotratios, zigzagpivotdirs, lastABar, css_longterm, style_longterm, width_longterm)
if(show_mediumterm_patterns)
[zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, _, _, _, _, _, newPivot, doublePivot] = f_zigzag(lookback_middleterm)
var lastABar = 0
lastABar := f_scan_patterns(startIndex+1, doublePivot, zigzagpivots, zigzagpivotbars, zigzagpivotratios, zigzagpivotdirs, lastABar, css_middleterm, style_middleterm, width_middleterm)
lastABar := f_scan_patterns(startIndex, newPivot, zigzagpivots, zigzagpivotbars, zigzagpivotratios, zigzagpivotdirs, lastABar, css_middleterm, style_middleterm, width_middleterm)
if(show_shortterm_patterns)
[zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, _, _, _, _, _, newPivot, doublePivot] = f_zigzag(lookback_shortterm)
var lastABar = 0
lastABar := f_scan_patterns(startIndex+1, doublePivot, zigzagpivots, zigzagpivotbars, zigzagpivotratios, zigzagpivotdirs, lastABar, css_shortterm, style_shortterm, width_shortterm)
lastABar := f_scan_patterns(startIndex, newPivot, zigzagpivots, zigzagpivotbars, zigzagpivotratios, zigzagpivotdirs, lastABar, css_shortterm, style_shortterm, width_shortterm)
Show all 373 lines