Hey everyone, I built this VWAP Deviation Band indicator that plots dynamic 1, 2, and 3 standard deviation bands around VWAP using either daily, weekly, or monthly aggregation. It also marks potential buy signals with arrows when a doji, hammer, or bullish engulfing candle forms near the VWAP (within ±0.2%). You can toggle the bands on/off individually, or hide all the lines using the showlines input if you just want the signals. It’s a good tool for spotting potential reversals or value zones during pullbacks. Let me know what you think or if you have ideas to improve it!
Ruby:
declare upper;
# Inputs
input showlines = yes;
input show1 = yes;
input show2 = yes;
input show3 = yes;
input numDevDn = -2.0;
input numDevUp = 2.0;
input devlength = 20;
input timeFrame = {default DAY, WEEK, MONTH};
# Definitions
def h = high;
def l = low;
def c = close;
def o = open;
def na = Double.NaN;
# VWAP (from Charles Schwab)
def cap = GetAggregationPeriod();
def errorInAggregation =
timeFrame == timeFrame.DAY and cap >= AggregationPeriod.WEEK or
timeFrame == timeFrame.WEEK and cap >= AggregationPeriod.MONTH;
Assert(!errorInAggregation, "timeFrame should be not less than current chart aggregation period");
def yyyyMmDd = GetYYYYMMDD();
def periodIndx;
switch (timeFrame) {
case DAY:
periodIndx = yyyyMmDd;
case WEEK:
periodIndx = Floor((DaysFromDate(First(yyyyMmDd)) + GetDayOfWeek(First(yyyyMmDd))) / 7);
case MONTH:
periodIndx = RoundDown(yyyyMmDd / 100, 0);
}
def isPeriodRolled = CompoundValue(1, periodIndx != periodIndx[1], yes);
def volumeSum;
def volumeVwapSum;
def volumeVwap2Sum;
if (isPeriodRolled) {
volumeSum = volume;
volumeVwapSum = volume * vwap;
volumeVwap2Sum = volume * Sqr(vwap);
} else {
volumeSum = CompoundValue(1, volumeSum[1] + volume, volume);
volumeVwapSum = CompoundValue(1, volumeVwapSum[1] + volume * vwap, volume * vwap);
volumeVwap2Sum = CompoundValue(1, volumeVwap2Sum[1] + volume * Sqr(vwap), volume * Sqr(vwap));
}
def price = volumeVwapSum / volumeSum;
def deviation = Sqrt(Max(volumeVwap2Sum / volumeSum - Sqr(price), 0));
# VWAP Calculations
def vwapvalue = price;
def tp = (h + l + c) / 3;
def d = StDev(tp - vwapvalue, devlength);
def u1 = vwapvalue + d;
def l1 = vwapvalue - d;
def u2 = vwapvalue + 2 * d;
def l2 = vwapvalue - 2 * d;
def u3 = vwapvalue + 3 * d;
def l3 = vwapvalue - 3 * d;
# Candle Pattern Logic
def bs = AbsValue(c - o);
def pbs = AbsValue(c[1] - o[1]);
def ch = h;
def cl = l;
def uw = ch - Max(o, c);
def lw = Min(o, c) - cl;
def cr = ch - cl;
def sb = bs <= (cr * 0.3);
def llw = lw >= (bs * 2);
def sup = uw <= (bs * 0.5);
def r = h - l;
def mr = r > TickSize() * 5;
def isBullCurrent = c > o;
def isBearPrev = c[1] < o[1];
def bullishEngulfing = isBullCurrent and isBearPrev and o <= c[1] and c >= o[1];
def isHammer = sb and llw and sup and c > o;
def isDoji = bs <= r * 0.1 and mr;
# Trade Signal
def nearVWAP = (l <= vwapvalue * 1.002) and (h >= vwapvalue * 0.998);
def buy = (isDoji or isHammer or bullishEngulfing) and nearVWAP;
# Plots
plot vwapline = if showlines then vwapvalue else na;
vwapline.SetDefaultColor(Color.YELLOW);
vwapline.SetStyle(Curve.FIRM);
plot upper1 = if showlines and show1 then u1 else na;
plot lower1 = if showlines and show1 then l1 else na;
upper1.SetDefaultColor(Color.GREEN);
lower1.SetDefaultColor(Color.RED);
upper1.SetStyle(Curve.FIRM);
lower1.SetStyle(Curve.FIRM);
plot upper2 = if showlines and show2 then u2 else na;
plot lower2 = if showlines and show2 then l2 else na;
upper2.SetDefaultColor(Color.DARK_GREEN);
lower2.SetDefaultColor(Color.DARK_RED);
upper2.SetStyle(Curve.FIRM);
lower2.SetStyle(Curve.FIRM);
plot upper3 = if showlines and show3 then u3 else na;
plot lower3 = if showlines and show3 then l3 else na;
upper3.SetDefaultColor(Color.WHITE);
lower3.SetDefaultColor(Color.WHITE);
upper3.SetStyle(Curve.FIRM);
lower3.SetStyle(Curve.FIRM);
plot SignalUp = if buy then l - TickSize() else Double.NaN;
SignalUp.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
SignalUp.SetDefaultColor(Color.GREEN);
SignalUp.SetLineWeight(2);
Last edited: