Nadaraya-Watson Oscillator
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// ©FractalTrade15, original Nadaraya_Watson Kernel code and library by ©jdehorty 
// @version=5

indicator(title="Nadaraya-Watson Oscillator", shorttitle = "NWO" , overlay=false, timeframe='')
// Libraries
import jdehorty/KernelFunctions/2 as kernels 

//Functions

darkenColor(color, factor) =>
    r = color.r(color)
    g = color.g(color)
    b = color.b(color)

    rd = int(r * factor)
    gd = int(g * factor)
    bd = int(b * factor)
    color.rgb(rd, gd, bd)

getBounds(_atr, _nearFactor, _farFactor, _yhat) => 
    _upper_far = _yhat + _farFactor*_atr
    _upper_near = _yhat + _nearFactor*_atr
    _lower_near = _yhat - _nearFactor*_atr
    _lower_far = _yhat - _farFactor*_atr
    _upper_avg = (_upper_far + _upper_near) / 2
    _lower_avg = (_lower_far + _lower_near) / 2 
    [_upper_near, _upper_far, _upper_avg, _lower_near, _lower_far, _lower_avg]

kernel_atr(length, _high, _low, _close) =>
    trueRange = na(_high[1])? _high-_low : math.max(math.max(_high - _low, math.abs(_high - _close[1])), math.abs(_low - _close[1]))
    ta.rma(trueRange, length)

f_c_gradientAdvDecPro(_source, _center, _steps, _c_bearWeak, _c_bearStrong, _c_bullWeak, _c_bullStrong) =>
    var float _qtyAdvDec = 0.
    var float _maxSteps = math.max(1, _steps)
    bool _xUp = ta.crossover(_source, _center)
    bool _xDn = ta.crossunder(_source, _center)
    float _chg = ta.change(_source)
    bool _up = _chg > 0
    bool _dn = _chg < 0
    bool _srcBull = _source > _center
    bool _srcBear = _source < _center
    _qtyAdvDec := _srcBull ? _xUp ? 1 : _up ? math.min(_maxSteps, _qtyAdvDec + 1) : _dn ? math.max(1, _qtyAdvDec - 1) : _qtyAdvDec : _srcBear ? _xDn ? 1 : _dn ? math.min(_maxSteps, _qtyAdvDec + 1) : _up ? math.max(1, _qtyAdvDec - 1) : _qtyAdvDec : _qtyAdvDec
    var color _return = na
    _return := _srcBull ? color.from_gradient(_qtyAdvDec, 1, _maxSteps, _c_bullWeak, _c_bullStrong) : _srcBear ? color.from_gradient(_qtyAdvDec, 1, _maxSteps, _c_bearWeak, _c_bearStrong) : _return
    _return

// Kernel Settings
h = input.int(8, 'Lookback Window', tooltip='The number of bars used for the estimation. This is a sliding value that represents the most recent historical bars. Recommended range: 3-50', group='Kernel Settings')
alpha = input.float(8., 'Relative Weighting', step=0.25, tooltip='Relative weighting of time frames. As this value approaches zero, the longer time frames will exert more influence on the estimation. As this value approaches infinity, the behavior of the Rational Quadratic Kernel will become identical to the Gaussian kernel. Recommended range: 0.25-25', group='Kernel Settings')
x_0 = input.int(25, "Start Regression at Bar", tooltip='Bar index on which to start regression. The first bars of a chart are often highly volatile, and omission of these initial bars often leads to a better overall fit. Recommended range: 5-25', group='Kernel Settings')

atr_length = input.int(21, 'ATR Length', minval=1, tooltip='The number of bars associated with the Average True Range (ATR).', group = "Zones")
nearFactor = input.float(2.25, 'Near ATR Factor', minval=0.5, step=0.25, tooltip='The factor by which to multiply the ATR to calculate the near bound of the envelope. Recommended range: 0.5-2.0', group = "Zones")
farFactor = input.float(3, 'Far ATR Factor', minval=1.0, step=0.25, tooltip='The factor by which to multiply the ATR to calculate the far bound of the envelope. Recommended range: 6.0-8.0', group = "Zones")
mid_width = input.float(0.5, title = "Middle Band Width",minval=0.1, step=0.1, tooltip='The size of the centerline band', group = "Zones")

hull_on = input.bool(true, title = "", group = "Hull NW MA", inline = "Hull")
hull_length = input.int(55, minval=1, title = "Hull Smoothing Length", group = "Hull NW MA", inline = "Hull", tooltip = "Adds a Hull Moving Average to the Nadaraya Oscillator")


//Calculations
yhat_close = kernels.rationalQuadratic(close, h, alpha, x_0)
yhat_high = kernels.rationalQuadratic(high, h, alpha, x_0)
yhat_low = kernels.rationalQuadratic(low, h, alpha, x_0)
yhat = yhat_close
ktr = kernel_atr(atr_length, yhat_high, yhat_low, yhat_close)
[upper_near, upper_far, upper_avg, lower_near, lower_far, lower_avg] = getBounds(ktr, nearFactor, farFactor, yhat_close)
avg_factor = math.avg(nearFactor,farFactor)
z_nad = (close - yhat_close) / ktr // Calculates the oscillator from the Kernel
hullma = ta.hma(z_nad, hull_length)


// Colors
bullc = input(color.green, title = "Bull Color", group = "Colors")
bearc = input(color.red, title = "Bear Color", group = "Colors")
neutc = input(color.gray, title = "Neutral Color", group = "Colors")

grad = z_nad > 0? color.from_gradient(z_nad, 0,nearFactor,neutc,bullc) : color.from_gradient(z_nad, -nearFactor,0,bearc,neutc)
color black = color.rgb(0,0,0)
grad2 = f_c_gradientAdvDecPro(yhat_close, ta.ema(yhat_close,2), 5, darkenColor(bearc, 0.5), bearc, darkenColor(bullc, 0.5), bullc) 


// Plots
p_upper_far = plot(farFactor,   color=color.new(bearc,90), title='Upper Boundary: Far')
p_upper_near = plot(nearFactor, color=color.new(bearc,90), title='Upper Boundary: Near')
p_yhath = plot(mid_width, color=color.new(bullc,97), linewidth=1, title='Nadaraya-Watson Estimation')
p_yhat = plot(0, color=color.new(grad2,50), linewidth=1, title='Nadaraya-Watson Estimation')
p_yhatl = plot(-mid_width, color=color.new(bearc,97), linewidth=1, title='Nadaraya-Watson Estimation')
p_lower_near = plot(-nearFactor, color=color.new(bullc,90), title='Lower Boundary: Near')
p_lower_far =  plot(-farFactor,  color=color.new(bullc,90), title='Lower Boundary: Far')

fill(p_yhath,p_yhat,mid_width,0,color.new(bullc,85), color.new(#000000, 90))
fill(p_yhatl,p_yhat,0,-mid_width,color.new(#000000,90), color.new(bearc,85))

fill(p_upper_far, p_upper_near, farFactor,nearFactor, color.new(color.black,93),color.new(bearc,80), title='Upper Boundary: Farmost Region')
fill(p_lower_far, p_lower_near, -nearFactor, -farFactor, color.new(bullc,80),color.new(color.black,93), title='Lower Boundary: Farmost Region')

plot(z_nad, "Z Shadow", color = color.new(neutc,90), linewidth = 8)
p_zn = plot(z_nad, color=grad, title='Nadaraya-Watson Estimator')

fill(p_yhat,p_zn, z_nad >= 0? farFactor+1 : na, z_nad >= 0? 0 : na, z_nad >= 0? color.new(bullc,34) : na, z_nad >= 0? black : na)
fill(p_yhat,p_zn, z_nad <= 0? 0: na, z_nad <= 0 ?-farFactor-1 : na,z_nad <= 0? black : na, z_nad <= 0? color.new(bearc,34) : na)

p_hull = plot(hull_on? hullma : na, color = hullma > hullma[2]? color.new(bullc,34) : color.new(bearc,34))
bgcolor(z_nad > farFactor? color.new(bearc,90) : z_nad < -farFactor? color.new(bullc,90) : na)





