Relative Bi-Directional Volatility Range
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © metka183

//@version=5
indicator(title="Relative Bi-Directional Volatility Range", shorttitle="RBVR2.1", overlay=false, max_bars_back=3000, timeframe="", timeframe_gaps=false)

//Input ----------------------------------
category = "Moving Average Control"
ma_type = input.string("Relative", title="Type", options=["Median", "Simple", "Relative", "Exponential", "Weighted", "Volume Weighted", "Hull 2xL Weighted"], group=category)
ma_len = input.int(8, "Length", minval=1, group=category)
ma_lohi = input.bool(false, "Activate Moving Averages for Volatility Lows and Highs", group=category)
category2 = "Trend Signal Separation"
ma_pow = input.float(0, "Power", minval=0, step=0.5, group=category2, tooltip="This factor potentiates the separation of strong and weak signals. Background color will become brighter for weaker signals. A too high factor can wipe out the weaker signals and therefore make trend indication maybe useless.")

[curO, curC, curH, curL] = request.security(syminfo.tickerid, timeframe.period, [open, close, high, low])
curG = not na(curC[1]) ? curO-curC[1] : 0.
//----------------------------------------

//Volatility -----------------------------
volaC = 0., volaL = 0., volaH = 0., divisor = 1., nrg = 1.
if bar_index > 0
    divisor := math.abs(curC[1]) > 0 ? math.abs(curC[1]) : 0.00000001
    volaC := (curC-curC[1])/divisor
    nrg := ma_pow > 0 ? math.pow(1-1/(1+volaC*volaC), ma_pow) : 1.
    if volaC > 0
        volaL := (curL-curG-curH[1])/divisor
        volaH := (curH-curL[1])/divisor
    else
        volaL := (curL-curH[1])/divisor
        volaH := (curH-curG-curL[1])/divisor
    volaC := volaC > 0 ? 1-1/(1+volaC) : 1/(1-volaC)-1
    volaL := volaL > 0 ? 1-1/(1+volaL) : 1/(1-volaL)-1
    volaH := volaH > 0 ? 1-1/(1+volaH) : 1/(1-volaH)-1
volaC *= 100, volaL *= 100, volaH *= 100
//----------------------------------------

//Moving Average -------------------------
ma = 0., ma_nrg = 0., ma_low = 0., ma_high = 0.
dL = bar_index >= ma_len ? ma_len : bar_index+1 //limitation for insufficient data history
if ma_type == "Relative" or ma_type == "Exponential"
    len = ma_type == "Relative" ? 1/ma_len : 2/(ma_len+1)  //does not need a limitation
    ma := (1-len)*nz(ma[1]) + len*volaC
    ma_nrg := (1-len)*nz(ma_nrg[1]) + len*volaC*nrg
    ma_low := (1-len)*nz(ma_low[1]) + len*volaL
    ma_high := (1-len)*nz(ma_high[1]) + len*volaH
else if ma_type == "Median"
    ma := ta.median(volaC, dL)
    ma_nrg := ta.median(volaC*nrg, dL)
    ma_low := ta.median(volaL, dL)
    ma_high := ta.median(volaH, dL)
else if ma_type == "Simple"
    ma := ta.sma(volaC, dL)
    ma_nrg := ta.sma(volaC*nrg, dL)
    ma_low := ta.sma(volaL, dL)
    ma_high := ta.sma(volaH, dL)
else if ma_type == "Weighted"
    ma := ta.wma(volaC, dL)
    ma_nrg := ta.wma(volaC*nrg, dL)
    ma_low := ta.wma(volaL, dL)
    ma_high := ta.wma(volaH, dL)
else if ma_type == "Volume Weighted"
    ma := ta.vwma(volaC, dL)
    ma_nrg := ta.vwma(volaC*nrg, dL)
    ma_low := ta.vwma(volaL, dL)
    ma_high := ta.vwma(volaH, dL)
else if ma_type == "Hull 2xL Weighted"    
    dL := bar_index >= 2*ma_len ? 2*ma_len : bar_index+1 //make Hull more comparable to Weighted Moving Average
    len = math.round(math.sqrt(dL))
    len2 = math.round(dL/2)
    len2 := dL != 2 ? len2 : 2 //small correction to avoid overshooting
    wma = ta.wma(volaC, dL), wma_nrg = ta.wma(volaC*nrg, dL)
    wma_low = ta.wma(volaL, dL), wma_high = ta.wma(volaH, dL)
    wma2 = 2*ta.wma(volaC, len2), wma2_nrg = 2*ta.wma(volaC*nrg, len2)
    wma2_low = 2*ta.wma(volaL, len2), wma2_high = 2*ta.wma(volaH, len2)
    ma := ta.wma(wma2-wma, len)
    ma_nrg := ta.wma(wma2_nrg-wma_nrg, len)
    ma_low := ta.wma(wma2_low-wma_low, len)
    ma_high := ta.wma(wma2_high-wma_high, len)
//----------------------------------------

//Status and Color -----------------------
var ma_status = 0
var ma_color = color.gray
var ma_color_up = color.purple
var ma_color_down = color.yellow
var fill_color_up = color.rgb(76, 175, 79, 100) 
var fill_color_down = color.rgb(255, 82, 82, 100)
var volaH_color = color.rgb(50, 115, 50, 40)
var volaL_color = color.rgb(175, 50, 50, 40)
ma_sum = 0.
if not na(ma[1])
    if ma_nrg < 0 and ma < 0
        ma_sum -= 1
    else if ma_nrg > 0 and ma < 0
        ma_sum -= 0.5
    else if ma_nrg > 0 and ma > 0
        ma_sum += 1
    else if ma_nrg < 0 and ma > 0
        ma_sum += 0.5

    if ma_status >= 0 and ma_sum < -0.5
        ma_status := -1
    else if ma_status <= 0 and ma_sum > 0.5
        ma_status := 1

    if ma_sum <= -1
        ma_color := ma_color_down
        fill_color_up := color.rgb(76, 175, 79, 100)
        fill_color_down := color.rgb(255, 82, 82, 65)
        volaH_color := color.rgb(50, 115, 50, 40)
        volaL_color := color.rgb(175, 50, 50, 0)
    else if ma_sum < 0
        ma_color := ma_color_down
        fill_color_up := color.rgb(76, 175, 79, 100)
        fill_color_down := color.rgb(255, 82, 82, 85)
        volaH_color := color.rgb(50, 115, 50, 40)
        volaL_color := color.rgb(175, 50, 50, 40)
    else if ma_sum >= 1
        ma_color := ma_color_up
        fill_color_up := color.rgb(76, 175, 79, 65)
        fill_color_down := color.rgb(255, 82, 82, 100)
        volaH_color := color.rgb(50, 115, 50, 0)
        volaL_color := color.rgb(175, 50, 50, 40)
    else if ma_sum > 0
        ma_color := ma_color_up
        fill_color_up := color.rgb(76, 175, 79, 85)
        fill_color_down := color.rgb(255, 82, 82, 100)
        volaH_color := color.rgb(50, 115, 50, 40)
        volaL_color := color.rgb(175, 50, 50, 40)
    else
        ma_color := color.gray
        if ma_status > 0
            fill_color_up := color.rgb(120, 123, 134, 85)
            fill_color_down := color.rgb(255, 82, 82, 100)
        else if ma_status < 0
            fill_color_up := color.rgb(76, 175, 79, 100) 
            fill_color_down := color.rgb(120, 123, 134, 85)
        else
            fill_color_up := color.rgb(76, 175, 79, 100) 
            fill_color_down := color.rgb(255, 82, 82, 100)
            volaH_color := color.rgb(50, 115, 50, 40)
            volaL_color := color.rgb(175, 50, 50, 40)

var volaC_color = color.gray
if volaC > 0
    volaC_color := ma_status > 0 ? color.rgb(76, 175, 79, 0) : color.rgb(76, 175, 79, 40)
else
    volaC_color := ma_status < 0 ? color.rgb(255, 82, 82, 0) : color.rgb(255, 82, 82, 40)
//----------------------------------------

//Trend Shift Signal ---------------------
signal = 0.
if ma_status > 0 and nz(ma_status[1]) <= 0
    signal := 1.0
else if ma_status < 0 and nz(ma_status[1]) >= 0
    signal := -1.0
//----------------------------------------

//Output ---------------------------------
line_center = hline(0, "Center Line", color=color.new(#000000, 30), linestyle=hline.style_solid, linewidth=1)
line_max = hline(100, "Max Line", color=color.new(#000000, 30), linestyle=hline.style_solid, linewidth=1, display=display.none)
line_min = hline(-100, "Min Line", color=color.new(#000000, 30), linestyle=hline.style_solid, linewidth=1, display=display.none)
plot(volaC, "Vola", color=volaC_color, linewidth=2, style=plot.style_columns)                               //Volatility
plot(volaH, "Vola High", color=volaH_color, linewidth=2, display=ma_lohi ? display.none : display.all)      //Volatility High
plot(volaL, "Vola Low", color=volaL_color, linewidth=2, display=ma_lohi ? display.none : display.all)       //Volatility Low
plot(ma, "VMA", color=ma_color, linewidth=2)                                                                //Volatility Moving Average
plot(ma_high, "VMA Heigh", color=volaH_color, linewidth=2, display=ma_lohi ? display.all : display.none)    //Volatility High Moving Average
plot(ma_low, "VMA Low", color=volaL_color, linewidth=2, display=ma_lohi ? display.all : display.none)       //Volatility Low Moving Average
plot(ma_nrg, "PVMA", color=ma_color, linewidth=2, display=display.none)                                     //Powered Volatility Moving Average
plot(signal, "TSS", color=color.new(color.black, 100), linewidth=1, display=display.none)                 //Trend Shift Signal
fill(line_center, line_max, color=fill_color_up, title="Background High")
fill(line_center, line_min, color=fill_color_down, title="Background Low")
//----------------------------------------