//@version=6 indicator("Rev & Line - CoffeeKiller", overlay=true, max_bars_back=3000) // ───────────────────────────────────────────────────────────── // Inputs // ───────────────────────────────────────────────────────────── // Zigzag group groupZZ = "Zigzag" hideRepainted = input(true, "Hide repainted", group=groupZZ) price = input(close, "Price", group=groupZZ) priceH = input(high, "Price High", group=groupZZ) priceL = input(low, "Price Low", group=groupZZ) atrReversalFac = input(3.2, "ATR Reversal Factor", group=groupZZ) showZZLines = input(true, "Show ZigZag Lines", group=groupZZ) showVertPivots = input.bool(true, "Show Vertical Lines at Pivots", group=groupZZ) maxVLines = input(10, "Max Vertical Lines on Chart", group=groupZZ) bullPivotColor = input.color(color.rgb(0, 128, 0), "Pivot Low", inline="vertical", group=groupZZ) bearPivotColor = input.color(color.rgb(255, 0, 0), "Pivot High", inline="vertical", group=groupZZ) zzLineWidth = input.int(3, "Zigzag Line Width", group=groupZZ) pivotLineWidth = input.int(2, "Vertical Pivot Line Width", group=groupZZ) // Donchian Channel group groupDCC = "Donchian Channel" showChannel = input(false, "Show Channel", group=groupDCC) displace = input(0, "Displace", group=groupDCC) length = input(20, "Length", group=groupDCC) bubbleOffset = input(0.50, "Bubble Displaced", group=groupDCC) // ───────────────────────────────────────────────────────────── // ATR & Reversal Calculation // ───────────────────────────────────────────────────────────── atrValue = ta.rma(ta.tr, 5) reversalAmount = atrReversalFac * atrValue // ───────────────────────────────────────────────────────────── // Zigzag State & Pivot Detection // ───────────────────────────────────────────────────────────── var string state = "init" var float minMaxPrice = na if state == "init" minMaxPrice := price state := "undefined" else if state == "undefined" if price <= minMaxPrice - reversalAmount state := "downtrend" minMaxPrice := priceL else if price >= minMaxPrice + reversalAmount state := "uptrend" minMaxPrice := priceH else minMaxPrice := minMaxPrice[1] else if state == "uptrend" if price <= minMaxPrice - reversalAmount state := "downtrend" minMaxPrice := priceL else minMaxPrice := math.max(priceH, minMaxPrice[1]) else // state == "downtrend" if price >= minMaxPrice + reversalAmount state := "uptrend" minMaxPrice := priceH else minMaxPrice := math.min(priceL, minMaxPrice[1]) newState = state != state[1] highPivot = state == "uptrend" and priceH == minMaxPrice lowPivot = state == "downtrend" and priceL == minMaxPrice // ───────────────────────────────────────────────────────────── // Zigzag Pivot & Line Calculation // ───────────────────────────────────────────────────────────── var int prevStateBar = na var int lastHBar = na var int prevLastHBar = na var int lastLBar = na var int prevLastLBar = na var float lastHVal = na var float prevLastHVal = na var float lastLVal = na var float prevLastLVal = na if newState prevStateBar := bar_index // For uptrend pivots if (newState or barstate.islast) and state[1] == "uptrend" prevLastHBar := lastHBar for i = 1 to (bar_index - prevStateBar[1]) if highPivot[i] lastHBar := bar_index - i break lastHVal := priceH[bar_index - lastHBar] prevLastHVal := priceH[bar_index - prevLastHBar] // For downtrend pivots if (newState or barstate.islast) and state[1] == "downtrend" prevLastLBar := lastLBar for i = 1 to (bar_index - prevStateBar[1]) if lowPivot[i] lastLBar := bar_index - i break lastLVal := priceL[bar_index - lastLBar] prevLastLVal := priceL[bar_index - prevLastLBar] // Manage vertical pivot lines array var array vLines = array.new_line() if newState and state[4] == "uptrend" if showZZLines line.new(lastHBar, lastHVal, lastLBar, lastLVal, width=zzLineWidth, color=color.green) if showVertPivots // Use solid line for confirmed pivot that won't be repainted array.push(vLines, line.new(lastLBar, priceH, lastLBar, priceL, extend=extend.both, color=bullPivotColor, width=pivotLineWidth, style=line.style_solid)) else if newState and state[4] == "downtrend" if showZZLines line.new(lastHBar, lastHVal, lastLBar, lastLVal, width=zzLineWidth, color=color.red) if showVertPivots // Use solid line for confirmed pivot that won't be repainted array.push(vLines, line.new(lastHBar, priceH, lastHBar, priceL, extend=extend.both, color=bearPivotColor, width=pivotLineWidth, style=line.style_solid)) if array.size(vLines) > maxVLines line.delete(array.shift(vLines)) // Draw final zigzag lines on the last bar var line finalZZLine = line.new(na, na, na, na) var line finalPivotLine1 = line.new(na, na, na, na, extend=extend.both, width=pivotLineWidth) var line finalPivotLine2 = line.new(na, na, na, na, extend=extend.both, width=pivotLineWidth) if barstate.islast if showZZLines if lastLBar < lastHBar line.set_xy1(finalZZLine, lastLBar, lastLVal) line.set_xy2(finalZZLine, lastHBar, lastHVal) line.set_color(finalZZLine, color.green) else line.set_xy1(finalZZLine, lastHBar, lastHVal) line.set_xy2(finalZZLine, lastLBar, lastLVal) line.set_color(finalZZLine, color.red) if showVertPivots // Determine which pivot is the most recent one bool isLowMostRecent = lastLBar > lastHBar if lastLBar < lastHBar // Low pivot came first (older bull) line.set_xy1(finalPivotLine1, lastLBar, lastLVal) line.set_xy2(finalPivotLine1, lastLBar, lastHVal) line.set_color(finalPivotLine1, bullPivotColor) line.set_style(finalPivotLine1, line.style_dashed) // High pivot is most recent line.set_xy1(finalPivotLine2, lastHBar, lastLVal) line.set_xy2(finalPivotLine2, lastHBar, lastHVal) line.set_color(finalPivotLine2, bearPivotColor) // Special color for most recent pivot line.set_style(finalPivotLine2, line.style_dotted) line.set_width(finalPivotLine2, pivotLineWidth) else // High pivot came first (older bear) line.set_xy1(finalPivotLine1, lastHBar, lastLVal) line.set_xy2(finalPivotLine1, lastHBar, lastHVal) line.set_color(finalPivotLine1, bearPivotColor) line.set_style(finalPivotLine1, line.style_dashed) // Low pivot is most recent line.set_xy1(finalPivotLine2, lastLBar, lastLVal) line.set_xy2(finalPivotLine2, lastLBar, lastHVal) line.set_color(finalPivotLine2, bullPivotColor) // Special color for most recent pivot line.set_style(finalPivotLine2, line.style_dotted) line.set_width(finalPivotLine2, pivotLineWidth) // ───────────────────────────────────────────────────────────── // Donchian Channel Calculation & Plotting // ───────────────────────────────────────────────────────────── upperBand = ta.highest(high[displace], length) lowerBand = ta.lowest(low[displace], length) middleBand = (upperBand + lowerBand) / 2 plot(showChannel ? upperBand : na, "Upper Band", color.red) plot(showChannel ? lowerBand : na, "Lower Band", color.green) plot(showChannel ? middleBand : na, "Middle Band", color.yellow) // ───────────────────────────────────────────────────────────── // Combined ZZ & Donchian Scenarios // ───────────────────────────────────────────────────────────── longEntry = low > low[1] and low[1] <= lowerBand[1] shortEntry = high < high[1] and high[1] >= upperBand[1] var float nonRepaintZz = na nonRepaintZz := highPivot ? priceH : lowPivot ? priceL : nonRepaintZz arrDown = highPivot[1] and not highPivot and shortEntry and not longEntry and nonRepaintZz[1] == upperBand arrUp = lowPivot[1] and not lowPivot and longEntry and not shortEntry and nonRepaintZz[1] == lowerBand plotshape(arrDown, "Down", shape.triangledown, location=location.abovebar, color=color.black, size=size.small, display = hideRepainted ? display.none : display.all) plotshape(arrUp, "Up", shape.triangleup, location=location.belowbar, color=color.black, size=size.small, display = hideRepainted ? display.none : display.all) // ───────────────────────────────────────────────────────────── // Arrow & Label Drawing // ───────────────────────────────────────────────────────────── upLabelStyle = label.style_label_upper_left dnLabelStyle = label.style_label_lower_left // Immediate labels on new state conditions if newState and state[1] == "uptrend" and shortEntry[bar_index - lastHBar - 1] and not longEntry[bar_index - lastHBar - 1] and high[bar_index - lastHBar] == upperBand[bar_index - lastHBar - 1] label.new(lastHBar + 1, high[bar_index - lastHBar - 1] + bubbleOffset * syminfo.mintick, style=label.style_triangledown, color=color.rgb(255, 0, 0), size=size.small, yloc=yloc.abovebar) if newState and state[1] == "downtrend" and longEntry[bar_index - lastLBar - 1] and not shortEntry[bar_index - lastLBar - 1] and low[bar_index - lastLBar] == lowerBand[bar_index - lastLBar - 1] label.new(lastLBar + 1, low[bar_index - lastLBar - 1] - bubbleOffset * syminfo.mintick, style=label.style_triangleup, color=color.rgb(0, 128, 0), size=size.small, yloc=yloc.belowbar) // Persistent arrow labels var label downArrow = label.new(na, na, style=dnLabelStyle, color=color.rgb(255, 0, 0), text="") var label upArrow = label.new(na, na, style=upLabelStyle, color=color.rgb(0, 128, 0), text="") var label downArrow2 = label.new(na, na, style=label.style_triangledown, color=color.rgb(255, 0, 0), size=size.small, yloc=yloc.abovebar) var label upArrow2 = label.new(na, na, style=label.style_triangleup, color=color.rgb(0, 128, 0), size=size.small, yloc=yloc.belowbar) if barstate.islast and state == "uptrend" and priceH < lastHVal and priceH[1] < lastHVal and shortEntry[bar_index - lastHBar - 1] and not longEntry[bar_index - lastHBar - 1] and high[bar_index - lastHBar] == upperBand[bar_index - lastHBar - 1] label.set_color(downArrow2, color.rgb(255, 0, 0)) label.set_xy(downArrow2, lastHBar + 1, high[bar_index - lastHBar - 1] + bubbleOffset) if barstate.islast and state == "downtrend" and priceL > lastLVal and priceL[1] > lastLVal and longEntry[bar_index - lastLBar - 1] and not shortEntry[bar_index - lastLBar - 1] and low[bar_index - lastLBar] == lowerBand[bar_index - lastLBar - 1] label.set_color(upArrow2, color.rgb(0, 128, 0)) label.set_xy(upArrow2, lastLBar + 1, low[bar_index - lastLBar - 1] - bubbleOffset) invisibleColor = color.new(color.white, 100) if barstate.islast and state == "uptrend" and priceH > lastHVal label.set_color(downArrow, invisibleColor) label.set_color(downArrow2, invisibleColor) if barstate.islast and state == "downtrend" and priceL < lastLVal label.set_color(upArrow, invisibleColor) label.set_color(upArrow2, invisibleColor) // ───────────────────────────────────────────────────────────── // Alerts // ───────────────────────────────────────────────────────────── alertcondition(highPivot[1] and not highPivot, "Down Arrow", "Down arrow") alertcondition(lowPivot[1] and not lowPivot, "Up Arrow", "Up arrow") if highPivot[1] and not highPivot alert(syminfo.ticker + " reversal down arrow", alert.freq_once_per_bar_close) if lowPivot[1] and not lowPivot alert(syminfo.ticker + " reversal up arrow", alert.freq_once_per_bar_close)