Normalized Adaptive Trend Lines [MAMA and FAMA]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © IkkeOmar

//@version=5
indicator("Adaptive Trend Lines [MAMA and FAMA]", overlay = false, timeframe = "")
fastLimit = input.float(title='Fast Limit', step=0.01, defval=0.01)
slowLimit = input.float(title='Slow Limit', step=0.01, defval=0.08)


norm_period = input.int(3, 'Normalization Period', 1)  // Period for normalization


src = input(title='Source', defval=close)


// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------}



// CALCULATIONS ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
var float PI = math.pi

// CREDIT TO everget and Chartprime for the following functions


// Truncated Hilbert transform{
laplace(src) =>
    (0.5) * math.exp(-math.abs(src))

_computeComponent(src, mesaPeriodMult) =>
    out = laplace(src) * mesaPeriodMult
    out

_smoothComponent(src) =>
    out = 0.2 * src + 0.8 * nz(src[1])
    out

_computeAlpha(src, fastLimit, slowLimit) =>
    mesaPeriod = 0.0
    mesaPeriodMult = 0.075 * nz(mesaPeriod[1]) + 0.54

    smooth = (4 * src + 3 * nz(src[1]) + 2 * nz(src[2]) + nz(src[3])) / 10
    detrender = _computeComponent(smooth, mesaPeriodMult)

    // Compute InPhase and Quadrature components
    I1 = nz(detrender[3])
    Q1 = _computeComponent(detrender, mesaPeriodMult)

    // Advance the phase of I1 and Q1 by 90 degrees
    jI = _computeComponent(I1, mesaPeriodMult)
    jQ = _computeComponent(Q1, mesaPeriodMult)

    // Phasor addition for 3 bar averaging
    I2 = I1 - jQ
    Q2 = Q1 + jI

    // Smooth the I and Q components before applying the discriminator
    I2 := _smoothComponent(I2)
    Q2 := _smoothComponent(Q2)

    // Homodyne Discriminator
    Re = I2 * nz(I2[1], I2) + Q2 * nz(Q2[1], Q2)
    Im = I2 * nz(Q2[1], Q2) - Q2 * nz(I2[1], I2)

    Re := _smoothComponent(Re)
    Im := _smoothComponent(Im)

    if Re != 0 and Im != 0
        mesaPeriod := 2 * PI / math.atan(Im / Re)
        mesaPeriod

    mesaPeriod := math.min(mesaPeriod, 1.5 * nz(mesaPeriod[1], mesaPeriod))
    mesaPeriod := math.max(mesaPeriod, 0.67 * nz(mesaPeriod[1], mesaPeriod))
    mesaPeriod := math.min(math.max(mesaPeriod, 6), 50)
    mesaPeriod := _smoothComponent(mesaPeriod)

    phase = 0.0

    if I1 != 0
        phase := 180 / PI * math.atan(Q1 / I1)
        phase

    deltaPhase = nz(phase[1], phase) - phase
    deltaPhase := math.max(deltaPhase, 1)

    alpha = math.max(fastLimit / deltaPhase, slowLimit)

    out = alpha
    out

alpha = _computeAlpha(src, fastLimit, slowLimit)
alpha2 = alpha / 2

mama = 0.0
mama := alpha * src + (1 - alpha) * nz(mama[1])

fama = 0.0
fama := alpha2 * mama + (1 - alpha2) * nz(fama[1])

mama_fama_diff = mama - fama


// Normalize the oscillator
lowest = ta.lowest(mama_fama_diff, norm_period)
highest = ta.highest(mama_fama_diff, norm_period)
normalized = (mama_fama_diff - lowest) / (highest - lowest) - 0.5



// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------}
// PLOTTING (I'm scared of aussies because they're usually tall) { ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// Plot the difference as a histogram with green and red bars
barcolor(normalized >= 0 ? color.green : color.red)
plot(normalized, color = normalized > 0 ? color.green : color.red, style = plot.style_histogram, title = 'MAFA Oscillator', linewidth = 3)

// Add a horizontal line at zero to indicate the neutral point
hline(0, "Zero Line", color=color.gray)