// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ //@version=5 //╱//╭╮╱╱/╭╮╱╱╱/╭━╮╭━╮ //╱//┃┃╱//┃┃╱╱//╰╮╰╯╭╯ //╭━━┫┃╭━━┫╰━┳━━╮╰╮╭╯╭┳━━┳╮╭╮ //┃╭╮┃┃┃╭╮┃╭╮┃╭╮┃╭╯╰╮┣┫╭╮┃╰╯┃ //┃╭╮┃╰┫╰╯┃┃┃┃╭╮┣╯╭╮╰┫┃╰╯┃┃┃┃ //╰╯╰┻━┫╭━┻╯╰┻╯╰┻━╯╰━┻┻━━┻┻┻╯ //╱╱╱╱/┃┃ //╱╱╱//╰╯ indicator("Shadow Range Index", "SRI") var string GRP_UI = 'UI' var string M1 = 'Classic' var string M2 = 'Night' // ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ // Inputs len_max_range = input.int(0, '   Range lookback', minval = 0, inline = 's') lengthInput_max = input.int(29, "Filter length", minval = 1, inline='s') len_min_range = input.int(9, 'Shadow range lookback', minval = 0, inline = 'f') lengthInput_min = input.int(7, "Filter length", minval = 1, inline='f') the_m = input.string(M1, "Theme", options = [M1, M2], inline= 'ez', group=GRP_UI) colorbars = input.bool(defval = true, title = 'Bar coloring', inline = 'ez', group=GRP_UI) i_bullColor_c = input.color(#36fc1c, "Up", inline='COLORc', group=GRP_UI) gr1 = input.color(#a5fa9a, "", inline='COLORc', group=GRP_UI) i_bearColor_c = input.color(#ff004c, "Down", inline='COLORc', group=GRP_UI) rd1 = input.color(#f8abab, "", inline='COLORc', group=GRP_UI) i_bullColor_a = input.color(#fbc02d, "Up", inline='COLORa', group=GRP_UI) gr2 = input.color(#fff9c4, "", inline='COLORa', group=GRP_UI) i_bearColor_a = input.color(#c358ed, "Down", inline='COLORa', group=GRP_UI) rd2 = input.color(#e1bbf8, "", inline='COLORa', group=GRP_UI) bullish = the_m == M1 ? i_bullColor_c : the_m == M2 ? i_bullColor_a : na bearish = the_m == M1 ? i_bearColor_c : the_m == M2 ? i_bearColor_a : na gr = the_m == M1 ? gr1 : the_m == M2 ? gr2 : na rd = the_m == M1 ? rd1 : the_m == M2 ? rd2 : na sm_thick = input.int(2, 'Range line thickness', group = GRP_UI) h_fill = input.int(69, 'Shadow fill', group = GRP_UI) // ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ // Functions // Super Smoother Function - Credit to @balipour ss(Series, Period) => var PI = 2.0 * math.asin(1.0) var SQRT2 = math.sqrt(2.0) lambda = PI * SQRT2 / Period a1 = math.exp(-lambda) coeff2 = 2.0 * a1 * math.cos(lambda) coeff3 = -math.pow(a1, 2.0) coeff1 = 1.0 - coeff2 - coeff3 filt1 = 0.0 filt1 := coeff1 * (Series + nz(Series[1])) * 0.5 + coeff2 * nz(filt1[1]) + coeff3 * nz(filt1[2]) filt1 // Hann Window Smoothing – Credit to @cheatcountry doHannWindow(float _series, float _hannWindowLength) => sum = 0.0, coef = 0.0 for i = 1 to _hannWindowLength cosine = 1 - math.cos(2 * math.pi * i / (_hannWindowLength + 1)) sum := sum + (cosine * nz(_series[i - 1])) coef := coef + cosine h = coef != 0 ? sum / coef : 0 // Gaussian Filter - Credit to @AlgoAlpha gaussian(ser, length) => var filter = 0.0 beta = (1 - math.cos(2 * math.pi / length)) / (math.pow(2, 1 / 1) - 1) alpha = -beta + math.sqrt(math.pow(beta, 2) + 2 * beta) filter := alpha * ser + (1 - alpha) * nz(filter[1]) // ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ // Calculations array_max_plus = array.new_float(0) array_max_minus = array.new_float(0) float cum_max_plus = 0 float cum_max_minus = 0 for i = 0 to len_max_range array.push(array_max_plus, math.max(high[i] - open[i], high[i] - close[i + 1])) array.push(array_max_minus, math.max(open[i] - low[i], close[i + 1] - low[i])) cum_max_plus += array.get(array_max_plus, i) / len_max_range cum_max_minus += array.get(array_max_minus, i) / len_max_range vr_max(length) => // Max: Range up = len_max_range > 0 ? cum_max_plus / 2 : math.max(high - open, high - close[1]) // divide by 2 is better scaling visually between the 2 plots. No change in bar plots dn = len_max_range > 0 ? cum_max_minus / 2 : math.max(open - low, close[1] - low) pVR = up > dn and up > 0 ? up : 0 mVR = dn > up and dn > 0 ? dn : 0 result = ss(doHannWindow(pVR - mVR, length), length) [result, up, dn] array_min_plus = array.new_float(0) array_min_minus = array.new_float(0) float cum_min_plus = 0 float cum_min_minus = 0 for i = 0 to len_min_range array.push(array_min_plus, math.min(high[i] - open[i], high[i] - close[i + 1])) array.push(array_min_minus, math.min(open[i] - low[i], close[i + 1] - low[i])) cum_min_plus += array.get(array_min_plus, i) / len_min_range cum_min_minus += array.get(array_min_minus, i) / len_min_range vr_min(length) => // Min: Shadow range up = len_min_range > 0 ? cum_min_plus / 2 : math.min(high - open, high - close[1]) dn = len_min_range > 0 ? cum_min_minus / 2 : math.min(open - low, close[1] - low) pVR = up > dn and up > 0 ? up : 0 mVR = dn > up and dn > 0 ? dn : 0 result = (gaussian(ta.alma(pVR - mVR, length, 0.76, 6), length) + gaussian(gaussian(pVR - mVR, length), length)) / 2 [result, up, dn] [signal_max, max_plus, max_minus] = vr_max(lengthInput_max) [signal_min, min_plus, min_minus] = vr_min(lengthInput_min) // ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ // Plots var sig_sigs = 0 if signal_max > signal_max[1] sig_sigs := 1 sig_sigs if signal_max < signal_max[1] sig_sigs := -1 sig_sigs col_sig = sig_sigs == 1 ? gr : sig_sigs == -1 ? rd : na plot(signal_max, "Range", color = col_sig, linewidth = sm_thick) sig_up = sig_sigs == 1 and sig_sigs[1] == -1 sig_dn = sig_sigs == -1 and sig_sigs[1] == 1 plotshape(sig_up ? signal_max : na, color = color.new(bullish, 0), style = shape.arrowup, location = location.absolute, title = 'Range Up', size = size.tiny) plotshape(sig_dn ? signal_max : na, color = color.new(bearish, 0), style = shape.arrowdown, location = location.absolute, title = 'Range Dn', size = size.tiny) lineColor_min = signal_min > 0 ? color.new(gr, 83) : color.new(rd, 83) sig = plot(signal_min, "Shadow range", color = lineColor_min, linewidth = 1) hline(0, "Zero", #787b8696, linestyle = hline.style_solid) centerPlot = plot(0, color = na, style = plot.style_line, editable = false) areaColor_min = signal_min > 0 ? color.new(gr, h_fill) : color.new(rd, h_fill) colUp = signal_min > 0 ? bullish : areaColor_min, colDn = signal_min > 0 ? areaColor_min : bearish fill(sig, centerPlot, signal_min > 0 ? signal_max : 0, signal_min > 0 ? 0 : signal_max, colUp, colDn) // ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ // Bars & Alerts bar_cols = signal_max > 0 and signal_min > 0 ? bullish : signal_max > 0 and signal_min < 0 ? color.new(rd, 35) : signal_max < 0 and signal_min < 0 ? bearish : signal_max < 0 and signal_min > 0 ? color.new(gr, 35) : na barcolor(colorbars ? bar_cols : na) var tren_sigs = 0 if signal_max > 0 and signal_min > 0 tren_sigs := 1 tren_sigs if signal_max < 0 and signal_min < 0 tren_sigs := -1 tren_sigs goin_up = tren_sigs == 1 and tren_sigs[1] == -1 goin_dn = tren_sigs == -1 and tren_sigs[1] == 1 anySignal = goin_up or goin_dn plotshape(goin_up, color = bullish, style = shape.triangleup, location = location.bottom, title = 'Trend up', size = size.tiny) plotshape(goin_dn, color = bearish, style = shape.triangledown, location = location.top, title = 'Trend down', size = size.tiny) alertcondition(anySignal, title='... SRI Any Alert 🟢🔴') alertcondition(goin_up, title='.. SRI Buy 🟢') alertcondition(goin_dn, title='. SRI Sell 🔴') // ( __)( ( \( \ // ) _) / / ) D ( // (____)\_)__)(____/ // ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟