Hurst Diamond Notation Pivots
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © BarefootJoey

// ██████████████████████████████████████████████████████████████████████
// █▄─▄─▀██▀▄─██▄─▄▄▀█▄─▄▄─█▄─▄▄─█─▄▄─█─▄▄─█─▄─▄─███▄─▄█─▄▄─█▄─▄▄─█▄─█─▄█
// ██─▄─▀██─▀─███─▄─▄██─▄█▀██─▄███─██─█─██─███─███─▄█─██─██─██─▄█▀██▄─▄██
// █▄▄▄▄██▄▄█▄▄█▄▄█▄▄█▄▄▄▄▄█▄▄▄███▄▄▄▄█▄▄▄▄██▄▄▄██▄▄▄███▄▄▄▄█▄▄▄▄▄██▄▄▄██

//@version=5
indicator("Hurst Diamond Notation Pivots", overlay=false)
 
philo = input.string("Lows", "Highs or Lows?", options=["Highs", "Lows"])
linecolor = input.color(color.new(color.gray,0), "Label/Line Color")
ptransp = input.int(33, "Radar Transparency", minval=0, maxval=100)
ltransp = input.int(100, "Line Transparency", minval=0, maxval=100)

n = bar_index
 
// Input/2 (Default 5) Length Pivot Cycle
hcol = input.color(color.purple, "Half Cycle Color", inline="hc")
cych = input.int(5, "Length", inline="hc")
labh = input.bool(true, "Label?", inline="hc")
labhf = input.bool(true, "Forecast?", inline="hc")
plh = philo == "Highs" ? ta.pivothigh(cych, cych) : ta.pivotlow(cych, cych) // Define a PL or PH based on L/H Switch in settings
plhy = philo == "Highs" ? -5 : 5 // Position the pivot on the Y axis of the oscillator
plhi = ta.barssince(plh) // Bars since pivot occured?
plhp = plhi>cych // Bars since pivot occured greater than cycle length?
lowhin = philo == "Highs" ? ta.highest(close, cych*2) : ta.lowest(close, cych*2) // Highest/Lowest for the cycle
lowh = ta.barssince(plh)>cych ? lowhin : na // If the barssince pivot are greater than cycle length, show the uncomnfirmed "pivot tracker"
plot(plhy, "Half Cycle Radar Line", color=plhp?hcol:color.new(linecolor,ltransp), offset=(cych*-1), display=display.none) // Cycle detection lines v1
plotshape(plh ? plhy : na, "Half Cycle Confirmed", style=shape.diamond, location=location.absolute, color=hcol, size = size.tiny, offset=(cych*-1)) // Past Pivots
plotshape(lowh ? plhy : na, "Half Cycle Radar", style=shape.circle, location=location.absolute, color=color.new(hcol, ptransp), size = size.tiny, offset=(cych*-1), show_last=1, display=display.none) // AKA the "Tracker/Radar" v1
// LuxAlgo pivot average calculation used for the forecast
barssince_ph = 0
ph_x2 = ta.valuewhen(plh, n - cych, 1) // x values for pivot
if plh
    barssince_ph := (n - cych) - ph_x2 // if there is a pivot, then BarsSincePivot = (BarIndex - Cycle Length) - x values for pivot
avg_barssince_ph = ta.cum(barssince_ph) / ta.cum(math.sign(barssince_ph)) // AvgBarsSincePivot = Sum of the BarsSincePivot divided by (Sum of the number of signs of BarsSincePivot, AKA the number of BarsSincePivots)
// Draw a diamond forecast label and forecast range line
tooltiph = "🔄 Pivot Cycle: " + str.tostring(cych) + " bars" +
  "\n⏱ Last Pivot: " + str.tostring(plhi + cych) + " bars ago" + 
  "\n🧮 Average Pivot: " + str.tostring(math.round(avg_barssince_ph)) + " bars" + 
  "\n🔮 Next Pivot: " + str.tostring(math.round(avg_barssince_ph)-(plhi + cych)) + " bars" + 
  "\n📏 Range: +/- " + str.tostring(math.round(avg_barssince_ph/2)) + " bars"
var label fh = na
var line lh = na
if  labhf
    fh := label.new(n + math.min((math.round(avg_barssince_ph) - (plhi+cych)), 500), y=plhy, size=size.tiny, style=label.style_diamond, color=color.new(hcol,ptransp),tooltip =tooltiph) 
    label.delete(fh[1])
    lh := line.new(x1=n + math.min(math.round((avg_barssince_ph - (plhi+cych))-(avg_barssince_ph/2)), 500), x2=n + math.min(math.round((avg_barssince_ph - (plhi+cych))+(avg_barssince_ph/2)), 500), y1=plhy, y2=plhy, color=color.new(hcol,ptransp))
    line.delete(lh[1]) 
var label ch = na // Create a label
if  labh // Define the label
    ch := label.new(bar_index, y=plhy, text=str.tostring(cych), size=size.small, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(plhp?hcol:linecolor,0), tooltip=tooltiph) 
    label.delete(ch[1])

// Input (Default 10) Length Pivot Cycle
col = input.color(color.blue, "Full Cycle Color", inline="fc")
cyc = input.int(10, "Length", inline="fc")
labf = input.bool(true, "Label?", inline="fc")
labff = input.bool(true, "Forecast?", inline="fc")
pl = philo == "Highs" ? ta.pivothigh(cyc, cyc) : ta.pivotlow(cyc, cyc)
ply = philo == "Highs" ? -10 : 10
pli = ta.barssince(pl)
plp = pli>cyc
lowin = philo == "Highs" ? ta.highest(close, cyc*2) : ta.lowest(close, cyc*2)
lowf = ta.barssince(pl)>cyc ? lowin : na
plot(ply, "Full Cycle Radar Line", color=plp?col:color.new(linecolor,ltransp), offset=(cyc*-1), display=display.none)
plotshape(pl ? ply : na, "Full Cycle Confirmed", style=shape.diamond, location=location.absolute, color=col, size = size.tiny, offset=(cyc*-1))
plotshape(lowf ? ply : na, "Full Cycle Radar", style=shape.circle, location=location.absolute, color=color.new(col, ptransp), size = size.tiny, offset=(cyc*-1), show_last=1, display=display.none)
// Forecast & Labels 
barssince_p = 0
p_x2 = ta.valuewhen(pl, n - cyc, 1)
p_y2 = ta.valuewhen(pl, pl, 1)
if pl
    barssince_p := (n - cyc) - p_x2
avg_barssince_p = ta.cum(barssince_p) / ta.cum(math.sign(barssince_p))
tooltipf = "🔄 Pivot Cycle: " + str.tostring(cyc) + " bars" +
  "\n⏱ Last Pivot: " + str.tostring(pli + cyc) + " bars ago" + 
  "\n🧮 Average Pivot: " + str.tostring(math.round(avg_barssince_p)) + " bars" + 
  "\n🔮 Next Pivot: " + str.tostring(math.round(avg_barssince_p)-(pli + cyc)) + " bars" +
  "\n📏 Range: +/- " + str.tostring(math.round(avg_barssince_p/2)) + " bars"
var label ff = na
var line lf = na
if  labff
    ff := label.new(n + math.min((math.round(avg_barssince_p) - (pli+cyc)), 500), y=ply, size=size.tiny, style=label.style_diamond, color=color.new(col,ptransp),tooltip =tooltipf) 
    label.delete(ff[1])
    lf := line.new(x1=n + math.min(math.round((avg_barssince_p - (pli+cyc))-(avg_barssince_p/2)), 500), x2=n + math.min(math.round((avg_barssince_p - (pli+cyc))+(avg_barssince_p/2)), 500), y1=ply, y2=ply, color=color.new(col,ptransp))
    line.delete(lf[1])
var label cf = na
if  labf
    cf := label.new(bar_index, y=ply, text=str.tostring(cyc), size=size.small, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(plp?col:linecolor,0), tooltip=tooltipf) 
    label.delete(cf[1])

// Input x2 (Default 20) Length Pivot Cycle
col2 = input.color(color.aqua, "2x Cycle Color  ", inline="2c")
cyc2 = input.int(20, "Length", inline="2c")
lab2 = input.bool(true, "Label?", inline="2c")
lab2f = input.bool(true, "Forecast?", inline="2c")
pl2 = philo == "Highs" ? ta.pivothigh(cyc2, cyc2) : ta.pivotlow(cyc2, cyc2)
pl2y = philo == "Highs" ? -15 : 15
pl2i = ta.barssince(pl2)
pl2p = pl2i>cyc2
low2in = philo == "Highs" ? ta.highest(close, cyc2*2) : ta.lowest(close, cyc2*2)
low2 = ta.barssince(pl2)>cyc2 ? low2in : na
plot(pl2y, "2x Cycle Radar Line", color=pl2p?col2:color.new(linecolor,ltransp), offset=(cyc2*-1), display=display.none)
plotshape(pl2 ? pl2y : na, "2x Cycle Confirmed", style=shape.diamond, location=location.absolute, color=col2, size = size.tiny, offset=(cyc2*-1))
plotshape(low2 ? pl2y : na, "2x Cycle Radar", style=shape.circle, location=location.absolute, color=color.new(col2, ptransp), size = size.tiny, offset=(cyc2*-1), show_last=1, display=display.none)
// Forecast & Labels 
barssince_p2 = 0
p2_x2 = ta.valuewhen(pl2, n - cyc2, 1)
p2_y2 = ta.valuewhen(pl2, pl2, 1)
if pl2
    barssince_p2 := (n - cyc2) - p2_x2
avg_barssince_p2 = ta.cum(barssince_p2) / ta.cum(math.sign(barssince_p2))
tooltip2 = "🔄 Pivot Cycle: " + str.tostring(cyc2) + " bars" +
  "\n⏱ Last Pivot: " + str.tostring(pl2i + cyc2) + " bars ago" + 
  "\n🧮 Average Pivot: " + str.tostring(math.round(avg_barssince_p2)) + " bars" + 
  "\n🔮 Next Pivot: " + str.tostring(math.round(avg_barssince_p2)-(pl2i + cyc2)) + " bars" +
  "\n📏 Range: +/- " + str.tostring(math.round(avg_barssince_p2/2)) + " bars"
var label f2 = na
var line l2 = na
if  lab2f
    f2 := label.new(n + math.min((math.round(avg_barssince_p2) - (pl2i+cyc2)), 500), y=pl2y, size=size.tiny, style=label.style_diamond, color=color.new(col2,ptransp),tooltip =tooltip2) 
    label.delete(f2[1])
    l2 := line.new(x1=n + math.min(math.round((avg_barssince_p2 - (pl2i+cyc2))-(avg_barssince_p2/2)), 500), x2=n + math.min(math.round((avg_barssince_p2 - (pl2i+cyc2))+(avg_barssince_p2/2)), 500), y1=pl2y, y2=pl2y, color=color.new(col2,ptransp))
    line.delete(l2[1])
var label c2 = na
if  lab2
    c2 := label.new(bar_index, y=pl2y, text=str.tostring(cyc2), size=size.small, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(pl2p?col2:linecolor,0), tooltip=tooltip2) 
    label.delete(c2[1])

// Input x4 (Default 40) Length Pivot Cycle
col4 = input.color(color.green, "4x Cycle Color  ", inline="4c")
cyc4 = input.int(40, "Length", inline="4c")
lab4 = input.bool(true, "Label?", inline="4c")
lab4f = input.bool(true, "Forecast?", inline="4c")
pl4 = philo == "Highs" ? ta.pivothigh(cyc4, cyc4) : ta.pivotlow(cyc4, cyc4)
pl4y = philo == "Highs" ? -20 : 20
pl4i = ta.barssince(pl4)
pl4p = pl4i>cyc4
low4in = philo == "Highs" ? ta.highest(close, cyc4*2): ta.lowest(close, cyc4*2)
low4 = ta.barssince(pl4)>cyc4 ? low4in : na
plot(pl4y, "4x Cycle Radar Line", color=pl4p?col4:color.new(linecolor,ltransp), offset=(cyc4*-1), display=display.none)
plotshape(pl4 ? pl4y : na, "4x Cycle Confirmed", style=shape.diamond, location=location.absolute, color=col4, size = size.tiny, offset=(cyc4*-1))
plotshape(low4 ? pl4y : na, "4x Cycle Radar", style=shape.circle, location=location.absolute, color=color.new(col4, ptransp), size = size.tiny, offset=(cyc4*-1), show_last=1, display=display.none)
// Forecast & Labels 
barssince_p4 = 0
p4_x2 = ta.valuewhen(pl4, n - cyc4, 1)
p4_y2 = ta.valuewhen(pl4, pl4, 1)
if pl4
    barssince_p4 := (n - cyc4) - p4_x2
avg_barssince_p4 = ta.cum(barssince_p4) / ta.cum(math.sign(barssince_p4))
tooltip4 = "🔄 Pivot Cycle: " + str.tostring(cyc4) + " bars" +
  "\n⏱ Last Pivot: " + str.tostring(pl4i + cyc4) + " bars ago" +
  "\n🧮 Average Pivot: " + str.tostring(math.round(avg_barssince_p4)) + " bars" + 
  "\n🔮 Next Pivot: " + str.tostring(math.round(avg_barssince_p4)-(pl4i + cyc4)) + " bars" + 
  "\n📏 Range: +/- " + str.tostring(math.round(avg_barssince_p4/2)) + " bars"
var label f4 = na
var line l4 = na
if  lab4f
    f4 := label.new(n + math.min((math.round(avg_barssince_p4) - (pl4i+cyc4)), 500), y=pl4y, size=size.tiny, style=label.style_diamond, color=color.new(col4,ptransp),tooltip =tooltip4) 
    label.delete(f4[1])
    l4 := line.new(x1=n + math.min(math.round((avg_barssince_p4 - (pl4i+cyc4))-(avg_barssince_p4/2)), 500), x2=n + math.min(math.round((avg_barssince_p4 - (pl4i+cyc4))+(avg_barssince_p4/2)), 500), y1=pl4y, y2=pl4y, color=color.new(col4,ptransp))
    line.delete(l4[1])
var label c4 = na
if  lab4
    c4 := label.new(bar_index, y=pl4y, text=str.tostring(cyc4), size=size.small, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(pl4p?col4:linecolor,0), tooltip=tooltip4) 
    label.delete(c4[1])

// Input x8 (Default 80) Length Pivot Cycle
col8    = input.color(color.yellow, "8x Cycle Color  ", inline="8c")
cyc8    = input.int(80, "Length", inline="8c")
lab8 = input.bool(true, "Label?", inline="8c")
lab8f = input.bool(true, "Forecast?", inline="8c")
pl8     = philo == "Highs" ? ta.pivothigh(cyc8, cyc8) : ta.pivotlow(cyc8, cyc8)
pl8y    = philo == "Highs" ? -25 : 25
pl8i    = ta.barssince(pl8)
pl8p    = pl8i>cyc8
low8in  = philo == "Highs" ? ta.highest(close, cyc8*2) : ta.lowest(close, cyc8*2)
low8t    = ta.barssince(pl8)>cyc8 ? low8in : na
low8p    = ta.barssince(low8in)
plot(pl8y, "8x Cycle Radar Line", color=pl8p?col8:color.new(linecolor,ltransp), offset=(cyc8*-1), display=display.none)
plotshape(pl8 ? pl8y : na, "8x Cycle Confirmed", style=shape.diamond, location=location.absolute, color=col8, size = size.tiny, offset=(cyc8*-1))
plotshape(low8t ? pl8y : na, "8x Cycle Radar", style=shape.circle, location=location.absolute, color=color.new(col8, ptransp), size = size.tiny, offset=(cyc8*-1), show_last=1, display=display.none)
// Forecast & Labels 
barssince_p8 = 0
p8_x2 = ta.valuewhen(pl8, n - cyc8, 1)
p8_y2 = ta.valuewhen(pl8, pl8, 1)
if pl8
    barssince_p8 := (n - cyc8) - p8_x2
avg_barssince_p8 = ta.cum(barssince_p8) / ta.cum(math.sign(barssince_p8))
tooltip8 = "🔄 Pivot Cycle: " + str.tostring(cyc8) + " bars" +
  "\n⏱ Last Pivot: " + str.tostring(pl8i + cyc8) + " bars ago" +
  "\n🧮 Average Pivot: " + str.tostring(math.round(avg_barssince_p8)) + " bars" + 
  "\n🔮 Next Pivot: " + str.tostring(math.round(avg_barssince_p8)-(pl8i + cyc8)) + " bars" +
  "\n📏 Range: +/- " + str.tostring(math.round(avg_barssince_p8/2)) + " bars"
var label f8 = na
var line l8 = na
if  lab8f
    f8 := label.new(n + math.min((math.round(avg_barssince_p8) - (pl8i+cyc8)), 500), y=pl8y, size=size.tiny, style=label.style_diamond, color=color.new(col8,ptransp),tooltip =tooltip8) 
    label.delete(f8[1])
    l8 := line.new(x1=n + math.min(math.round((avg_barssince_p8 - (pl8i+cyc8))-(avg_barssince_p8/2)), 500), x2=n + math.min(math.round((avg_barssince_p8 - (pl8i+cyc8))+(avg_barssince_p8/2)), 500), y1=pl8y, y2=pl8y, color=color.new(col8,ptransp))
    line.delete(l8[1])
var label c8 = na
if  lab8
    c8 := label.new(bar_index, y=pl8y, text=str.tostring(cyc8), size=size.small, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(pl8p?col8:linecolor,0), tooltip=tooltip8) 
    label.delete(c8[1])

// Input x16 (Default 160) Length Pivot Cycle
col16 = input.color(color.orange, "16x Cycle Color", inline="16c")
cyc16 = input.int(160, "Length", inline="16c")
lab16 = input.bool(true, "Label?", inline="16c")
lab16f = input.bool(true, "Forecast?", inline="16c")
pl16 = philo == "Highs" ? ta.pivothigh(cyc16, cyc16) : ta.pivotlow(cyc16, cyc16)
pl16y = philo == "Highs" ? -30 : 30
pl16i = ta.barssince(pl16)
pl16p = pl16i>cyc16
low16in = philo == "Highs" ? ta.highest(close, cyc16*2) : ta.lowest(close, cyc16*2)
low16 = ta.barssince(pl16)>cyc16 ? low16in : na
plot(pl16y, "16x Cycle Radar Line", color=pl16p?col16:color.new(linecolor,ltransp), offset=(cyc16*-1), display=display.none)
plotshape(pl16 ? pl16y : na, "16x Cycle Confirmed", style=shape.diamond, location=location.absolute, color=col16, size = size.tiny, offset=(cyc16*-1))
plotshape(low16 ? pl16y : na, "16x Cycle Radar", style=shape.circle, location=location.absolute, color=color.new(col16, ptransp), size = size.tiny, offset=(cyc16*-1), show_last=1, display=display.none)
// Forecast & Labels 
barssince_p16 = 0
p16_x2 = ta.valuewhen(pl16, n - cyc16, 1)
p16_y2 = ta.valuewhen(pl16, pl16, 1)
if pl16
    barssince_p16 := (n - cyc16) - p16_x2
avg_barssince_p16 = ta.cum(barssince_p16) / ta.cum(math.sign(barssince_p16))
tooltip16 = "🔄 Pivot Cycle: " + str.tostring(cyc16) + " bars" +
  "\n⏱ Last Pivot: " + str.tostring(pl16i + cyc16) + " bars ago" +
  "\n🧮 Average Pivot: " + str.tostring(math.round(avg_barssince_p16)) + " bars" + 
  "\n🔮 Next Pivot: " + str.tostring(math.round(avg_barssince_p16)-(pl16i + cyc16)) + " bars" +
  "\n📏 Range: +/- " + str.tostring(math.round(avg_barssince_p16/2)) + " bars"
var label f16 = na
var line l16 = na
if lab16f
    f16 := label.new(n + math.min((math.round(avg_barssince_p16) - (pl16i+cyc16)), 500), y=pl16y, size=size.tiny, style=label.style_diamond, color=color.new(col16,ptransp),tooltip =tooltip16) 
    label.delete(f16[1])
    l16 := line.new(x1=n + math.min(math.round((avg_barssince_p16 - (pl16i+cyc16))-(avg_barssince_p16/2)), 500), x2=n + math.min(math.round((avg_barssince_p16 - (pl16i+cyc16))+(avg_barssince_p16/2)), 500), y1=pl16y, y2=pl16y, color=color.new(col16,ptransp))
    line.delete(l16[1])
var label c16 = na
if  lab16
    c16 := label.new(bar_index, y=pl16y, text=str.tostring(cyc16), size=size.small, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(pl16p?col16:linecolor,0), tooltip=tooltip16) 
    label.delete(c16[1])

// Input x32 (Default 230) Length Pivot Cycle
col32 = input.color(color.red, "32x Cycle Color", inline="32c")
cyc32 = input.int(320, "Length", inline="32c")
lab32 = input.bool(true, "Label?", inline="32c")
lab32f = input.bool(true, "Forecast?", inline="32c")
pl32 = philo == "Highs" ? ta.pivothigh(cyc32, cyc32) : ta.pivotlow(cyc32, cyc32)
pl32y = philo == "Highs" ? -35 : 35
pl32i = ta.barssince(pl32)
pl32p = pl32i>cyc32
low32in = philo == "Highs" ? ta.highest(close, cyc32*2) : ta.lowest(close, cyc32*2)
low32 = ta.barssince(pl32)>cyc32 ? low32in : na
plot(pl32y, "32x Cycle Radar Line", color=pl32p?col32:color.new(linecolor,ltransp), offset=(cyc32*-1), display=display.none)
plotshape(pl32 ? pl32y : na, "32x Cycle Confirmed", style=shape.diamond, location=location.absolute, color=col32, size = size.tiny, offset=(cyc32*-1))
plotshape(low32 ? pl32y : na, "16x Cycle Radar", style=shape.circle, location=location.absolute, color=color.new(col32, ptransp), size = size.tiny, offset=(cyc32*-1), show_last=1, display=display.none)
// Forecast & Labels 
barssince_p32 = 0
p32_x2 = ta.valuewhen(pl32, n - cyc32, 1)
p32_y2 = ta.valuewhen(pl32, pl32, 1)
if pl32
    barssince_p32 := (n - cyc32) - p32_x2
avg_barssince_p32 = ta.cum(barssince_p32) / ta.cum(math.sign(barssince_p32))
tooltip32 = "🔄 Pivot Cycle: " + str.tostring(cyc32) + " bars" +
  "\n⏱ Last Pivot: " + str.tostring(pl32i + cyc32) + " bars ago" +
  "\n🧮 Average Pivot: " + str.tostring(math.round(avg_barssince_p32)) + " bars" + 
  "\n🔮 Next Pivot: " + str.tostring(math.round(avg_barssince_p32)-(pl32i + cyc32)) + " bars" +
  "\n📏 Range: +/- " + str.tostring(math.round(avg_barssince_p32/2)) + " bars"
var label f32 = na
var line l32 = na
if  lab32f
    f32 := label.new(n + math.min((math.round(avg_barssince_p32) - (pl32i+cyc32)), 500), y=pl32y, size=size.tiny, style=label.style_diamond, color=color.new(col32,ptransp),tooltip =tooltip32) 
    label.delete(f32[1])
    l32 := line.new(x1=n + math.min(math.round((avg_barssince_p32 - (pl32i+cyc32))-(avg_barssince_p32/2)), 500), x2=n + math.min(math.round((avg_barssince_p32 - (pl32i+cyc32))+(avg_barssince_p32/2)), 500), y1=pl32y, y2=pl32y, color=color.new(col32,ptransp))
    line.delete(l32[1])
var label c32 = na
if  lab32
    c32 := label.new(bar_index, y=pl32y, text=str.tostring(cyc32), size=size.small, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(pl32p?col32:linecolor,0), tooltip=tooltip32) 
    label.delete(c32[1])

// EoS made w/ ❤ by @BarefootJoey ✌💗📈