Chart Pattern
Chart Pattern
// This source code is subject to the terms of the Mozilla Public License 2.0 at
[Link]
// © reees
//@version=5
import reees/TA/85 as t
import reees/Draw/27 as draw
import reees/Utilities/5 as u
import reees/Pattern/1 as p
import reees/Obj_XABCD_Harmonic/10 as h
//import reees/Algebra/3 as alg
//-----------------------------------------
// inputs and vars
//-----------------------------------------
// Alert Inputs
//var a_on = [Link](true, "Alert", inline="alert", group="Alerts")
var a_type = [Link]("Both", "Alert for", options=["Potential
patterns","Complete patterns","Both"], inline="alert", group="Alerts")
// Display Inputs
var c_bline = [Link]([Link]([Link],20), "Bullish lines",
group="Display")
var c_beline = [Link]([Link]([Link],20), "Bearish lines",
group="Display")
var c_blab = [Link]([Link]([Link],75), "Bullish labels",
group="Display")
var c_belab = [Link]([Link]([Link],75), "Bearish labels",
group="Display")
var l_txt = [Link]([Link]([Link],20), "Label text", group="Display")
// Stat totals
var int[] pTot = array.new_int(0)
var int[] tTot = array.new_int(0)
var float[] t1Tot = array.new_float(0)
var float[] t2Tot = array.new_float(0)
var float[] arTot = array.new_float(0)
var float[] trTot = array.new_float(0)
//-----------------------------------------
// functions
//-----------------------------------------
nToArray(n) =>
switch n
"a1" => bullGart
"a2" => bullBat
"a3" => bullBfly
"a4" => bullCrab
"a5" => bullShark
"a6" => bullCyph
"b1" => bearGart
"b2" => bearBat
"b3" => bearBfly
"b4" => bearCrab
"b5" => bearShark
"b6" => bearCyph
typeToArray(t,tp) =>
n = (t ? "a" : "b") + [Link](tp)
nToArray(n)
t1(tp) =>
switch tp
6 => cyph_t1
5 => shark_t1
4 => crab_t1
3 => bfly_t1
2 => bat_t1
=> gart_t1
t2(tp) =>
switch tp
6 => cyph_t2
5 => shark_t2
4 => crab_t2
3 => bfly_t2
2 => bat_t2
=> gart_t2
// get target
targets(tp) =>
[t1(tp),t2(tp)]
harmonic_xabcd_targets(tp,xY,aY,bY,cY,dY) =>
tgt1 = t1(tp)
tgt2 = t2(tp)
[t1,t2,_] = t.harmonic_xabcd_targets(xY,aY,bY,cY,dY,tgt1,tgt2)
[t1,t2]
// Timeout period
tLimit(xX,dX) =>
int((dX - xX)*tLimitMult)
incTLimit(xX,cX) =>
avg = (cX-xX)/3
int(avg * (1 + pctAsym/100)) // time out after max possible bars based on
asymmetry parameter
// Entry has timed out
eTimeout(xX,dX) =>
bar_index - dX > int((dX - xX)*e_tLimit)
entry(p) =>
if noEntry(p)
[na,na,na]
else
t.harmonic_xabcd_entry([Link],[Link],p.x.y,p.a.y,p.b.y,p.c.y,p.d.y,e_afterC,e_lvlc,e_
afterD,e_lvldPct)
alertMsg(p) =>
if na(p.d.x)
"Potential " + h.get_name(p) + " is forming."
else
h.get_name(p) + " has formed."
deleteFip() =>
for lbl in fullIpL
[Link](lbl)
[Link](fullIpL)
//
for ln in fullIpLn
[Link](ln)
[Link](fullIpLn)
//
for lf in fullIpLf
[Link](lf)
[Link](fullIpLf)
removePending(pid) =>
if [Link](pending) > 0
for i=0 to [Link](pending)-1
p = [Link](pending,i)
if [Link] == pid
if i == [Link](pending)-1
deleteFip()
[Link](pending,i)
break
successTxt(p) =>
if p.t2Hit
" (Success - Target 1, Target 2)"
else if p.t1Hit
" (Success - Target 1)"
else if [Link]
" (Failed)"
else if noEntry(p) and stillActive(p.x.x,p.d.x)
" (No entry)"
else if ([Link]==false or na([Link])) and stillActive(p.x.x,p.d.x) == false
" (Missed entry)"
else if na([Link]) and stillActive(p.x.x,p.d.x)
" (Entry pending)"
else if na(p.t1Hit) and stillActive(p.x.x,p.d.x)
" (Targets pending)"
else
" (Timed out)"
ratToStr(r) =>
na(r) ? "NA " : [Link](r, "0.000")
reToStr(r) =>
na(r) ? "NA " : [Link](r, "0.0") + "%"
// Pattern tooltip
ttTxt(p) =>
[rb,rc,rd1,rd2] = t.harmonic_xabcd_fibDispTxt([Link])
[_,_,e] = entry(p)
l1 = h.get_name(p) + successTxt(p) + "\n\n"
l2 = (p.invalid_d?"Incomplete":"Total") + " Score: " +
[Link]([Link]*100,"#.###") + "\n"
l3 = " Leg retracement accuracy: " + [Link]((1-p.score_eAvg)*100,
"#.##") + "%\n"
l42 = " PRZ level confluence: " + ([Link]==6 ? "NA" :
([Link](p.score_prz*100, "#.##") + "%")) + "\n"
l43 = " Point D confluence with PRZ: " + (p.invalid_d?"NA (D
unconfirmed)":[Link]((1-p.score_eD)*100, "#.##") + "%") +"\n"
l5 = "\n Actual % Err Theoretical\n"
l6 = "AB/XA " + [Link](p.r_xb, "0.000") + " " + ([Link]==5 ? "NA
" : ([Link](p.re_xb*100, "00.0")+"%")) + " " + rb + "\n"
l7 = "BC/AB " + [Link](p.r_ac, "0.000") + " " +
[Link](p.re_ac*100, "00.0") + "% " + rc + "\n"
l8 = "CD/BC " + ratToStr(p.r_bd) + " " + ([Link]==6 ? "NA " :
reToStr(p.re_bd*100)) + " " + rd1 + "\n"
l9 = ([Link]==6 ? "CD/XC " : "AD/XA ") + ratToStr(p.r_xd) + " " +
reToStr(p.re_xd*100) + " " + rd2 + "\n"
l91 = "\nTarget 1: " + [Link](p.t1,"#.#####") + "\nTarget 2: " +
[Link](p.t2,"#.#####")
l92 = "\nEntry: " + [Link](na(p.e.y)?e:p.e.y,"#.#####")
l93 = na([Link]) ? "" : "\nStop: " + [Link]([Link],"#.#####")
l1 + l2 + l3 + l42 + l43 + l5 + l6 + l7 + l8 + l9 + l92 + l93 + l91
status(p) =>
if p.t2Hit
" ✅✅"
else if p.t1Hit
" ✅"
else if [Link]
" ❌"
else if stillActive(p.x.x,p.d.x) == false and ([Link] == false or na([Link]))
" ⛔"
else if stillActive(p.x.x,p.d.x) and noEntry(p)
" ⛔"
else if stillActive(p.x.x,p.d.x)
" ⏳"
else
" 🕝"
lbTxt(p,status) =>
t.harmonic_xabcd_symbol([Link]) + " " + [Link]([Link]([Link],3)*100) +
status
incLbTxt(p) =>
"Potential " + t.harmonic_xabcd_symbol([Link]) + " (" +
[Link]([Link]*100,"#.##") + ")"
erasePattern(p) =>
h.erase_pattern(p)
h.erase_label(p)
drawPattern(p) =>
if incOn or not na(p.d.x)
status = status(p)
lbTxt = not na(p.d.x) ? lbTxt(p,status) : incLbTxt(p)
[_,e,_] = entry(p)
tt = not na(p.d.x) ? ttTxt(p) :
[Link]([Link],h.get_name(p),p.r_xb,p.re_xb,p.r_ac,p.re_ac,p.prz_bN,p.prz_bF,p.
prz_xN,p.prz_xF,[Link],e)
h.draw_pattern(p, [Link]?c_bline:c_beline)
if p.invalid_d
line.set_style([Link]([Link],3),line.style_dashed)
h.draw_label(p, [Link]?c_blab:c_belab, l_txt, lbTxt, tt)
notLast(p) => // check if not same pattern as last completed pattern of same type
comp = typeToArray([Link],[Link])
h.xabcd_harmonic last = [Link](comp) == 0 ? na :
[Link](comp,[Link](comp)-1)
na(last) ? true : last.x.x!=p.x.x or last.a.x!=p.a.x or last.b.x!=p.b.x
addIncompletePattern(t,h1,h2,h3,h4,h5,h6,xX,xY,aX,aY,bX,bY,cX,cY) =>
btps = [Link](h1,h2,h3,h4,h5,h6)
tps = [Link](btps)
lowest = lowest(bar_index-cX)
highest = highest(bar_index-cX)
int dX = na
float dY = na
// check if pattern already exists (incomplete or pending)
exists = false
iN = [Link](inc)
iP = [Link](pending)
if iN > 0
for i=0 to iN-1
p = [Link](inc,iN-1-i) // more likely to find it at top of
stack
if p.x.x==xX and p.a.x==aX and p.b.x==bX
if p.c.x == cX
exists := true
else
// if new point C, delete the old inc pattern in favor of this
one
[Link](inc,iN-1-i)
erasePattern(p)
break
if exists==false and iP > 0
for i=0 to iP-1
p = [Link](pending,iP-1-i)
if p.x.x==xX and p.a.x==aX and p.b.x==bX and p.c.x==cX
exists := true
break
if exists == false
// add separate incomplete pattern for each potential harmonic type
for tpe in tps
tp = tpe+1
if exists == false
pat = [Link](xX,xY,aX,aY,bX,bY,cX,cY,dX,dY,params,tp)
if not na(pat)
[_,eC,_] = entry(pat)
if na(eC) or (not na(eC) and ((t and eC < lowest) or (t==false
and eC > highest)))
if notLast(pat)
[Link](inc,pat)
if incOn and (not na(eC) or not e_afterC)
drawPattern(pat)
if a_type == "Potential patterns" or a_type == "Both"
setTargets(p) =>
[t1,t2] = targets([Link])
h.set_target(p,1,calc_target=t1)
h.set_target(p,2,calc_target=t2)
incPid(p) =>
[Link]([Link]) + "_"
+ [Link](p.x.x) + "_"
+ [Link](p.a.x) + "_"
+ [Link](p.b.x) + "_"
+ [Link](p.c.x) + "_"
+ [Link](na)
[Link](last.x.x,last.x.y,last.a.x,last.a.y,last.b.x,last.b.y,last.c.x,last.c.y,p.d.
x,p.d.y,params,[Link],last)
[upper,lower] =
t.harmonic_xabcd_przRange(last.prz_bN,last.prz_bF,last.prz_xN,last.prz_xF)
[Link] :=
t.harmonic_xabcd_stop(stopB,stopPct,[Link],last.x.y,last.d.y,upper,lower,last.t1
,last.e.y)
drawPattern(last) // redraw with new D
setTargets(last) // reset targets for new D
pat := last
else // ELSE, replace last pattern
with the new pattern
removePending([Link])
erasePattern([Link](a))
addCompleted(p,force)
pat := p
else
addCompleted(p,force)
pat := p
updatePendingPatterns() =>
h.xabcd_harmonic[] new = [Link]<h.xabcd_harmonic>()
if [Link](pending) > 0
for i=0 to [Link](pending)-1
ip = [Link](pending,i)
[eH,eHx,eHy] = entryHit(ip)
tLimit = tLimit(ip.x.x,ip.d.x)
expired = bar_index == (ip.d.x + tLimit + 1) or eH==false
[t1h,t2h,sH,t1x,t1y,t2x,t2y] = success(ip)
// if time has expired or there's nothing left to update, no longer
pending
if not (expired or (eH and (not na(t2h) or t1h == false or sH)))
[Link](new,ip)
// if anything to update, update completed array entry and label if
necessary
if expired
or (eH
and ((not na(t1h) and na(ip.t1Hit)) or (not na(t2h) and
na(ip.t2Hit))))
or (eH != [Link]) or (eH and na([Link])) or (eH==false and
na([Link]))
or sH
if (eH or eH==false) and na([Link])
[Link] := eH
ip.e.x := eHx
ip.e.y := eHy
[Link](eHx,eHy,ip.d.x,ip.d.y,[Link])
// targets will be na if eH==false, so no need to also check eH
if t1h and na(ip.t1Hit)
ip.t1Hit := true
[Link](t1x,t1y,eHx,eHy,[Link])
else if t1h == false
ip.t1Hit := false
[Link](t1x,t1y,eHx,eHy,[Link]) // only draw stop X if
no target was already hit
if sH
[Link] := true
//[Link](bar_index,[Link],eHx,eHy,[Link])
if t2h
ip.t2Hit := true
[Link](t2x,t2y,eHx,eHy,[Link])
h.draw_label(ip, [Link]?c_blab:c_belab, l_txt,
lbTxt(ip,status(ip)), ttTxt(ip))
new
updateIncompletePatterns() =>
h.xabcd_harmonic[] new = [Link]<h.xabcd_harmonic>()
if [Link](inc) > 0
for p in inc
[upper,lower] =
t.harmonic_xabcd_przRange(p.prz_bN,p.prz_bF,p.prz_xN,p.prz_xF)
tLimit = incTLimit(p.x.x,p.c.x)
[eH,eHx,eHy] = entryHit(p)
if eH and e_afterC
if notLast(p)
p.d.x := eHx
p.d.y := eHy
p.invalid_d := true
erasePattern(p)
addValidPattern(p,true)
else
erasePattern(p)
// Don't keep incomplete pattern if it's timed out or has been
invalidated
else if bar_index == (p.c.x + tLimit + 1)
or ([Link] and (high > p.c.y or low < lower))
or ([Link]==false and (low < p.c.y or high > upper))
erasePattern(p)
else
[Link](new,p)
new
drawFullInProgress() =>
if [Link](pending) > 0
last = [Link](pending,[Link](pending)-1)
if not na(last.d.x)
bb = last_bar_index - last.d.x
tLimit = tLimit(last.x.x,last.d.x)
// Only
draw if...
if bb <= tLimit //
within pattern time limit and
and noEntry(last)==false //
entry is possible and
and (eTimeout(last.x.x,last.d.x)==false or [Link]) //
entry has not timed out and
//and (na(last.t1Hit) or (last.t1Hit and na(last.t2Hit)))
and not last.t2Hit //
targets remain to be hit and
and not [Link] // stop
has not been hit
deleteFip() // delete previously drawn completed
pattern in progress
[highest,lowest] =
t.harmonic_xabcd_przRange(last.prz_bN,last.prz_bF,last.prz_xN,last.prz_xF)
[bcNt,_] =
harmonic_xabcd_targets([Link],last.x.y,last.a.y,last.b.y,last.c.y,last.prz_bN)
[bcFt,_] =
harmonic_xabcd_targets([Link],last.x.y,last.a.y,last.b.y,last.c.y,last.prz_bF)
[xaNt,_] =
harmonic_xabcd_targets([Link],last.x.y,last.a.y,last.b.y,last.c.y,last.prz_xN)
[xaFt,_] =
harmonic_xabcd_targets([Link],last.x.y,last.a.y,last.b.y,last.c.y,last.prz_xF)
stop = [Link]
[e,_,_] = entry(last)
entry = na(last.e.y) ? e : last.e.y
[ln,lb,lf] = draw.xabcd_inProgress([Link],[Link],tLimit>500?
500:tLimit,entry,stop,last.t1,last.t2,bcNt,bcFt,xaNt,xaFt,
last.x.x,last.x.y,last.a.y,last.b.x,last.b.y,last.c.y,last.d.x,last.d.y,c_bline,c_b
eline,l_txt)
for l in ln
[Link](fullIpLn,l)
for l in lb
[Link](fullIpL,l)
for l in lf
[Link](fullIpLf,l)
else
deleteFip()
else
deleteFip()
rowValues(tp) =>
a1 = typeToArray(true,tp)
a2 = typeToArray(false,tp)
float[] ra = array.new_float(0)
t1_tot = 0
t2_tot = 0
closed = 0
if [Link](a1) > 0
for p in a1
if (not na(p.t1Hit) or [Link]) and [Link]
if p.t2Hit
r = (p.t2/p.e.y) - 1
[Link](ra,r)
t2_tot+=1
t1_tot+=1
closed+=1
else if p.t1Hit
r = (p.t1/p.e.y) - 1
[Link](ra,r)
t1_tot+=1
closed+=1
else if [Link]
r = ([Link]/p.e.y) - 1
[Link](ra,r)
closed+=1
//for p in a2
if [Link](a2) > 0
for p in a2
if (not na(p.t1Hit) or [Link]) and [Link]
if p.t2Hit
r = (p.e.y/p.t2) - 1
[Link](ra,r)
t2_tot+=1
t1_tot+=1
closed+=1
else if p.t1Hit
r = (p.e.y/p.t1) - 1
[Link](ra,r)
t1_tot+=1
closed+=1
else if [Link]
r = (p.e.y/[Link]) - 1
[Link](ra,r)
closed+=1
tot = [Link](a1) + [Link](a2)
[Link](pTot,tot)
[Link](tTot,closed)
if closed > 0
[Link](t1Tot,(t1_tot/closed)*100)
[Link](t2Tot,(t2_tot/closed)*100)
if not na([Link](ra))
[Link](arTot,[Link](ra)*100)
if not na([Link](ra))
[Link](trTot,[Link](ra)*100)
st1 = closed>0 ? [Link]((t1_tot/closed)*100,"#.##")+"%" : "NA"
st2 = closed>0 ? [Link]((t2_tot/closed)*100,"#.##")+"%" : "NA"
ravg = not na([Link](ra)) ? [Link]([Link](ra)*100,"#.##")+"%" :
"NA"
rtot = not na([Link](ra)) ? [Link]([Link](ra)*100,"#.##")+"%" :
"NA"
[[Link](tot),[Link](closed),st1,st2,ravg,rtot]
printStats() =>
if [Link]
nR = [Link](includeTps) + 3
r = 0
t = [Link](position.bottom_left, 7, nR, bgcolor =
[Link]([Link],30), border_width = 1)
[Link](t, 0, 0, " ", text_color=[Link],
text_halign=text.align_center)
[Link](t, 1, 0, "Patterns", text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 2, 0, "Trades", text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 3, 0, "T1 Success", text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 4, 0, "T2 Success", text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 5, 0, "Avg Return %", text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 6, 0, "Total Return %", text_color=[Link],
text_halign=text.align_center,text_size=[Link])
for tp in includeTps
r+=1
[tot,trd,st1,st2,ar,tr] = rowValues(tp)
[Link](t, 0, r, " "+t.harmonic_xabcd_symbol(tp)+" ",
text_color=[Link], text_halign=text.align_center,text_size=[Link])
[Link](t, 1, r, tot, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 2, r, trd, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 3, r, st1, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 4, r, st2, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 5, r, ar, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 6, r, tr, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
r+=1
t1Total = not na([Link](t1Tot)) ? ([Link]([Link](t1Tot),"#.##")
+ "%") : "NA"
t2Total = not na([Link](t2Tot)) ? ([Link]([Link](t2Tot),"#.##")
+ "%") : "NA"
arTotal = not na([Link](arTot)) ? ([Link]([Link](arTot),"#.##")
+ "%") : "NA"
trTotal = not na([Link](trTot)) ? ([Link]([Link](trTot),"#.##")
+ "%") : "NA"
[Link](t, 0, r, "Total", text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 1, r, [Link]([Link](pTot)), text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 2, r, [Link]([Link](tTot)), text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 3, r, t1Total, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 4, r, t2Total, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 5, r, arTotal, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
[Link](t, 6, r, trTotal, text_color=[Link],
text_halign=text.align_center,text_size=[Link])
r+=1
keyTxt = "✅ = Success | ❌ = Failure | 🕝 = Timed out* | ⛔ = No entry* | ⏳ =
In progress* \n*Not included in Success Rate/Return % statistics"
[Link](t, 0, r, keyTxt, text_color=[Link],
text_halign=text.align_right, text_size=[Link])
table.merge_cells(t,0,r,6,r)
[Link](pTot),[Link](tTot),[Link](t1Tot),[Link](t2Tot),[Link]
ar(arTot),[Link](trTot)
validD(p,dX,dY) =>
// If CD has valid length symmetry...
if dX <= (incTLimit(p.x.x,p.c.x) + p.c.x)
if t.pat_xabcd_testSym(p.a.x-p.x.x, p.b.x-p.a.x, p.c.x-p.b.x, p.d.x-p.c.x,
pctAsym)
highest = highest(bar_index-p.c.x,1)
lowest = lowest(bar_index-p.c.x,1)
// If no intermediate high/low invalidates the CD leg...
if (([Link] and dY <= lowest) or ([Link]==false and dY >= highest))
and (([Link] and p.c.y >= highest) or ([Link]==false and p.c.y <=
lowest))
// validate CD retracement for this pattern type
xa = [Link](p.x.y - p.a.y)
bc = [Link](p.b.y - p.c.y)
cd = [Link](p.c.y - dY)
ad = [Link](p.a.y - dY)
xc = [Link](p.x.y - p.c.y)
tp = [Link]
p_types = switch tp
1 => [Link](true,false,false,false,false,false)
2 => [Link](false,true,false,false,false,false)
3 => [Link](false,false,true,false,false,false)
4 => [Link](false,false,false,true,false,false)
5 => [Link](false,false,false,false,true,false)
6 => [Link](false,false,false,false,false,true)
[t1,t2,t3,t4,t5,t6] = t.test_cd(cd,bc,xa,xc,ad,pctErr,p_types)
t1 or t2 or t3 or t4 or t5 or t6
else
false
else
false
else
false
// Check for a valid pivot point D based on the pattern confirmation length
parameter (i.e. checking bar_index[t_b])
checkForValidD() =>
x = bar_index[t_b] // bar of interest
bool isLow = true
bool isHigh = true
l = low
h = high
// Validate pivot
for i=0 to t_b-1 // check bars after potential pivot point
if l[i] < l[t_b]
isLow := false
break
if isLow // check bars before potential pivot point
for i=t_b+1 to t_b+3 // need at least 3 bars before
if l[i] < l[t_b]
isLow := false
break
for n in new
if not na(n)
addValidPattern(n)
//-----------------------------------------
// Main
//-----------------------------------------
checkForValidD()