True Range Osc
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © peacefulLizard50262

//@version=5
indicator("True Range Osc")

source = input.source(close, "Source")
length = input.int(14, "Length", 1)
smooth = input.int(3, "Smoothing", 1)
outlier_level = input.float(1, "Outlier Level", 0.0001, 100, 0.025)

average_true_range(source, length)=>
    true_range = high - low
    _range = math.abs(open - close)
    a_range = (true_range * 2 + _range)/3
    average_true_range = ta.rma(a_range, length)

tr_score(source, length)=>
    mean = ta.sma(source, length)
    tr = average_true_range(source, length)
    z_score = (source - mean)/tr

max(source, outlier_level, dev_lookback)=>
    var float max = na
    src = array.new<float>()
    stdev = math.abs((source - ta.ema(source, dev_lookback))/ta.stdev(source, dev_lookback))
    array.push(src, stdev < outlier_level ? source : -1.7976931348623157e+308)
    max := math.max(nz(max[1]), array.get(src, 0))

min(source, outlier_level, dev_lookback) =>
    var float min = na
    src = array.new<float>()
    stdev = math.abs((source - ta.ema(source, dev_lookback))/ta.stdev(source, dev_lookback))
    array.push(src, stdev < outlier_level ? source : 1.7976931348623157e+308)
    min := math.min(nz(min[1]), array.get(src, 0))

min_max(src, outlier_level, dev_lookback) =>
    out = (src - min(src, outlier_level, dev_lookback))/(max(src, outlier_level, dev_lookback) - min(src, outlier_level, dev_lookback)) * 100
    math.max(math.min(100, out), 0)

true_rance_osc = min_max(ta.sma(tr_score(source, length), smooth), outlier_level, length)

plot(true_rance_osc, "True Range Osc", color.orange)

top = plot(70, "Upper Range", color.gray)
bot = plot(30, "Lower", color.gray)
plot(50, "Center", color.gray)

fill(top, bot, color.new(color.blue, 90))
