Adaptive Average Vortex Index [lastguru]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © lastguru

//@version=5
indicator("Adaptive Average Vortex Index by lastguru", "AAVX [lastguru]", precision=2)
import lastguru/CommonFilters/1 as cf
import lastguru/NormalizedOscillators/2 as co
import lastguru/LengthAdaptation/1 as la
import lastguru/DominantCycle/1 as dc

// Internal smoothing algorithm selection
_smooth(type, src, len, default) =>
    cf.doMA(type == "Default" or type == "Fast Default" ? default : type, src, type == "Fast Default" ? math.round(len/2) :len)

_avx(len, smooth) =>
    default = "RMA"
    plusVM = math.sum(math.abs( high - low[1]), len)
    minusVM = math.sum(math.abs( low - high[1]), len)
    str = math.sum(ta.tr, len)
    plus = plusVM / str
    minus = minusVM / str
    sum = plus + minus
    adx = _smooth(smooth, math.abs(plus - minus) / (sum == 0 ? 1 : sum), len, default)
    [4 * adx - 1, plus - 1, minus - 1]

////////////
// Inputs //
////////////

src = input(close, title="Source")
Dynamic = input.string(title="Adaptation", options=["None", "VIDYA", "VIDYA-RS", "Kaufman Efficiency Scaling", "Fractal Adaptation", "MESA MAMA Cycle", "Pearson Autocorrelation", "DFT Cycle", "Phase Accumulation"], defval="None", inline="30")
DynamicHP = input.bool(true, "HP", inline="30")
DynamicSS = input.bool(true, "SS", inline="30")
DynamicHW = input.bool(false, "HW", inline="30")

DynamicLow = input.int(title="Bound From", defval=8, inline="31")
DynamicHigh = input.int(title="To", defval=50, inline="31")


OscWindow = input.string(title="Window", options=["Default", "Fast Default", "SMA", "Triangle Window", "Hamming Window", "Hann Window"], defval="Fast Default", group="Average Vortex Index")
OscLength = input.int(title="Length", defval=14, inline="11", group="Average Vortex Index")
OscCycle = input.float(title="Cycle", defval=0.5, step=0.1, inline="11", group="Average Vortex Index")

OutMAType = input.string(title='Filter/MA', options=["None", "SMA", "RMA", "EMA", "HMA", "VWMA", "2-pole Super Smoother", "3-pole Super Smoother", "Filt11", "Triangle Window", "Hamming Window", "Hann Window", "Lowpass", "DSSS"], defval="None", inline="12", group="Average Vortex Index")
OutMALength = input.float(title="Length", defval=9, inline="12", group="Average Vortex Index")
PostType = input.string(title='Postfilter', options=["None", "Stochastic", "Super Smooth Stochastic", "Inverse Fisher Transform", "Noise Elimination Technology", "Momentum"], defval="None", inline="13", group="Average Vortex Index")
PostLength = input.int(title="Length", defval=0, inline="13", group="Average Vortex Index")

////////////////////
// Dynamic Length //
////////////////////

dynLength = 0
dynLengthRaw = 0.0
dynLengthLim = 0.0

switch Dynamic
    "None" =>
        dynLength := OscLength
    "Pearson Autocorrelation" =>
        nl = dc.paPeriod(src, DynamicLow, DynamicHigh, DynamicHP, DynamicSS, DynamicHW)
        dynLengthRaw := nl
        dynLengthLim := math.max(math.min(nl, DynamicHigh), DynamicLow)
        dynLength := math.round(dynLengthLim * OscCycle)
    "DFT Cycle" =>
        nl = dc.dftPeriod(src, DynamicLow, DynamicHigh, DynamicHP, DynamicSS, DynamicHW)
        dynLengthRaw := nl
        dynLengthLim := math.max(math.min(nl, DynamicHigh), DynamicLow)
        dynLength := math.round(dynLengthLim * OscCycle)
    "Phase Accumulation" =>
        nl = dc.phasePeriod(src, DynamicLow, DynamicHigh, DynamicHP, DynamicSS, DynamicHW)
        dynLengthRaw := nl
        dynLengthLim := math.max(math.min(nl, DynamicHigh), DynamicLow)
        dynLength := math.round(dynLengthLim * OscCycle)
    =>
        nl = dc.doAdapt(Dynamic, src, OscLength, DynamicLow, DynamicHigh)
        dynLengthRaw := nl
        dynLengthLim := math.max(math.min(nl, DynamicHigh), DynamicLow)
        dynLength := math.round(dynLengthLim * OscCycle)

plotchar(dynLengthRaw, "Raw calculated length", "", location.top)
plotchar(dynLength, "Calculated length", "", location.top)

/////////////////
// Oscillators //
/////////////////

[osc, plus, minus] = _avx(math.max(dynLength, 1), OscWindow)

malen = OutMALength < 2 ? math.round(math.max(dynLength, 1) * OutMALength) : math.round(OutMALength)
float newosc = la.doMA(OutMAType, osc, malen)
float signal = co.doPostfilter(PostType, newosc, PostLength == 0 ? math.max(dynLength, 1) : PostLength)

//////////////
// Plotting //
//////////////

plot(osc, "Without smoothing", color.gray)
plot(signal, "Oscillator", color.white)
plot(plus, title="VI +", color=#2442BF)
plot(minus, title="VI -", color=#991643)
hline(0.0, "Zero", color.gray)
uBand = hline(0.6, "80%", color.gray)
lBand = hline(-0.6, "20%", color.gray)
fill(uBand, lBand, color=color.rgb(126, 87, 194, 90), title="Background Fill")
hline(1.0, "Upper", color.gray)
hline(-1.0, "Lower", color.gray)