Slope Normalizer
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Electrified

//@version=5
//@description This oscillator style indicator takes another indicator as its source and measures the change over time (the slope).  It then isolates the positive slope values from the negative slope values to determine a 'normal' slope value for each. [i]A 'normal' value of 1.0 is determined by the sum of the average slope plus the standard deviation of that slope.[/i]
indicator("Slope Normalizer")

hot = color.orange, hot_line = color.new(hot, 50), hot_bg = color.new(hot, 75)
cold = color.rgb(128,128,255), cold_line = color.new(cold, 50), cold_bg = color.new(cold, 75)

import Electrified/Time/7
MINUTES = "Minutes", DAYS = "Days", BARS = "Bars"


SOURCE = "Source", LENGTH = "Length"
src = input.source(hlc3, SOURCE, "The source to measure the slope.")
len = input.int(1, LENGTH, 1, tooltip = "The number of bars to measure the slope.")
change = ta.change(src, len)

DEVIATION = "Deviation Calculation"
var mLen = Time.spanToIntLen(
  input.float(400, LENGTH, minval=0.5, step=0.5, inline=LENGTH, group=DEVIATION),
  input.string(BARS, "", options = [BARS, MINUTES, DAYS], inline=LENGTH, group=DEVIATION, tooltip = "The length of the deviation measurement."))

trim(float[] a, len) =>
    if array.size(a) > len
        array.shift(a)

var pos = array.new_float()
var neg = array.new_float()
if(change > 0)
    array.push(pos, change)
    trim(pos, mLen)
if(change < 0)
    array.push(neg, change)
    trim(neg, mLen)

type Levels
    float level1
    float level2
    float stdev
    // reamaining should scale linearly.

getLevels(float[] values) =>
    avg = math.abs(array.avg(values))
    stdev = array.stdev(values)
    level1 = avg + stdev
    level2 = level1 + stdev
    Levels.new(level1, level2, stdev)

posLevels = getLevels(pos)
negLevels = getLevels(neg)

normalized = if change > 0
    if(change < posLevels.level1)
        change / posLevels.level1
    else
        (change - posLevels.level1) / posLevels.stdev + 1
else if change < 0
    if(change > -negLevels.level1)
        change / negLevels.level1
    else
        (change + negLevels.level1) / negLevels.stdev - 1
else
    0

plot(normalized, "Slope Normalized", color.white, 2)
plot(normalized > 0 ? normalized : 0, "Slope Normalized, Positive", hot_line, 0, style = plot.style_area, display = display.pane)
plot(normalized < 0 ? normalized : 0, "Slope Normalized, Negative", cold_line, 0, style = plot.style_area, display = display.pane)

bgcolor(normalized> 0 ? hot_bg : cold_bg, title = "Up/Down Highlight")

hline(+4, "+4", hot_line, display = display.none)
hline(+3, "+3", hot_line)
hline(+2, "+2", hot_line)
hline(+1, "+1", hot_line)
hline(0, "0")
hline(-1, "-1", cold_line)
hline(-2, "-2", cold_line)
hline(-3, "-3", cold_line)
hline(-4, "-4", cold_line, display = display.none)
