Anchored VWAP (Auto High & Low)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © liquid-trader

// This script plots, and auto-updates, 3 separate VWAPs: a traditional VWAP, a VWAP anchored to a trends high,
// and another anchored to a trends low. Because VWAP is a prominent market maker tool for executing large trades,
// day traders can use it to better anticipate trends, mean reversion, and breakouts.
// More here: https://www.tradingview.com/script/72stxVxI-Anchored-VWAP-Auto-High-Low/

//@version=5 
indicator("Anchored VWAP (Auto High & Low)", "AVWAP (Auto Hi / Lo)", overlay=true, max_bars_back=0)
max_bars_back(time, 1440)

// Base Colors
none = color.new(color.black, 100), clr1 = color.aqua

// Groups
g1 = "Anchored VWAP", g2 = "Normal VWAP"

// High Anchor
shoHiA = input.bool(true, "High Anchor           ", "VWAP anchored and weighted to trend highs.", inline="hi", group=g1, display=display.none)
hiAnClr = input.color(clr1, "", inline="hi", group=g1, display=display.none)
hiWidth = input.int(2, "", 1, inline="hi", group=g1, display=display.none)

// Low Anchor
shoLoA = input.bool(true, "Low Anchor            ", "VWAP anchored and weighted to trend lows.", inline="lo", group=g1, display=display.none)
loAnClr = input.color(clr1, "", inline="lo", group=g1, display=display.none)
loWidth = input.int(2, "", 1, inline="lo", group=g1, display=display.none)

// Average of Anchors
shoMid = input.bool(true, "Avg. of Anchors     ", "The mean (average) between the high and low anchored VWAP's", inline="mid", group=g1, display=display.none)
midClr = input.color(color.new(clr1, 50), "", inline="mid", group=g1, display=display.none)
mWidth = input.int(2, "", 1, inline="mid", group=g1, display=display.none)

// Quarter levels
shoQrt = input.bool(true, "Quarter Values       ", "The median (middle) value between the mean (average) and high / low anchors.", inline="quarter", group=g1, display=display.none)
qrtClr = input.color(color.new(clr1, 80), "", inline="quarter", group=g1, display=display.none)
qWidth = input.int(1,"", 1, inline="quarter", group=g1, display=display.none)

// High eighth levels with fill
shoHiFil = input.bool(true, "High Interim Bands",  inline="hiFill", group=g1, display=display.none)
hiEthClr = input.color(color.new(clr1, 93), "", inline="hiFill", group=g1, display=display.none)
hiFilClr = input.color(color.new(clr1, 97), "", inline="hiFill", group=g1, display=display.none)

// Low eighth levels with fill
shoLoFil = input.bool(true, "Low Interim Bands ",  inline="loFill", group=g1, display=display.none)
loEthClr = input.color(color.new(clr1, 93), "", inline="loFill", group=g1, display=display.none)
loFilClr = input.color(color.new(clr1, 97), "", inline="loFill", group=g1, display=display.none)

// Smooth Average of Anchors
shoMA = input.bool(false, "Smooth Average    ", inline="smooth", group=g1, display=display.none)
maSrc = input.string("SMMA (RMA)", "", ["SMA", "EMA", "SMMA (RMA)", "WMA", "VWMA", "Linear Regression"], inline="smooth", group=g1, display=display.none)
maLen = input.int(3, "", 1, inline="smooth", group=g1, display=display.none)

// Anchor Reset Settings
sessionLimited = input.bool(true, "Limit anchor session continuation to ", "Defines when a given anchor should be reset if its VWAP has not been broken, measured in sessions.", inline="reset", group=g1, display=display.none)
anchorMax = input.int(1, "", 1, inline="reset", group=g1, display=display.none)
mktHrsOnly = input.bool(false, "Limit anchor resets to only regular trading hours and new sessions.", "Prevents a given VWAP from resetting afhours, overnight, or during the premarket.", group=g1, display=display.none)

// 1 Day VWAP
O = "open", H = "high", L = "low", C = "close", HL2 = "hl2", HLC3 = "hlc3", OHLC4 = "ohlc4", HLCC4 = "hlcc4"
shoVWAP = input.bool(true, "VWAP", "The Volume Weighted Average Price is reset at the beginning of each session.", inline="vwap1", group=g2, display=display.none)
vwap1Clr = input.color(color.blue, "", inline="vwap1", group=g2, display=display.none)
vWidth1 = input.int(2, "", 1, inline="vwap1", group=g2, display=display.none)
source1 = input.string(HLC3, "", [O,H,L,C,HL2, HLC3, OHLC4, HLCC4], inline="vwap1", group=g2, display=display.none)

// 2 Day VWAP
shoDay2 = input.bool(false, "Day 2 ", "Continuation of VWAP into a second day.", inline="vwap2", group=g2, display=display.none)
vwap2Clr = input.color(color.new(color.teal, 25), "", inline="vwap2", group=g2, display=display.none)
vWidth2 = input.int(2, "", 1, inline="vwap2", group=g2, display=display.none)
source2 = input.string(HLC3, "", [O,H,L,C,HL2, HLC3, OHLC4, HLCC4], inline="vwap2", group=g2, display=display.none)

// 3 Day VWAP
shoDay3 = input.bool(false, "Day 3 ", "Continuation of VWAP into a third day.", inline="vwap3", group=g2, display=display.none)
vwap3Clr = input.color(color.new(color.red, 50), "", inline="vwap3", group=g2, display=display.none)
vWidth3 = input.int(2, "", 1, inline="vwap3", group=g2, display=display.none)
source3 = input.string(HLC3, "", [O,H,L,C,HL2, HLC3, OHLC4, HLCC4], inline="vwap3", group=g2, display=display.none)

// Market Hours Settings
mktHrs = input.session("0930-1600", "Start / End Time", tooltip = "A 24 hour format, where 0930 is 9:30 AM, 1600 is 4:00 PM, etc.", group="Market Hours", inline="mkt", display=display.none)
zone = input("America/New_York", "Time Zone          ", "Any IANA time zone ID. Ex: America/New_York\n\nYou can also use \"syminfo.timezone\", which inherits the time zone of the exchange of the chart.", group="Market Hours", inline="zone", display=display.none)
timezone = zone == "syminfo.timezone" ? syminfo.timezone : zone
minutesInDay = "1440", marketHours = time(minutesInDay, mktHrs, timezone)

// Put colors into an array.
colors = array.from(hiAnClr, loAnClr, midClr, qrtClr, hiEthClr, loEthClr, hiFilClr, loFilClr)

// Define new sessions.
newSession = timeframe.isdwm ? false : session.isfirstbar

// Set core variables.
o = open, h = high, l = low, c = close, index = bar_index

// Define candle patterns and parts.
candleBody = math.abs( c - o )
rising = c > o
falling = o > c
upWick = rising ? h - c : h - o
dnWick = falling ? c - l : o - l
doji = (upWick >= candleBody or dnWick >= candleBody) and (h != o and h != c and l != o and l != c)
higher = o > o[1] and c > c[1]
lower = o < o[1] and c < c[1]
risingHigher = rising and higher
fallingLower = falling and lower

// Anchor variables.
var hi = h, var lo = l, var anchoredHi = h, var anchoredLo = l, var hrH = h, var hrL = l, var lrH = h, var lrL = l
var resetHiAnchor = false, var resetLoAnchor = false, var bullish = false, var bearish = false
var hiSessionCount = 0, var loSessionCount = 0

// Logic for what qualifies as "breaking" the previous anchors.
breakingHigher = rising  and (c > hi or (c[1] > anchoredHi and c > h[1] and o > anchoredHi and not doji))
breakingLower  = falling and (c < lo or (c[1] < anchoredLo and c < l[1] and o < anchoredLo and not doji))

// Logic for when to reset anchors.
if newSession and sessionLimited
    hiSessionCount += 1, loSessionCount += 1
    if hiSessionCount == anchorMax
        hiSessionCount := 0
        hi := h, hrH := h, hrL := l
        resetHiAnchor := true
        bullish := true
    if loSessionCount == anchorMax
        loSessionCount := 0
        lo := l, lrH := h, lrL := l
        resetLoAnchor := true
        bearish := true
else if breakingHigher and (not mktHrsOnly or (marketHours and marketHours))
    hiSessionCount := 0
    hi := h, hrH := h, hrL := l
    resetHiAnchor := true
    bullish := true
else if breakingLower and (not mktHrsOnly or (marketHours and marketHours))
    loSessionCount := 0
    lo := l, lrH := h, lrL := l
    resetLoAnchor := true
    bearish := true
else
    resetHiAnchor := false
    resetLoAnchor := false

// Set the Anchored VWAP, and their average.
anchoredHi := ta.vwap(h, resetHiAnchor)
anchoredLo := ta.vwap(l, resetLoAnchor)
anchoredMean = math.avg(anchoredHi, anchoredLo)

// Smooth the average according to the traders settings.
if shoMA
    anchoredMean := switch maSrc
        "SMA" => ta.sma(anchoredMean, maLen)
        "EMA" => ta.ema(anchoredMean, maLen)
        "SMMA (RMA)" => ta.rma(anchoredMean, maLen)
        "WMA" => ta.wma(anchoredMean, maLen)
        "VWMA" => ta.vwma(anchoredMean, maLen)
        "Linear Regression" => ta.linreg(anchoredMean, maLen, 0)

// Set the anchored quarter values.
avg75 = math.avg(anchoredHi, anchoredMean), avg87 = math.avg(anchoredHi, avg75), avg62 = math.avg(anchoredMean, avg75)
avg25 = math.avg(anchoredLo, anchoredMean), avg12 = math.avg(anchoredLo, avg25), avg42 = math.avg(anchoredMean, avg25)

// Function to get a price.
price(a) =>
    switch a
        O => open
        H => high
        L => low
        C => close
        HL2 => hl2
        HLC3 => hlc3
        OHLC4 => ohlc4
        HLCC4 => hlcc4

// Function to get yesterdays total volume.
ydVol() => request.security(syminfo.tickerid, "1D", volume)

// Initialize variables for the VWAP handoff.
var vwap1D = 0., var handoff = array.new_float(4, 0)

// Handoff VWAP values from the previous day.
if newSession
    handoff.unshift(ydVol()), handoff.pop()
    handoff.unshift(vwap1D), handoff.pop()

// Calc VWAP continuation beyond the first day.
for i = 0 to handoff.size() / 2 - 1
    j = i * 2
    vwap = handoff.get(j)
    src = switch i
        0 => source2
        1 => source3
    vol = volume
    cvol = handoff.get(j + 1)
    newV = cvol + vol
    newP = (vwap * cvol + price(src) * vol) / newV
    handoff.set(j, newP)
    handoff.set(j + 1, newV)

// Set VWAP value.
vwap1D := ta.vwap(price(source1), timeframe.change("1D"))

// Set VWAP colors.
v1Clr = newSession ? none : vwap1Clr
v2Clr = newSession ? none : vwap2Clr
v3Clr = newSession ? none : vwap3Clr

// Plot VWAPs
plot(vwap1D, "1 Day VWAP", v1Clr, vWidth1, plot.style_linebr, false, na, na, na, false, na, shoVWAP  ? display.pane : display.none)
plot(handoff.get(0), "2 Day VWAP", v2Clr, vWidth2, plot.style_linebr, false, na, na, na, false, na, shoDay2 ? display.pane : display.none)
plot(handoff.get(2), "3 Day VWAP", v3Clr, vWidth3, plot.style_linebr, false, na, na, na, false, na, shoDay3 ? display.pane : display.none)

// Function defining abnormal candle move continuation.
moveCont(d) =>
    switch d
        "up" => anchoredHi >= anchoredHi[1] or hlcc4 > avg87
        "dn" => anchoredLo <= anchoredLo[1] or hlcc4 < avg12

// Function defining candle consolidation, relative to the previous anchor.
consolidating(d, resetHi, resetLo) => doji or (o <= resetHi and c <= resetHi and o >= resetLo and c >= resetLo)
    
// Set the plot and fill colors.
var clr = array.new_color(colors.size())
for i = 0 to colors.size() - 1
    _c_ = colors.get(i)
    _ifTrue_ = switch i
        0 => bullish and (resetHiAnchor or moveCont("up") or consolidating("up", hrH, hrL))
        1 => bearish and (resetLoAnchor or moveCont("dn") or consolidating("dn", lrH, lrL))
        => false
    clr.set(i, newSession ? none : _ifTrue_ ? color.new(_c_, 75) : _c_)

// Toggle the colors reset to be false, if it has been reset back to the user specified color.
bullish := clr.get(0) == hiAnClr ? false : bullish
bearish := clr.get(1) == loAnClr ? false : bearish

// Functions to determine if a plot or fill should display, based on the traders settings.
displayPlot(ifTrue) => ifTrue ? display.pane : display.none
displayFill(ifTrue) => ifTrue ? display.all : display.none

// Plot the top, bottom, and average anchor values.
top = plot(anchoredHi, "High Anchor", clr.get(0), hiWidth, plot.style_linebr, false, na, na, na, false, na, displayPlot(shoHiA))
bot = plot(anchoredLo, "Low Anchor", clr.get(1), loWidth, plot.style_linebr, false, na, na, na, false, na, displayPlot(shoLoA))
mid = plot(anchoredMean, "Average of Anchors", clr.get(2), mWidth, plot.style_linebr, false, na, na, na, false, na, displayPlot(shoMid))

// Plot 1/4 values (half way between average and anchor).
t25 = plot(avg75, "Top 25% Line", clr.get(3), qWidth, plot.style_linebr, false, na, na, na, false, na, displayPlot(shoQrt))
b25 = plot(avg25, "Bottom 25% Line", clr.get(3), qWidth, plot.style_linebr, false, na, na, na, false, na, displayPlot(shoQrt))

// Plot 1/8 lines.
showHideTopLine = displayPlot(shoHiFil)
showHideBotLine = displayPlot(shoLoFil)
t12  = plot(avg87, "Top 12.5% Line",clr.get(4), 1, plot.style_linebr, false, na, na, na, false, na, showHideTopLine)
t252 = plot(avg75, "Top 25% Line", clr.get(4), 1, plot.style_linebr, false, na, na, na, false, na, showHideTopLine)
t42  = plot(avg62, "Top 42.5% Line",clr.get(4), 1, plot.style_linebr, false, na, na, na, false, na, showHideTopLine)
b42  = plot(avg42, "Bottom 42.5% Line",clr.get(5), 1, plot.style_linebr, false, na, na, na, false, na, showHideBotLine)
b252 = plot(avg25, "Bottom 25% Line", clr.get(5), 1, plot.style_linebr, false, na, na, na, false, na, showHideBotLine)
b12  = plot(avg12, "Bottom 12.5% Line",clr.get(5), 1, plot.style_linebr, false, na, na, na, false, na, showHideBotLine)

// Plot 1/8 fills.
showHideTopFill = displayFill(shoHiFil)
showHideBotFill = displayFill(shoLoFil)
fill(top, t12, clr.get(6), "Top 12.5% Fill", false, display = showHideTopFill)
fill(top, t25, clr.get(6), "Top 25% Fill", false, display = showHideTopFill)
fill(top, t42, clr.get(6), "Top 42.5% Fill", false, display = showHideTopFill)
fill(bot, b42, clr.get(7), "Bottom 42.5% Fill", false, display = showHideBotFill)
fill(bot, b25, clr.get(7), "Bottom 25% Fill", false, display = showHideBotFill)
fill(bot, b12, clr.get(7), "Bottom 12.5% Fill", false, display = showHideBotFill)