VHF Adaptive Linear Regression KAMA
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © simwai

//@version=5
indicator('VHF Adaptive Linear Regression KAMA', 'AL_K', true)

// -- Input --
string resolution = input.timeframe(defval='', title='Resolution', group='General', inline='1')
int kamaLength = input.int(89, minval=6, title='KAMA Length', group='General', inline='2')
float kamaFactor1 = input.float(0.66, step=0.01, title='KAMA Factor 1', group='General', inline='3')
float kamaFactor2 = input.float(0.0645, step=0.001, title='KAMA Factor 2', group='General', inline='3')
string smoothingMethod = input.string('T3', 'Choose Smoothing Method', ['Hann Window', 'T3', 'None'], group='General', inline='4')
float smoothingLength = input.float(3, minval=2, title='Smoothing Length', group='General', inline='4')
bool isVhfEnabled = input.bool(true, 'Enable VHF Adaptiveness', group='General', inline='5')
bool isLinRegEnabled = input.bool(false, 'Enable Linear Regression', group='General', inline='6')
bool isSignalPlotEnabled = input.bool(true, 'Enable Signal Plot', group='General', inline='7')

// Gradient Color Schemas
var string SH1 = 'Red –> Green'
var string SH2 = 'Purple –> Blue'
var string SH3 = 'Yellow –> Dark Green'
var string SH4 = 'White –> Black'
var string SH5 = 'White –> Blue'
var string SH6 = 'White –> Red'
var string SH7 = 'Rainbow'
var color[] colors = array.new_color(na)

bool isGradientEnabled = input.bool(true, 'Enable Gradient', group='Gradient', inline='1')
bool isGradientInverted = input.bool(false, 'Invert Colors', group='Gradient', inline='1')
string gradientScheme = input.string(SH1, 'Choose Color Scheme',  options = [SH1, SH2, SH3, SH4, SH5, SH6, SH7], group='Gradient', inline='2')
int gradientLookback = input.int(14, 'Gradient Lookback', group='Gradient', inline='3')

// -- Colors --
color maximumYellowRed = color.rgb(255, 203, 98) // yellow
color rajah = color.rgb(242, 166, 84) // orange
color magicMint = color.rgb(171, 237, 198)
color lightPurple = color.rgb(193, 133, 243)
color languidLavender = color.rgb(232, 215, 255)
color maximumBluePurple = color.rgb(181, 161, 226)
color skyBlue = color.rgb(144, 226, 244)
color lightGray = color.rgb(214, 214, 214)
color quickSilver = color.rgb(163, 163, 163)
color mediumAquamarine = color.rgb(104, 223, 153)
color carrotOrange = color.rgb(239, 146, 46)

// -- Functions --
// Kaufman's Adaptive Moving Average (KAMA)
kama(float _src, int _period) =>
    if (bar_index > _period)
        tmpVal = 0.0
        len = math.max(_period, 1) // add this line to ensure length is always greater than 0
        tmpVal := nz(tmpVal[1]) + math.pow(((math.sum(math.abs(_src - _src[1]), len) != 0) ? (math.abs(_src - _src[_period]) / math.sum(math.abs(_src - _src[1]), len)) : 0) * (kamaFactor1 - kamaFactor2) + kamaFactor2, 2) * (_src - nz(tmpVal[1]))
        tmpVal

// Credits to @LucF, https://www.pinecoders.com/faq_and_code/#how-can-i-rescale-an-indicator-from-one-scale-to-another
// Rescales series with known min/max to the color array size.
rescale(_src, _min, _max) =>
    var int _size = array.size(colors) - 1
    int _colorStep = int(_size * (_src - _min)) / int(math.max(_max - _min, 10e-10))
    _colorStep := _colorStep > _size ? _size : _colorStep < 0 ? 0 : _colorStep
    int(_colorStep)

colGrad(_src, _min, _max) => array.get(colors, rescale(_src, _min, _max))

// Hann Window Smoothing – Credits 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

// T3 - Credits to @loxx
t3(float _src, float _length, float hot=1, string clean='T3')=>
    a = hot
    _c1 = -a * a * a
    _c2 = 3 * a * a + 3 * a * a * a
    _c3 = -6 * a * a - 3 * a - 3 * a * a * a
    _c4 = 1 + 3 * a + a * a * a + 3 * a * a
    
    alpha = 0.
    
    if (clean == 'T3 New')
        alpha := 2.0 / (2.0 + (_length - 1.0) / 2.0)
    else 
        alpha := 2.0 / (1.0 + _length)

    _t30 = _src, _t31 = _src
    _t32 = _src, _t33 = _src
    _t34 = _src, _t35 = _src
    
    _t30 := nz(_t30[1]) + alpha * (_src -  nz(_t30[1]))
    _t31 := nz(_t31[1]) + alpha * (_t30 - nz(_t31[1]))
    _t32 := nz(_t32[1]) + alpha * (_t31 - nz(_t32[1]))
    _t33 := nz(_t33[1]) + alpha * (_t32 - nz(_t33[1]))
    _t34 := nz(_t34[1]) + alpha * (_t33 - nz(_t34[1]))
    _t35 := nz(_t35[1]) + alpha * (_t34 - nz(_t35[1]))
    out =
         _c1 * _t35 + _c2 * _t34 +
         _c3 * _t33 +  _c4 * _t32
    out

atr(src, length, _high, _low, _close) =>
    tr = math.max(math.max(_high - _low, math.abs(_high - nz(_close[1]))), math.abs(_low - nz(_close[1])))
    atr = ta.ema(tr, length)

// -- Calculation --
[_close, _low, _high] = request.security(syminfo.tickerid, resolution, [close[1], high[1], low[1]], barmerge.gaps_off, barmerge.lookahead_on)

if (smoothingMethod == 'T3')
    _close := t3(_close, smoothingLength)
    _low := t3(_low, smoothingLength)
    _high := t3(_high, smoothingLength)

if (smoothingMethod == 'Hann Window')
    _close := doHannWindow(_close, smoothingLength)
    _low := doHannWindow(_low, smoothingLength)
    _high := doHannWindow(_high, smoothingLength)

int kamaLookback = 3
int _kamaLength = kamaLength

if (isVhfEnabled)
    vhfNoise = 0.
    vhfDiff = (_close > nz(_close[1])) ? _close - nz(_close[1]) : nz(_close[1]) - _close
    vhfNoise := nz(vhfNoise[1]) - nz(vhfDiff[kamaLength]) + vhfDiff

    HCP = ta.highest(_close, kamaLength) 
    LCP = ta.lowest(_close, kamaLength) 
    vhf = (HCP - LCP) / vhfNoise  
    int peradapt = math.round(kamaLength * vhf * 2.0) 
    _kamaLength := peradapt

float kama = isLinRegEnabled ? ta.linreg(kama(_close, _kamaLength), 5, 2) : kama(_close, _kamaLength)

// If you wanna expirement with different calculations like I did:
// float kamaSlope = (kama[kamaLookback] - kama) / (ta.highest(kama, kamaLookback) - ta.lowest(kama, kamaLookback))
// kamaSlope := isLinRegEnabled ? ta.linreg(kamaSlope, kamaLength, 1) : kamaSlope
// float kamaSlope = 5 / (kama[5] - kama)
// float kamaSlope = ta.roc(kama, kamaLookback)
// float kamaSlope = (bar_index[1] - bar_index) / (kama[1] - kama) 

float maDiff = kama - ta.sma(kama, gradientLookback)
float kamaSlope = math.atan(maDiff / atr(kama, gradientLookback, _high, _low, _close)) * (180 / math.pi)

bool enterLong = ta.crossover(_close, kama) and ta.crossover(kama, kama[1]) and kamaSlope > 0.5
bool enterShort = ta.crossunder(_close, kama) and ta.crossunder(kama, kama[1]) and kamaSlope < -0.5

plotshape(enterLong, title='ENTER LONG', style=shape.triangleup, color=magicMint, size=size.tiny, location=location.belowbar)
plotshape(enterShort, title='ENTER SHORT', style=shape.triangledown, color=rajah, size=size.tiny, location=location.abovebar)

alertcondition(enterLong, 'Enter Long', 'Adaptive Fisherized KAMA – Enter Long')
alertcondition(enterShort, 'Enter Short', 'Adaptive Fisherized KAMA – Enter Short')

// Gradient - Credits @e2e4mfck
// Fill the array with colors only on the first iteration of the script
if barstate.isfirst and isGradientEnabled
    if gradientScheme == SH1
        array.push(colors, #ff0000), array.push(colors, #ff2c00), array.push(colors, #fe4200), array.push(colors, #fc5300), array.push(colors, #f96200), array.push(colors, #f57000), array.push(colors, #f07e00), array.push(colors, #ea8a00), array.push(colors, #e39600), array.push(colors, #dca100), array.push(colors, #d3ac00), array.push(colors, #c9b600), array.push(colors, #bfc000), array.push(colors, #b3ca00), array.push(colors, #a6d400), array.push(colors, #97dd00), array.push(colors, #86e600), array.push(colors, #71ee00), array.push(colors, #54f700), array.push(colors, #1eff00)
    else if gradientScheme == SH2
        array.push(colors, #ff00d4), array.push(colors, #f71fda), array.push(colors, #ef2ee0), array.push(colors, #e63ae5), array.push(colors, #de43ea), array.push(colors, #d44bee), array.push(colors, #cb52f2), array.push(colors, #c158f6), array.push(colors, #b75df9), array.push(colors, #ac63fb), array.push(colors, #a267fe), array.push(colors, #966bff), array.push(colors, #8b6fff), array.push(colors, #7e73ff), array.push(colors, #7276ff), array.push(colors, #6479ff), array.push(colors, #557bff), array.push(colors, #437eff), array.push(colors, #2e80ff), array.push(colors, #0082ff)
    else if gradientScheme == SH3
        array.push(colors, #fafa6e), array.push(colors, #e0f470), array.push(colors, #c7ed73), array.push(colors, #aee678), array.push(colors, #97dd7d), array.push(colors, #81d581), array.push(colors, #6bcc86), array.push(colors, #56c28a), array.push(colors, #42b98d), array.push(colors, #2eaf8f), array.push(colors, #18a48f), array.push(colors, #009a8f), array.push(colors, #00908d), array.push(colors, #008589), array.push(colors, #007b84), array.push(colors, #0c707d), array.push(colors, #196676), array.push(colors, #215c6d), array.push(colors, #275263), array.push(colors, #2a4858)
    else if gradientScheme == SH4
        array.push(colors, #ffffff), array.push(colors, #f0f0f0), array.push(colors, #e1e1e1), array.push(colors, #d2d2d2), array.push(colors, #c3c3c3), array.push(colors, #b5b5b5), array.push(colors, #a7a7a7), array.push(colors, #999999), array.push(colors, #8b8b8b), array.push(colors, #7e7e7e), array.push(colors, #707070), array.push(colors, #636363), array.push(colors, #575757), array.push(colors, #4a4a4a), array.push(colors, #3e3e3e), array.push(colors, #333333), array.push(colors, #272727), array.push(colors, #1d1d1d), array.push(colors, #121212), array.push(colors, #000000)
    else if gradientScheme == SH5
        array.push(colors, #ffffff), array.push(colors, #f4f5fa), array.push(colors, #e9ebf5), array.push(colors, #dee1f0), array.push(colors, #d3d7eb), array.push(colors, #c8cde6), array.push(colors, #bdc3e1), array.push(colors, #b2b9dd), array.push(colors, #a7b0d8), array.push(colors, #9ca6d3), array.push(colors, #919dce), array.push(colors, #8594c9), array.push(colors, #7a8bc4), array.push(colors, #6e82bf), array.push(colors, #6279ba), array.push(colors, #5570b5), array.push(colors, #4768b0), array.push(colors, #385fab), array.push(colors, #2557a6), array.push(colors, #004fa1)
    else if gradientScheme == SH6
        array.push(colors, #ffffff), array.push(colors, #fff4f1), array.push(colors, #ffe9e3), array.push(colors, #ffded6), array.push(colors, #ffd3c8), array.push(colors, #fec8bb), array.push(colors, #fdbdae), array.push(colors, #fbb2a1), array.push(colors, #f8a794), array.push(colors, #f69c87), array.push(colors, #f3917b), array.push(colors, #f0856f), array.push(colors, #ec7a62), array.push(colors, #e86e56), array.push(colors, #e4634a), array.push(colors, #df563f), array.push(colors, #db4933), array.push(colors, #d63a27), array.push(colors, #d0291b), array.push(colors, #cb0e0e)
    else if gradientScheme == SH7
        array.push(colors, #E50000), array.push(colors, #E6023B), array.push(colors, #E70579), array.push(colors, #E908B7), array.push(colors, #E00BEA), array.push(colors, #A70DEB), array.push(colors, #6E10ED), array.push(colors, #3613EE), array.push(colors, #162DEF), array.push(colors, #1969F1), array.push(colors, #1CA4F2), array.push(colors, #1FDFF4), array.push(colors, #22F5D2), array.push(colors, #25F69C), array.push(colors, #28F867), array.push(colors, #2CF933), array.push(colors, #5DFA2F), array.push(colors, #96FC32), array.push(colors, #CDFD35), array.push(colors, #FFF938)
    
    // Invert colors in array
    if isGradientInverted
        array.reverse(colors)
    
// Colorize the bounded source
color plotColor = isGradientEnabled ? colGrad(kamaSlope, -20, 20) : kamaSlope > 0 ? skyBlue : maximumBluePurple

plot(kama, linewidth=2, color=plotColor, title='KAMA')