AIR Vortex ADX
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/

//  ╭━━╮╱╱╱╱╱╱╭╮╱╱╱╭━┳╮
//  ┃╭╮┣━┳━╮╭┳┫╰┳━╮┃━┫╰╮
//  ┃╭╮┃┻┫╋╰┫╭┫╋┃╋╰╋━┃┃┃
//  ╰━━┻━┻━━┻╯╰━┻━━┻━┻┻╯ @ Woody_Bearbash

//@version=5

indicator('AIR ADX of Vortex', 'AIR Vortex ADX')

var M1 = 'Urban'
var M2 = 'Night'
var M3 = 'Earth'
var M4 = 'Classic'
var M5 = 'Wood'
var M6 = 'Pop'
var M7 = 'Mono'

length          = input.int(34, minval=2, maxval=499, title='Length', group = 'Vortex', inline='1')
threshold       = input.float(0.2, minval=0, maxval=1.0, step=0.025, title='Threshold', group = 'Vortex', inline='1')

smoothingMode   = input.string('Super Smoother', 'Smoothing', options=['Hann Window', 'Super Smoother', 'Wilders', 'None'], group='Vortex', inline='4')
smoothing       = input.int(9, minval=2, title='Length', group = 'Vortex', inline='4')

var string GRP_RF  = '══════ Range mix ══════'
atrActive       = input.bool(true, 'ATR,', inline='42', group=GRP_RF)
atrMult         = input.float(1.0, 'Mult', step=0.1, inline='42', group=GRP_RF)
atr_move        = input.string('Super Smoother', 'MA', options=['Hann Window', 'Super Smoother', 'Wilders', 'None'], group=GRP_RF, inline='42') 

pipActive       = input.bool(true, 'IR,', inline='43', group=GRP_RF)
pipMult         = input.float(0.25, 'Mult', step=0.05, inline='43', group=GRP_RF)
spacer_chop     = input.int(17, '%', inline='43', group=GRP_RF, tooltip = 'IR (Interpercentile range) is to AIR (Average IR) what TR (True range) is to ATR (Average TR)')

airActive       = input.bool(false, 'AIR,', inline='44', group=GRP_RF)
airMult         = input.float(0.3, 'Mult', step=0.1, inline='44', group=GRP_RF)
air_move        = input.string('Hann Window', 'MA', options=['Hann Window', 'Super Smoother', 'Wilders', 'None'], group=GRP_RF, inline='44') 
percent_rng     = input.int(26, '%', inline='44', group=GRP_RF)

var string GRP_RSI  = '══════ Trendless RSI filter ══════'
rsi_threshold   = input.int(6, 'RSI centre-cancel theshold', minval = 0, inline = 'r', group = GRP_RSI)
rsi_len         = input.int(14, 'Length', inline = 'r', group = GRP_RSI, tooltip = 'RSI centres around 50, hence a threshold value of 6 cancels ranging RSI between 44 & 56')

var string GRP_UI  = '══════ UI ══════'

the_m           = input.string(M4, "Theme", options = [M1, M2, M3, M4, M5, M6, M7], inline= 'ez', group=GRP_UI)
switch_col      = input.bool(defval = true, title = 'Bar coloring', inline = 'ez', group=GRP_UI)

i_bullColor_t   = input.color(#fbc02d, 'Up', inline='COLOR', group=GRP_UI)
gr1             = input.color(#eae4a0, '', inline='COLOR', group=GRP_UI)
i_bearColor_t   = input.color(#3179f5, 'Down', inline='COLOR', group=GRP_UI)
rd1             = input.color(#99c1f1, '', inline='COLOR', group=GRP_UI)

i_bullColor_a   = input.color(#fbc02d, "Up", inline='COLORa', group=GRP_UI)
gr2             = input.color(#fff9c4, "", inline='COLORa', group=GRP_UI)
i_bearColor_a   = input.color(#ba4bcd, "Down", inline='COLORa', group=GRP_UI) 
rd2             = input.color(#d8a7f7, "", inline='COLORa', group=GRP_UI) 

i_bullColor_m   = input.color(#388e3c, 'Up', inline='COLORb', group=GRP_UI)
gr3             = input.color(#96df99, '', inline='COLORb', group=GRP_UI)
i_bearColor_m   = input.color(#3179f5, 'Down', inline='COLORb', group=GRP_UI)
rd3             = input.color(#99c1f1, '', inline='COLORb', group=GRP_UI)

i_bullColor_c   = input.color(#57d132, "Up", inline='COLORc', group=GRP_UI)
gr4             = input.color(#b6f1b8, "", inline='COLORc', group=GRP_UI)
i_bearColor_c   = input.color(#e42626, "Down", inline='COLORc', group=GRP_UI)
rd4             = input.color(#f581a8, "", inline='COLORc', group=GRP_UI)

i_bullColor_s   = input.color(#379832, 'Up', inline='COLORs', group=GRP_UI)
gr5             = input.color(#aadba4, '', inline='COLORs', group=GRP_UI)
i_bearColor_s   = input.color(#ffa726, 'Down', inline='COLORs', group=GRP_UI)
rd5             = input.color(#eacfa0, '', inline='COLORs', group=GRP_UI)

i_bullColor_p   = input.color(#8da23d, "Up", inline='COLORcp', group=GRP_UI)
gr6             = input.color(#e4ffc4, "", inline='COLORcp', group=GRP_UI)
i_bearColor_p   = input.color(#7d35b2, "Down", inline='COLORcp', group=GRP_UI)
rd6             = input.color(#b9aef7, "", inline='COLORcp', group=GRP_UI)

i_bullColor_o   = input.color(#9598a1, "Up", inline='COLORcpn', group=GRP_UI)
gr7             = input.color(#e5edf6, "", inline='COLORcpn', group=GRP_UI)
i_bearColor_o   = input.color(#3179f5, "Down", inline='COLORcpn', group=GRP_UI)
rd7             = input.color(#99c1f1, "", inline='COLORcpn', group=GRP_UI)

up_ = the_m == M1 ? i_bullColor_t : the_m == M2 ? i_bullColor_a : the_m == M3 ? i_bullColor_m : the_m == M4 ? i_bullColor_c : the_m == M5 ? i_bullColor_s : the_m == M6 ? i_bullColor_p : the_m == M7 ? i_bullColor_o : na
dn_ = the_m == M1 ? i_bearColor_t : the_m == M2 ? i_bearColor_a : the_m == M3 ? i_bearColor_m : the_m == M4 ? i_bearColor_c : the_m == M5 ? i_bearColor_s : the_m == M6 ? i_bearColor_p : the_m == M7 ? i_bearColor_o : na

gr = the_m == M1 ? gr1    : the_m == M2 ? gr2    : the_m == M3 ? gr3    : the_m == M4 ? gr4    : the_m == M5 ? gr5    : the_m == M6 ? gr6    : the_m == M7 ? gr7    : na
rd = the_m == M1 ? rd1    : the_m == M2 ? rd2    : the_m == M3 ? rd3    : the_m == M4 ? rd4    : the_m == M5 ? rd5    : the_m == M6 ? rd6    : the_m == M7 ? rd7    : na

bk_grd          = input.color(#b5a1e2, 'Threshold color, fade', inline = 'q', group = GRP_UI)
fader           = input.int(72, '', inline = 'q', group = GRP_UI)

isThreshCol     = input.bool(true, 'ADX fill', inline='i', group=GRP_UI)

// ===========================================================================================================
//          Functions
// ===========================================================================================================

// 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

// Super Smoother Function
ss(Series, Period) =>  // Super Smoother Function
    var PI = 2.0 * math.asin(1.0)
    var SQRT2 = math.sqrt(2.0)
    lambda = PI * SQRT2 / Period
    a1 = math.exp(-lambda)
    coeff2 = 2.0 * a1 * math.cos(lambda)
    coeff3 = -math.pow(a1, 2.0)
    coeff1 = 1.0 - coeff2 - coeff3
    filt1 = 0.0
    filt1 := coeff1 * (Series + nz(Series[1])) * 0.5 + coeff2 * nz(filt1[1]) + coeff3 * nz(filt1[2])
    filt1

wilders(src, period)=>
    wild =0.
    wild := nz(wild[1]) + (src -nz(wild[1]))/period
    wild

doSmoothing(float _input, int length) =>
    switch smoothingMode
        'Hann Window' => doHannWindow(_input, length)
        'Super Smoother' => ss(_input, length)
        'Wilders' => wilders(_input, length)
        => _input

atr_ma(float _input, int length) =>
    switch atr_move
        'Hann Window' => doHannWindow(_input, length)
        'Super Smoother' => ss(_input, length)
        => _input

air_ma(float _input, int length) =>
    switch air_move
        'Hann Window' => doHannWindow(_input, length)
        'Super Smoother' => ss(_input, length)
        'Wilders' => wilders(_input, length)
        => _input


// ===========================================================================================================
//          Calculations
// ===========================================================================================================

[_close, _low, _high] = request.security(symbol = syminfo.tickerid, timeframe = '', expression = [close[1], low[1], high[1]], gaps =  barmerge.gaps_off, lookahead = barmerge.lookahead_on)

atr(atrLength) => 
    float tr = math.max(math.max(_high - _low, math.abs(_high - _close[1])), math.abs(_low - _close[1]))
    atr_ma(tr, atrLength)

ipr_array(len, dnny, uppy) =>
    float[] hiArray = array.new_float(0)
    float[] loArray = array.new_float(0)
    float[] cmArray = array.new_float(0)

    for i = 0 to len - 1
        array.push(hiArray, high[i]) // tested with '_close', '_low', etc. bof
        array.push(loArray, low[i])
        array.push(cmArray, hlcc4[i])

    hlArray   = array.concat(hiArray, loArray)
    hlcmArray = array.concat(hlArray, cmArray)
    
    q1        = array.percentile_linear_interpolation(hlcmArray, dnny)
    q3        = array.percentile_linear_interpolation(hlcmArray, uppy)

    iqr = (q3 - q1) / 2

atrFactor   = atrActive ? atrMult * atr(smoothing)                                                          : 0
pipFactor   = pipActive ? pipMult * ipr_array(smoothing, spacer_chop, 100 - spacer_chop)                    : 0
airFactor   = airActive ? airMult * air_ma(ipr_array(smoothing, percent_rng, 100 - percent_rng), smoothing) : 0

blender = nz(atrFactor) + nz(pipFactor) + nz(airFactor)

vortex(int period) =>
    tr = blender
    VMP = math.sum(math.abs(high - low[1]), period) 
    VMM = math.sum(math.abs(low - high[1]), period) 
    Sum_TR = math.sum(tr, period)
    plus = VMP / Sum_TR
    minus = VMM / Sum_TR

    DI_Dif = math.abs(plus - minus) * 10
    DI_Sum = (plus + minus) * 10
    Vortex = 100 * doSmoothing(DI_Dif / DI_Sum, period)
    [Vortex, plus, minus]

[vortex, vortexPlus, vortexMinus] = vortex(length)

normalize(float _src, int _min, int _max) =>
    var float _historicMin = 1.0
    var float _historicMax = -1.0
    _historicMin := math.min(nz(_src, _historicMin), _historicMin)
    _historicMax := math.max(nz(_src, _historicMax), _historicMax)
    _min + (_max - _min) * (_src - _historicMin) / math.max(_historicMax - _historicMin, 1)

max = 1
min = 0
vortex := normalize(vortex, min, max)
vortexPlus := normalize(vortexPlus, min, max)
vortexMinus := normalize(vortexMinus, min, max)

// RSI filter
rsi_threshold_up    = 50 + rsi_threshold
rsi_threshold_dn    = 50 - rsi_threshold

rsi_cond = ta.rsi(close, rsi_len) > rsi_threshold_up or ta.rsi(close, rsi_len) < rsi_threshold_dn

// ===========================================================================================================
//          Colors & plots
// ===========================================================================================================

vortexPlot      = plot(vortex, color=color.new(bk_grd, fader), linewidth = 1, style=plot.style_cross, title='Vortex')
flat            = plot(0, display = display.none)

above_thr_up    = vortex > threshold and vortexPlus > vortexMinus 
above_thr_dn    = vortex > threshold and vortexPlus < vortexMinus 
thresher_col    = above_thr_up ? gr : above_thr_dn ? rd : na

agreg           = above_thr_up ? vortexPlus - vortexMinus : above_thr_dn ? vortexMinus - vortexPlus : na
absolute        = plot(agreg, color=thresher_col, style = plot.style_linebr, title='absolute DX')

fill(flat, absolute, color=isThreshCol and vortex > threshold ? color.new(thresher_col, 65) : color.new(bk_grd, 80))

// Bar coloring conditions
buy   =  vortexPlus > vortexMinus and vortexPlus  > vortex and above_thr_up and rsi_cond 
sell  =  vortexMinus > vortexPlus and vortexMinus > vortex and above_thr_dn and rsi_cond 

self_cancel = vortexPlus < threshold and vortexMinus < threshold 
ob = not self_cancel and not buy and not sell and vortexPlus > vortexMinus and rsi_cond
os = not self_cancel and not buy and not sell and vortexMinus > vortexPlus and rsi_cond

baring          = self_cancel ? color.new(bk_grd, 75)  : buy ? gr : sell ? rd : ob ? up_                : os ? dn_                : color.new(bk_grd, 75)

barcolor(switch_col ? baring : na)

baring_histo    = self_cancel ? color.new(bk_grd, 100) : buy ? gr : sell ? rd : ob ? color.new(up_, 35) : os ? color.new(dn_, 35) : color.new(bk_grd, 100)
vortexPlot_hist = plot(vortex, color=baring_histo, linewidth = 1, style=plot.style_histogram, title='Vortex')

// ===========================================================================================================
//          Signals & alerts
// ===========================================================================================================

var trend_noch = 0
if buy
    trend_noch := 1
    trend_noch
if sell
    trend_noch := -1
    trend_noch

up_trig = trend_noch[1] == -1 and trend_noch ==  1
dn_trig = trend_noch[1] ==  1 and trend_noch == -1

alertcondition(up_trig, "AIR Vortex ADX Up 🟢",   "AIR Vortex ADX Up 🟢")
alertcondition(dn_trig, "AIR Vortex ADX Down 🔴", "AIR Vortex ADX Down 🔴")

plotshape(up_trig, title='Trend up',   style=shape.triangleup,   location=location.top, color=gr, size = size.tiny)
plotshape(dn_trig, title='Trend down', style=shape.triangledown, location=location.top, color=rd, size = size.tiny)

// (  __)(  ( \(    \
//  ) _) /    / ) D (
// (____)\_)__)(____/

//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
