VWAP Delayed
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © raymah48

//@version=5 
indicator("VWAP Delayed", "", true)

src = input.source(hlc3, "Source")
overrideTimeZone = input(false, "Override Time Zone", inline = "tz", tooltip = "Time input values use this IANA time zone instead of the symbol's native timezone.")
timezoneOverride = input.string("America/New_York", " ", inline = "tz")
validTimeRange = input.session("1000-2000", "VWAP Visible Between", tooltip="VWAP is calculated for the entire day but only displayed during this time period")
startCalcInPremarket = input(true, "Include Premarket", tooltip="Start calculating VWAP for the day on the first bar of the premarket session.  Chart must be showing extended session for this to take effect")
showSdInfo = input(true, "Show Standard Deviation Value", inline="info")
position = input.string("Top Right", " ", options=["Bottom Left", "Top Left", "Bottom Center", "Top Center", "Bottom Right", "Top Right"], inline="info")

showVWAP = input(true, "Show VWAP", inline="vwap")
alwaysShowVWAP = input(false, "Always", inline="vwap", tooltip="Always show VWAP regardless of Session defined above.  Show VWAP must also be checked for VWAP to always be displayed.")
showStdDevMultiple1 = input(true, "", inline="band_1")
stdDevMultiplier1 = input(1.0, "Std. Dev Multiplier 1", inline="band_1")
showStdDevMultiple2 = input(true, "", inline="band_2")
stdDevMultiplier2 = input(2.0, "Std. Dev Multiplier 2", inline="band_2")
showStdDevMultiple3 = input(false, "", inline="band_3")
stdDevMultiplier3 = input(3.0, "Std. Dev Multiplier 3", inline="band_3")

var resetGroup = "Reset Times"
reset1 = input(false, "Reset 1", group=resetGroup, inline="Reset1")
reset1Hour = input(8, " ", group=resetGroup, inline="Reset1")
reset1Minute = input(30, " ", group=resetGroup, inline="Reset1")
reset2 = input(false, "Reset 2", group=resetGroup, inline="Reset2")
reset2Hour = input(15, " ", group=resetGroup, inline="Reset2")
reset2Minute = input(00, " ", group=resetGroup, inline="Reset2")

var alertsGroup = "Alerts"
alertTooltip = "Changing this option does NOT affect existing alerts.  If you want to change when alerts are triggered, you will need to update existing alerts."
alertSession = input.session("1000-1530", "Alert Between", group = alertsGroup, tooltip = alertTooltip)
alertPlus3 = input(false, "+3 SD", group=alertsGroup, tooltip = alertTooltip)
alertPlus2 = input(false, "+2 SD", group=alertsGroup, tooltip = alertTooltip)
alertPlus1 = input(false, "+1 SD", group=alertsGroup, tooltip = alertTooltip)
alertPlus0 = input(false, "VWAP", group=alertsGroup, tooltip = alertTooltip)
alertNeg1 = input(false, "-1 SD", group=alertsGroup, tooltip = alertTooltip)
alertNeg2 = input(false, "-2 SD", group=alertsGroup, tooltip = alertTooltip)
alertNeg3 = input(false, "-3 SD", group=alertsGroup, tooltip = alertTooltip)
showAlerts = input(true, "Show Alerts on Chart", group=alertsGroup)
realtimeAlerts = input(false, "Realtime Alerts", group = alertsGroup, 
     tooltip = "Alert as soon as the conditions are met.  When set to false, detects cross events only after the current bar closes.  NOTE: Setting this to true may create false alarms.")

//
// VWAP 
//

timezoneVal = overrideTimeZone ? timezoneOverride : syminfo.timezone

var float reset1Time = na
var float reset2Time = na

if dayofmonth(time) != dayofmonth(time[1])
    reset1Time := timestamp(timezoneVal, year(time), month(time), dayofmonth(time), reset1Hour, reset1Minute)
    reset2Time := timestamp(timezoneVal, year(time), month(time), dayofmonth(time), reset2Hour, reset2Minute)

inReset1Time = time >= reset1Time
inReset2Time = time >= reset2Time

resetCalc = if reset1 or reset2 
    (reset1 and inReset1Time and not inReset1Time[1]) or (reset2 and inReset2Time and not inReset2Time[1])
else
    (startCalcInPremarket and session.isfirstbar) or (startCalcInPremarket == false and session.isfirstbar_regular)

[vwap, vwapSdHigh, vwapSdLow] = ta.vwap(src, resetCalc, 1.0)
vwapSd = (vwapSdHigh - vwap)

vwapStdDev1AbsVal = vwapSd * stdDevMultiplier1
vwapPlus1 = vwap + vwapStdDev1AbsVal
vwapNeg1 = vwap - vwapStdDev1AbsVal

vwapStdDev2AbsVal = vwapSd * stdDevMultiplier2
vwapPlus2 = vwap + vwapStdDev2AbsVal
vwapNeg2 = vwap - vwapStdDev2AbsVal

vwapStdDev3AbsVal = vwapSd * stdDevMultiplier3
vwapPlus3 = vwap + vwapStdDev3AbsVal
vwapNeg3 = vwap - vwapStdDev3AbsVal

// Show plots if current candle is in the specified display time range
isInRange = not na(time(timeframe.period, validTimeRange, timezoneVal))
firstVisibleCandle = isInRange and not isInRange[1]

vwapVal = showVWAP and isInRange or (alwaysShowVWAP and session.isfirstbar == false) ? vwap : na
vwapPlus1Val = isInRange and showStdDevMultiple1 ? vwapPlus1 : na
vwapPlus2Val = isInRange and showStdDevMultiple2 ? vwapPlus2 : na
vwapPlus3Val = isInRange and showStdDevMultiple3 ? vwapPlus3 : na
vwapNeg1Val = isInRange and showStdDevMultiple1 ? vwapNeg1 : na
vwapNeg2Val = isInRange and showStdDevMultiple2 ? vwapNeg2 : na
vwapNeg3Val = isInRange and showStdDevMultiple3 ? vwapNeg3 : na

var plus1Color = color.new(#ffe600, 20)
var plus2Color = color.new(color.orange, 60)
var plus3Color = color.new(color.yellow, 80)
plot(vwapPlus3Val, "SD +3", color = plus3Color, linewidth = 1, style=plot.style_linebr, display = showStdDevMultiple3 ? display.all : display.none)
plot(vwapPlus2Val, "SD +2", color = plus2Color, linewidth=2, style=plot.style_linebr, display = showStdDevMultiple2 ? display.all : display.none)
plot(vwapPlus1Val, "SD +1", color = plus1Color, style=plot.style_cross, display = showStdDevMultiple1 ? display.all : display.none)

vwapColor = color.new(color.white, 40)
plot(vwapVal, "VWAP", color = vwapColor, style=plot.style_linebr, display = showVWAP ? display.all : display.none)

var neg1Color = color.new(#6ef372, 20)
var neg2Color = color.new(color.green, 60)
var neg3Color = color.new(color.lime, 80)
plot(vwapNeg1Val, "SD -1", color = neg1Color, style=plot.style_cross, display = showStdDevMultiple1 ? display.all : display.none)
plot(vwapNeg2Val, "SD -2", color = neg2Color, linewidth=2, style=plot.style_linebr, display = showStdDevMultiple2 ? display.all : display.none)
plot(vwapNeg3Val, "SD -3", color = neg3Color, linewidth = 1, style=plot.style_linebr, display = showStdDevMultiple3 ? display.all : display.none)

//
// Alerts
//
inBarAlertTime = realtimeAlerts or barstate.isconfirmed
inAlertTime = not na(time("", alertSession, timezoneVal)) and inBarAlertTime
firstAlertCandle = inAlertTime and not inAlertTime[1]

barCross(val) => ta.crossunder(low, val) or ta.crossover(high, val)

plus1Hit = barCross(vwapPlus1) or (firstVisibleCandle and high > vwapPlus1)
plus2Hit = barCross(vwapPlus2) or (firstVisibleCandle and high > vwapPlus2)
plus3Hit = barCross(vwapPlus3) or (firstVisibleCandle and high > vwapPlus3)

neg1Hit = barCross(vwapNeg1) or (firstVisibleCandle and low < vwapNeg1)
neg2Hit = barCross(vwapNeg2) or (firstVisibleCandle and low < vwapNeg2)
neg3Hit = barCross(vwapNeg3) or (firstVisibleCandle and low < vwapNeg3)

vwapHit = barCross(vwap)

triggerPlus1HitAlert = alertPlus1 and ((inAlertTime and plus1Hit) or (firstAlertCandle and high > vwapPlus1))
triggerPlus2HitAlert = alertPlus2 and ((inAlertTime and plus2Hit) or (firstAlertCandle and high > vwapPlus2))
triggerPlus3HitAlert = alertPlus3 and ((inAlertTime and plus3Hit) or (firstAlertCandle and high > vwapPlus3))
triggerNeg1HitAlert = alertNeg1 and ((inAlertTime and neg1Hit) or (firstAlertCandle and low < vwapNeg1))
triggerNeg2HitAlert = alertNeg2 and ((inAlertTime and neg2Hit) or (firstAlertCandle and low < vwapNeg2))
triggerNeg3HitAlert = alertNeg3 and ((inAlertTime and neg3Hit) or (firstAlertCandle and low < vwapNeg3))
triggerVWAPHitAlert = alertPlus0 and ((inAlertTime and vwapHit))

plus1Marker = false
plus2Marker = false
plus3Marker = false
vwapMarker = false
neg1Marker = false
neg2Marker = false
neg3Marker = false

alertInfo = "  " + syminfo.ticker + ". " + timeframe.period + (timeframe.isminutes ? "m" : "") +  " chart."

if triggerPlus1HitAlert
    alert("+" + str.tostring(stdDevMultiplier1) + " hit." + alertInfo)
    plus1Marker := true

if triggerPlus2HitAlert
    alert("+" + str.tostring(stdDevMultiplier2) + " hit." + alertInfo)
    plus2Marker := true

if triggerPlus3HitAlert
    alert("+" + str.tostring(stdDevMultiplier3) + " hit." + alertInfo)
    plus3Marker := true
    
if triggerVWAPHitAlert
    alert("VWAP hit." + alertInfo)
    vwapMarker := true
    
if triggerNeg1HitAlert
    alert("-" + str.tostring(stdDevMultiplier1) + " hit." + alertInfo)
    neg1Marker := true

if triggerNeg2HitAlert
    alert("-" + str.tostring(stdDevMultiplier2) + " hit." + alertInfo)
    neg2Marker := true

if triggerNeg3HitAlert
    alert("-" + str.tostring(stdDevMultiplier3) + " hit." + alertInfo)
    neg3Marker := true

plotshape(showAlerts ? plus3Marker : na, "+3 Alert", shape.labeldown, location.abovebar, color.red, text="+3", textcolor = color.white, display = display.pane)
plotshape(showAlerts ? plus2Marker : na, "+2 Alert", shape.labeldown, location.abovebar, color.red, text="+2", textcolor = color.white, display = display.pane)
plotshape(showAlerts ? plus1Marker : na, "+1 Alert", shape.labeldown, location.abovebar, color.red, text="+1", textcolor = color.white, display = display.pane)
plotshape(showAlerts ? vwapMarker : na, "VWAP Alert", shape.labeldown, location.abovebar, color.rgb(171, 156, 20), text="V", textcolor = color.white, display = display.pane)
plotshape(showAlerts ? neg3Marker : na, "-3 Alert", shape.labelup, location.belowbar, color.green, text="-3", textcolor = color.white, display = display.pane)
plotshape(showAlerts ? neg2Marker : na, "-2 Alert", shape.labelup, location.belowbar, color.green, text="-2", textcolor = color.white, display = display.pane)
plotshape(showAlerts ? neg1Marker : na, "-1 Alert", shape.labelup, location.belowbar, color.green, text="-1", textcolor = color.white, display = display.pane)

alertcondition(plus3Hit and inBarAlertTime, "+3 Hit", "+3 hit. {{ticker}}")
alertcondition(plus2Hit and inBarAlertTime, "+2 Hit", "+2 hit. {{ticker}}")
alertcondition(plus1Hit and inBarAlertTime, "+1 Hit", "+1 hit. {{ticker}}")
alertcondition(vwapHit and inBarAlertTime, "VWAP Hit", "VWAP Hit. {{ticker}}")
alertcondition(neg3Hit and inBarAlertTime, "-3 Hit", "-3 hit. {{ticker}}")
alertcondition(neg2Hit and inBarAlertTime, "-2 Hit", "-2 hit. {{ticker}}")
alertcondition(neg1Hit and inBarAlertTime, "-1 Hit", "-1 hit. {{ticker}}")

//
// Info Card
//

cardPostion = switch position
    "Bottom Left" => position.bottom_left
    "Bottom Center" => position.bottom_center
    "Bottom Right" => position.bottom_right
    "Top Left" => position.top_left
    "Top Center" => position.top_center
    "Top Right" => position.top_right

var table reportCard = table.new(cardPostion, 2, 1, color.gray, color.black, 1, color.black, 1)

if barstate.islast and showSdInfo and isInRange
    table.cell(reportCard, 0, 0, "VWAP SD", text_color=color.black, bgcolor=color.white)
    table.cell(reportCard, 1, 0, str.tostring(vwapSd, "0.00"), text_color=color.black, bgcolor=color.white)

plot(isInRange ? vwapSd : na, "SD Value", color.gray, display = display.status_line)