[Giankam] Nadaraya-Watson: Envelope (Non-Repainting)

stable
By StacksMooningWhale in Trend Published March 2023 👁 4,916 views 💬 2 comments ★ Staff Pick

Description

This is an envelope implementation of non-repainting Nadaraya-Watson indicator using the Rational Quadratic Kernel: Nadaraya-Watson What is Nadaraya–Watson Regression? Nadaraya–Watson Regression is a type of Kernel Regression, which is a non-parametric method for estimating the curve of best fit for a dataset. Unlike Linear Regression or Polynomial Regression, Kernel Regression does not assume any underlying distribution of the data. For estimation, it uses a kernel function, which is a weighting function that assigns a weight to each data point based on how close it is to the current point. The computed weights are then used to calculate the weighted average of the data points. How is this different from using a Moving Average? A Simple Moving Average is actually a special type of Kernel Regression that uses a Uniform (Retangular) Kernel function. This means that all data points in the specified lookback window are weighted equally. In contrast, the Rational Quadratic Kernel function used in this indicator assigns a higher weight to data points that are closer to the current point. This means that the indicator will react more quickly to changes in the data. Why use the Rational Quadratic Kernel over the Gaussian Kernel? The Gaussian Kernel is one of the most commonly used Kernel functions and is used extensively in many Machine Learning algorithms due to its general applicability across a wide variety of datasets. The Rational Quadratic Kernel can be thought of as a Gaussian Kernel on steroids; it is equivalent to adding together many Gaussian Kernels of differing length scales. This allows the user even more freedom to tune the indicator to their specific needs. The formula for the Rational Quadratic function is: K(x, x') = (1 + ||x - x'||^2 / (2 * alpha * h^2))^(-alpha) where x and x' data are points, alpha is a hyperparameter that controls the smoothness (i.e. overall "wiggle") of the curve, and h is the band length of the kernel. Does this Indicator Repaint? No, this indicator has been intentionally designed to NOT repaint. This means that once a bar has closed, the indicator will never change the values in its plot. This is useful for backtesting and for trading strategies that require a non-repainting indicator. Example:

local alpha = 2
local nearFactor = 1.5
local farFactor = 8
local atr_length = 60
local relative_weight = 8
local lookback_w = 8

local nqk = CC_NQK(alpha, nearFactor, farFactor, atr_length, relative_weight,lookback_w)

Plot(0, 'nqk', nqk.kernel_avg , {color=White, width=4})
PlotBBandsChart(0, 'upper', nqk.up_far, nqk.up_avg, nqk.up_near)
PlotBBandsChart(0, 'lower', nqk.low_far, nqk.low_avg, nqk.low_near)
HaasScript
-- Author: Giankam
DefineCommand('NQK', 'Nadaraya Quadratic Kernel')


local function kernel_regression (_src, _alpha, _r, _h)
    local _currentWeight = 0
    local _cumulativeWeight = 0
    for i = 1, _alpha + 26 do
        local y = _src[i]
        local w = Pow( 1 + (Pow(i, 2) / (Pow(_h, 2) * 2 * _r )), -_r)
         _currentWeight = _currentWeight + (y*w)
         _cumulativeWeight = _cumulativeWeight + w
    end
    return  _currentWeight / _cumulativeWeight
end

local function getBounds(_atr, _nearFactor, _farFactor, _yhat)  
    local _upper_far = _yhat + _farFactor*_atr
    local _upper_near = _yhat + _nearFactor*_atr
    local _lower_near = _yhat - _nearFactor*_atr
    local _lower_far = _yhat - _farFactor*_atr
    local _upper_avg = (_upper_far + _upper_near) / 2
    local _lower_avg = (_lower_far + _lower_near) / 2 
    local _bounds = {_upper_near, _upper_far, _upper_avg, _lower_near, _lower_far, _lower_avg} 
    return _bounds
end

local function kernel_atr(_length, _high, _low, _close)
    local trueRange = TRANGE(_high, _low, _close)
    local k_atr = EMA(trueRange, _length*2)
    return k_atr
end


local alpha = DefineParameter(NumberType, 'Alpha', 'NQK Alpha', true, 2)
local nearFactor = DefineParameter(NumberType, 'nearFactor', 'NQK nearFactor', true, 1.5)
local farFactor = DefineParameter(NumberType, 'farFactor', 'NQK farFactor', true, 8)
local atr_length = DefineParameter(NumberType, 'atr_length', 'NQK atr_length', true, 60)
local relative_weight = DefineParameter(NumberType, 'relative_weight', 'NQK relative_weight', true, 8)
local lookback_w = DefineParameter(NumberType, 'lookback_w', 'NQK lookback_w', true, 8)

local close = ClosePrices()
local high = HighPrices()
local low = LowPrices()


local yhat_close = kernel_regression(close, alpha, relative_weight, lookback_w)
local yhat_high = kernel_regression(high, alpha, relative_weight, lookback_w)
local yhat_low = kernel_regression(low, alpha, relative_weight, lookback_w)
local ktr = kernel_atr(atr_length, yhat_high, yhat_low, yhat_close)
local bounds = getBounds(ktr, nearFactor, farFactor, yhat_close)


local results = {

        kernel_avg = yhat_close,
        up_near = bounds[1],
        up_far = bounds[2],
        up_avg = bounds[3],
        low_near = bounds[4],
        low_far = bounds[5],
        low_avg = bounds[6],
}

DefineOutput(ListDynamicType, results)

2 Comments

Sign in to leave a comment.

P
pshai about 3 years ago

Super.

K
Katerin about 3 years ago

Thank you ^^ excelent