Volume Adaptive Bollinger Bands (MZ VABB)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © MightyZinger
//@version=4

study("Volume Adaptive Bollinger Bands (MZ VABB)", shorttitle="MZ VABB", overlay=true)

uha  =input(true, title="Use Heikin Ashi Candles for Volume Oscillator Calculations")

// Use only Heikinashi Candles for all calculations
haclose = uha ? security(heikinashi(syminfo.tickerid), timeframe.period, close) : security(syminfo.tickerid, timeframe.period, close)
haopen = uha ? security(heikinashi(syminfo.tickerid), timeframe.period, open) : security(syminfo.tickerid, timeframe.period, open)
hahigh = security(syminfo.tickerid, timeframe.period, high)
halow = security(syminfo.tickerid, timeframe.period, low)

vol = security(syminfo.tickerid, timeframe.period, volume)

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////                     Source Options                       //////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

// ─── Different Sources Options List ───► [
string SRC_Tv = "Use traditional TradingView Sources "
string SRC_Wc = "(high + low + (2 * close)) / 4"
string SRC_Wo = "close+high+low-2*open"
string SRC_Wi = "(close+high+low) / 3"
string SRC_Ex = "close>open ? high : low"
string SRC_Hc = "Heikin Ashi Close"
string src_grp = "Source Parameters"

// ●───────── Inputs ─────────● {
diff_src	= input(SRC_Wi, "→ Different Sources Options", 	options=[SRC_Tv, SRC_Wc, SRC_Wo, SRC_Wi, SRC_Ex, SRC_Hc], group = src_grp)
i_sourceSetup	= input(close, "  ‍↳ Source Setup", group = src_grp)
uha_src   =input(true, title="‍↳ Use Heikin Ashi Candles for Different Source Calculations", group = src_grp)
i_Symmetrical 	= input(true, "‍↳ Apply Symmetrically Weighted Moving Average at the price source?", group = src_grp)

// Heikinashi Candles for calculations
h_close = uha_src ? security(heikinashi(syminfo.tickerid), timeframe.period, close) : security(syminfo.tickerid, timeframe.period, close)
h_open = uha_src ? security(heikinashi(syminfo.tickerid), timeframe.period, open) : security(syminfo.tickerid, timeframe.period, open)
h_high = security(syminfo.tickerid, timeframe.period, high)
h_low = security(syminfo.tickerid, timeframe.period, low)
// Get Source 
src_o =
	 diff_src == SRC_Wc ? (high + low + (2 * h_close)) / 4 :
	 diff_src == SRC_Wo ? h_close+h_high+h_low-2*h_open :
	 diff_src == SRC_Wi ? (h_close+h_high+h_low) / 3:
	 diff_src == SRC_Ex ? h_close>h_open ? h_high : h_low :
	 diff_src == SRC_Hc ? security(heikinashi(syminfo.tickerid), timeframe.period, close) :
	 i_sourceSetup

src_f = i_Symmetrical ? swma(src_o) : src_o	 // Symmetrically Weighted Moving Average?

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////                     Dynamic Inputs                       //////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

// Basis (AEDSMA) Parameters
grp1 = "Basis (AEDSMA) Parameters"
grp2 = "Adapt Dynamic Length Based on (Max Length if both left unchecked)"
grp3 = "Show Signals and Dynamic Coloring Based on"

minLength   = input(50, title="Basis MA Minimum Length:", group = grp1, inline = "bs1")
maxLength   = input(100, title="Basis MA Maximum Length:", group = grp1, inline = "bs1")
ssfLength = input(title="EDSMA - Super Smoother Filter Length", type=input.integer, minval=1, defval=20, group = grp1)
ssfPoles = input(title="EDSMA - Super Smoother Filter Poles", type=input.integer, defval=2, options=[2, 3], group = grp1)
adaptPerc    = input(3.141, minval = 0, maxval = 100, title="Adapting Percentage:", group = grp1) / 100.0
mult = input(2.0, minval=0.001, maxval=50, title="StdDev", group = grp1)
offset = input(0, "Offset", minval = -500, maxval = 500, group = grp1)

// Adaptive Length Checks
ch1 = "Slope"
ch2 = "Volume"
ch3 = "Volatility"

volume_chk = input(true, title= ch2, group= grp2, inline="len_chks")
volat_chk = input(false, title= ch3, group= grp2, inline="len_chks")

t_slp_chk = input(true, title= ch1, group= grp3, inline="trd_chks")
t_volume_chk = input(true, title= ch2, group= grp3, inline="trd_chks")
t_volat_chk = input(false, title= ch3, group= grp3, inline="trd_chks")

showSignals = input(true, title="Show Possible Signals", group="Plot Parameters")
showFibs = input(true, title="Show Fibonacci Bands", group="Plot Parameters")
showBg = input(true, title="Enable Background Warning Color", group="Plot Parameters")

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

// Volume and other Inputs
// Volume Oscillator Types Input
osc1 = "TFS Volume Oscillator"
osc2 = "On Balance Volume"
osc3 = "Klinger Volume Oscillator"
osc4 = "Cumulative Volume Oscillator"
osc5 = "Volume Zone Oscillator"
osctype = input(title="Volume Oscillator Type", type=input.string, group="RVSI Parameters", defval = osc3, options=[osc1, osc2, osc3, osc4, osc5])

volLen = input(30, minval=1,title="Volume Length", group="MZ RVSI Indicator Parameters")
rvsiLen = input(14, minval=1,title="RVSI Period",  group="MZ RVSI Indicator Parameters")
vBrk = input(50, minval=1,title="RVSI Break point",  group="MZ RVSI Indicator Parameters")

atrFlength         = input(14, title="ATR Fast Length", group="Volatility Dynamic Optimization Parameters")
atrSlength         = input(46, title="ATR Slow Length", group="Volatility Dynamic Optimization Parameters")

slopePeriod = input(34, title="Slope Period", group="Slope Dynamic Optimization Parameters")
slopeInRange = input(25, title="Slope Initial Range", group="Slope Dynamic Optimization Parameters")
flat        = input(17, title="Consolidation area is when slope below:", group="Slope Dynamic Optimization Parameters")

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////                    DSMA Function                         //////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

// Pre-reqs
get2PoleSSF(src, length) =>
    PI = 2 * asin(1)
    arg = sqrt(2) * PI / length
    a1 = exp(-arg)
    b1 = 2 * a1 * cos(arg)
    c2 = b1
    c3 = -pow(a1, 2)
    c1 = 1 - c2 - c3
    
    ssf = 0.0
    ssf := c1 * src + c2 * nz(ssf[1]) + c3 * nz(ssf[2])

get3PoleSSF(src, length) =>
    PI = 2 * asin(1)

    arg = PI / length
    a1 = exp(-arg)
    b1 = 2 * a1 * cos(1.738 * arg)
    c1 = pow(a1, 2)

    coef2 = b1 + c1
    coef3 = -(c1 + b1 * c1)
    coef4 = pow(c1, 2)
    coef1 = 1 - coef2 - coef3 - coef4

    ssf = 0.0
    ssf := coef1 * src + coef2 * nz(ssf[1]) + coef3 * nz(ssf[2]) + coef4 * nz(ssf[3])

//          MA Main function
dsma(src, len) =>
    float result = 0.0
    zeros = src - nz(src[2])
    avgZeros = (zeros + zeros[1]) / 2
    
    // Ehlers Super Smoother Filter 
    ssf = ssfPoles == 2
         ? get2PoleSSF(avgZeros, ssfLength)
         : get3PoleSSF(avgZeros, ssfLength)
        
    // Rescale filter in terms of Standard Deviations
    stdev = stdev(ssf, len)
    scaledFilter = stdev != 0
         ? ssf / stdev
         : 0
        
    alpha = 5 * abs(scaledFilter) / len
        
    edsma = 0.0
    edsma := alpha * src + (1 - alpha) * nz(edsma[1])
    result :=  edsma
    result
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////                Dynamic Length Function                   //////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

dyn_Len(volume_chk, volat_chk, volume_Break, high_Volatility, adapt_Pct, minLength, maxLength) =>
    var float result = avg(minLength,maxLength)
    para = (volume_chk == true and volat_chk == false) ? volume_Break : (volume_chk == false and volat_chk == true) ? high_Volatility : (volume_chk == true and volat_chk == true) ? volume_Break and high_Volatility : na
    result := iff(para, max(minLength, result * (1 - adapt_Pct)), min(maxLength, result * (1 + adapt_Pct)))
    result


//Slope calculation to determine whether market is in trend, or in consolidation or choppy, or might about to change current trend
calcslope(_ma,src,slope_period,range)=>
    pi = atan(1) * 4
    highestHigh = highest(slope_period) 
    lowestLow = lowest(slope_period)
    slope_range = range / (highestHigh - lowestLow) * lowestLow
    dt = (_ma[2] - _ma) / src * slope_range  
    c = sqrt(1 + dt * dt)
    xAngle = round(180 * acos(1 / c) / pi)
    maAngle = iff(dt > 0, -xAngle, xAngle)
    maAngle


//MA coloring function to mark market dynamics 

dyn_col(slp_chk, volume_chk, volat_chk, slp1, slp2, slp3, _flat, vol_Brk_up, vol_Brk_dn, high_Volatility, col_1, col_2, col_3, col_4, col_r) =>
    bool up_para = 0.0
    bool dn_para = 0.0
    bool sc1 = 0.0
    bool sc2 = 0.0
    bool sc3 = 0.0
    bool c1 = 0.0
    bool c2 = 0.0
    bool c3 = 0.0
    bool c4 = 0.0
    bool c5 = 0.0
    var color col = color.green
    
    if volume_chk == true and volat_chk == false
        up_para := vol_Brk_up
        dn_para := vol_Brk_dn
    
    if volume_chk == false and volat_chk == true
        up_para := high_Volatility
        dn_para := high_Volatility
    
    if volume_chk == true and volat_chk == true
        up_para := high_Volatility and vol_Brk_up
        dn_para := high_Volatility and vol_Brk_dn
    
    if volume_chk == false and volat_chk == false
        up_para := na
        dn_para := na
    
    sc1 := slp1 > _flat and slp2 > _flat and slp3 > _flat
    sc2 := slp1 <= _flat and slp1 > -flat and slp2 <= _flat and slp2 > -flat and slp3 <= _flat and slp3 > -flat
    sc3 := slp1 <= -_flat and slp2 <= -_flat and slp3 <= -_flat
    
    c1 := iff(slp_chk == true , sc1 and up_para, up_para)
    c2 := iff(slp_chk == true , sc1 and not up_para, na)
    c3 := iff(slp_chk == true , sc2, na)
    c4 := iff(slp_chk == true , sc3 and dn_para, dn_para)
    c5 := iff(slp_chk == true , sc3 and not dn_para, na) 
    col := c1 ? col_1 : c2 ? col_2 : c3 ? col_r : c4 ? col_3 : c5 ? col_4 : (c2 == c3 == c5 == na ) ? col_r : col_r
    col

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////             Volume Oscillator Functions                  //////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

// Volume Zone Oscillator

zone(_src, _type, _len) =>
    vp = _src > _src[1] ? _type : _src < _src[1] ? -_type : _src == _src[1] ? 0 : 0
    z = 100 * (ema(vp, _len) / ema(_type, _len))

vzo(vol_src, _close) =>
    float result = 0
    zLen = input(21, "VZO Length", minval=1, group="Volume Zone Oscillator Parameters")
    result := zone(_close, vol_src, zLen)
    result

//  Cumulative Volume Oscillator

_rate(cond, tw, bw, body) =>
    ret = 0.5 * (tw + bw + (cond ? 2 * body : 0)) / (tw + bw + body) 
    ret := nz(ret) == 0 ? 0.5 : ret
    ret

cvo(vol_src, _open, _high, _low, _close) =>
    float result = 0
    ema1len = input(defval = 8, title = "EMA 1 Length", minval = 1, group="Cumulative Volume Oscillator Parameters")
    ema2len = input(defval = 21, title = "EMA 1 Length", minval = 1, group="Cumulative Volume Oscillator Parameters")
    obvl = "On Balance Volume"
    cvdo = "Cumulative Volume Delta"
    pvlt = "Price Volume Trend"
    cvtype = input(defval = pvlt, options = [obvl, cvdo, pvlt], group="Cumulative Volume Oscillator Parameters")

    tw = _high - max(_open, _close) 
    bw = min(_open, _close) - _low 
    body = abs(_close - _open) 

    deltaup =  vol_src * _rate(_open <= _close , tw, bw, body) 
    deltadown = vol_src * _rate(_open > _close , tw, bw, body) 
    delta = _close >= _open ? deltaup : -deltadown
    cumdelta = cum(delta)
    float ctl = na
    ctl := cumdelta
    cv = cvtype == obvl ? obv : cvtype == cvdo ? ctl : pvt
    ema1 = ema(cv,ema1len)
    ema2 = ema(cv,ema2len)
    result := ema1 - ema2 
    result

//  Volume Oscillator function

vol_osc(type, vol_src, vol_Len, _open, _high, _low, _close) =>
    float result = 0
    if type=="TFS Volume Oscillator"
        nVolAccum = sum(iff(_close > _open, vol_src, iff(_close < _open, -vol_src, 0))  ,vol_Len)
        result := nVolAccum / vol_Len
    if type=="On Balance Volume"
        result :=  cum(sign(change(_close)) * vol_src)
    if type=="Klinger Volume Oscillator"
        FastX = input(50, minval=1,title="Volume Fast Length", group="Klinger Volume Oscillator Parameters")
        SlowX = input(200, minval=1,title="Volume Slow Length", group="Klinger Volume Oscillator Parameters")
        xTrend = iff(_close > _close[1], vol * 100, -vol * 100)
        xFast = ema(xTrend, FastX)
        xSlow = ema(xTrend, SlowX)
        result :=  xFast - xSlow
    if type=="Cumulative Volume Oscillator"
        result := cvo(vol_src, _open, _high, _low, _close)
    if type=="Volume Zone Oscillator"
        result := vzo(vol_src, _close)
    result
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////


// MA of Volume Oscillator Source
volMA = sma(vol_osc(osctype,vol,volLen,haopen,hahigh,halow,haclose) , rvsiLen) // HMA can also be used just like used in RVSI's original Indicator

// RSI of Volume Oscillator Data
rsivol = rsi(volMA, rvsiLen)
rvsi = hma(rsivol, rvsiLen)

rvsiSlp = calcslope(rvsi, rsivol, slopePeriod, slopeInRange)    // Slope of RVSI

// Volume Breakout Condition
volBrkUp = rvsi > vBrk // and rvsiSlp >=  flat 
volBrkDn = rvsi < vBrk //or rvsiSlp <= -flat //

//Volatility Meter
highVolatility = atr(atrFlength) > atr(atrSlength)                  

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////


dynaLen = dyn_Len(volume_chk, volat_chk, volBrkUp, highVolatility, adaptPerc, minLength, maxLength)

basis = dsma(src_f , int(dynaLen))

dev = mult * stdev(src_f, int(dynaLen))

upper = basis + dev
lower = basis - dev

slopeBasis = calcslope(basis, src_f, slopePeriod, slopeInRange)
slopeUpper = calcslope(upper, src_f, slopePeriod, slopeInRange)  
slopeLower = calcslope(lower, src_f, slopePeriod, slopeInRange)   

up_col = color.lime
w_up_col = color.fuchsia
dn_col = color.red
w_dn_col = color.gray

dynCol = dyn_col(t_slp_chk, t_volume_chk, t_volat_chk, slopeBasis, slopeUpper, slopeLower, flat, volBrkUp, volBrkDn, highVolatility, up_col, w_up_col, dn_col, w_dn_col, color.yellow)


/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

// Live Dynamic Length Table on Chart   

var length_table = table(na)
length_table := table.new(position.bottom_right , columns = 2, rows = 1, border_width = 1)
table.cell(length_table, 0,  0, "VABB Dynamic Length", bgcolor = #cccccc)
table.cell(length_table, 1,  0, tostring(int(dynaLen)), bgcolor = #cccccc)

// SIGNALS

_upSig = dynCol == up_col //or dynCol == w_up_col
_dnSig = dynCol == dn_col //or dynCol == w_dn_col

buy  = _upSig and not _upSig[1] 
sell = _dnSig and not _dnSig[1] 

var sig = 0
if buy and sig <= 0
    sig := 1
if sell and sig >= 0 
    sig := -1

longsignal  = sig ==  1 and (sig !=  1)[1]
shortsignal = sig == -1 and (sig != -1)[1]

// Caution for price crossing outer bands
top_out    = high > upper
bot_out    = low  < lower
upWarn = top_out and not top_out[1]
dnWarn = bot_out and not bot_out[1]

// Signal Plots
atrPos = 0.72 * atr(5)
plotshape(showSignals and buy ?  basis-atrPos: na, style=shape.circle, color= #AADB1E, location=location.absolute, size = size.tiny)
plotshape(showSignals and sell ? basis+atrPos: na, style=shape.circle, color= #E10600, location=location.absolute, size = size.tiny)

plotshape(showSignals and longsignal ? lower-atrPos: na, style = shape.triangleup, color = color.green, location=location.absolute, text = "Long", size = size.small)
plotshape(showSignals and shortsignal ? upper+atrPos: na, style = shape.triangledown, color = color.red, location=location.absolute, text = "Short", size = size.small)

plotchar(showSignals and upWarn ? upper+atrPos: dnWarn? lower-atrPos: na,
     color = upWarn ? #E10600 : dnWarn ? #AADB1E : na, location=location.absolute, char = "⚑", size = size.tiny)

bgScheme = upWarn ? color.rgb(153,71,88,90) : dnWarn ? color.rgb(0,155,119,90): na
bgcolor(showBg ? bgScheme: na)

// BB Plots

plot(basis, "Basis", color=dynCol, offset = offset, linewidth = 3)

up_width = iff(upWarn, 5, 1)

fill_col = iff(sig == 1, color.rgb(0,155,119,85), color.rgb(153,71,88,85) )
p1 = plot(upper, "Upper", color= color.new(fill_col, 5), offset = offset)
p2 = plot(lower, "Lower", color= color.new(fill_col, 5), offset = offset)
fill(p1, p2, title = "Background", color = fill_col)

plotchar(rvsi, "RVSI", "", location.top, color.red)
plotchar(int(dynaLen), "VABB Dynamic Length", "", location.top, dynCol)


// Fibonacci Bands
m1 = input(0.236, minval=0, maxval=1, step=0.01, title="Fib 1", group= "Fibonacci Parameters", inline = "fibs1")
m2 = input(0.382, minval=0, maxval=1, step=0.01, title="Fib 2", group= "Fibonacci Parameters", inline = "fibs1")
m3 = input(0.5, minval=0, maxval=1, step=0.01, title="Fib 3", group= "Fibonacci Parameters", inline = "fibs1")
m4 = input(0.618, minval=0, maxval=1, step=0.01, title="Fib 4", group= "Fibonacci Parameters", inline = "fibs1")
m5 = input(0.764, minval=0, maxval=1, step=0.01, title="Fib 5", group= "Fibonacci Parameters", inline = "fibs1")

upper_1= basis + (m1 * (upper - basis))
upper_2= basis + (m2 * (upper - basis))
upper_3= basis + (m3 * (upper - basis))
upper_4= basis + (m4 * (upper - basis))
upper_5= basis + (m5 * (upper - basis))
lower_1= basis - (m1 * (basis - lower))
lower_2= basis - (m2 * (basis - lower))
lower_3= basis - (m3 * (basis - lower))
lower_4= basis - (m4 * (basis - lower))
lower_5= basis - (m5 * (basis - lower))

f1_col = color.rgb(235,33,46,80)
f2_col = color.rgb(0,135,62,80)
f3_col = color.rgb(0,154,23,80)
f4_col = color.rgb(47,249,36,80)
f5_col = color.rgb(187,0,0,80)

fu1 = plot(showFibs ? upper_1 : na, color=f1_col , linewidth=2, title="0.236")
fu2 = plot(showFibs ? upper_2 : na, color=f2_col , linewidth=2, title="0.382")
fu3 = plot(showFibs ? upper_3 : na, color=f3_col , linewidth=2, title="0.5")
fu4 = plot(showFibs ? upper_4 : na, color=f4_col , linewidth=3, title="0.618")
fu5 = plot(showFibs ? upper_5 : na, color=f5_col , linewidth=2, title="0.764")
fl1 = plot(showFibs ? lower_1 : na, color=f1_col, linewidth=2, title="0.236")
fl2 = plot(showFibs ? lower_2 : na, color=f2_col , linewidth=2, title="0.382")
fl3 = plot(showFibs ? lower_3 : na, color=f3_col , linewidth=2, title="0.5")
fl4 = plot(showFibs ? lower_4 : na, color=f4_col , linewidth=3, title="0.618")
fl5 = plot(showFibs ? lower_5 : na, color=f5_col , linewidth=2, title="0.764")

