Discrete Fourier Transformed Money Flow Index
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © profitprotrading
//@version=5
indicator(title="Fourier Transformed Money Flow Index", shorttitle="FMFI", format=format.price, precision=2, timeframe="", timeframe_gaps=true)


//Fourier Settings (Credit to author: wbburgin - May 26 2023 https://www.tradingview.com/v/gThgCf69/)
//Inputs
N = input.int(3,"Fourier Period", group = "Fourier Transform")
xval = input.source(hlc3,"Fourier Source", group = "Fourier Transform")


//Discrete Fourier Transform
DFT(x, y, Nx, _dir) =>
    float _arg = 0.0
    float _cos = 0.0
    float _sin = 0.0
    float xArr_i = 0.0
    float yArr_i = 0.0
    xArr = array.new_float(array.size(x))
    yArr = array.new_float(array.size(y))

    for i = 0 to Nx - 1 by 1
        xArr_i := 0.0
        yArr_i := 0.0
        kx = float(i) / float(Nx)
        _arg := -_dir * 2 * math.pi * kx
        for k = 0 to Nx - 1 by 1
            _cos := math.cos(k * _arg)
            _sin := math.sin(k * _arg)
            xArr_i += array.get(x, k) * _cos - array.get(y, k) * _sin
            yArr_i += array.get(x, k) * _sin + array.get(y, k) * _cos
            yArr_i
        array.set(xArr, i, xArr_i)
        array.set(yArr, i, yArr_i)

    if _dir == 1
        for i = 0 to Nx - 1 by 1
            array.set(x, i, array.get(xArr, i) / float(Nx))
            array.set(y, i, array.get(yArr, i) / float(Nx))
    else
        for i = 0 to Nx - 1 by 1
            array.set(x, i, array.get(xArr, i))
            array.set(y, i, array.get(yArr, i))


//

x = array.new_float(N, 0.0)
y = array.new_float(N, 0.0)
for i = 0 to N - 1
    array.set(x, i, xval[i])
    array.set(y, i, 0.0)

DFT(x, y, N, 1)

mag = array.new_float(N, 0.0)
for i = 0 to N - 1
    mag_i = math.sqrt(math.pow(array.get(x, i), 2) + math.pow(array.get(y, i), 2))
    array.set(mag, i, mag_i)

ft = array.get(mag,0)

//Money Flow Index
length = input.int(title="Length", defval=14, minval=1, maxval=2000, group = "MFI")
smooth = input.int(7, "Smoothing", group = "MFI")
src = input(hlc3, "Source", group = "MFI")
ori = input.bool(true, "Plot original MFI?")
mf = ta.mfi(ft, length)
mf := ta.ema(mf, smooth)
short = (ta.crossunder(mf,mf[1]))

//Plotting
plot(mf, "MF", color=mf > mf[1] ? #ffffff: color.rgb(128, 35, 95), linewidth = 4)
plotchar(ta.crossover(mf,mf[1]) ? mf : na, char = "⊙", location = location.absolute, size = size.tiny, color = #ffffff, offset = -1)
plotchar(ta.crossunder(mf,mf[1]) ? mf : na, char = "⦿", location = location.absolute, size = size.tiny, color = color.rgb(246, 93, 192), offset = -1)
plotshape((ta.crossunder(mf,mf[1]) and mf > 80), style = shape.triangledown, color = color.rgb(201, 8, 8), location = location.top, size = size.tiny, offset = -1)
plotshape((ta.crossover(mf,mf[1]) and mf < 20), style = shape.triangleup, color = color.rgb(0, 171, 11), location = location.bottom, size = size.tiny, offset = -1)
mfx = plot(ori ? ta.mfi(src,length): na, color = color.rgb(0, 208, 255, 65))

ob = plot(80, display = display.none)
ob1 = plot(100, display = display.none)
ob2 = plot(65, display = display.none)
os = plot(20, display = display.none)
os1 = plot(0, display = display.none)
os2 = plot(35, display = display.none)
fill(ob, ob1, color = color.rgb(234, 0, 0, 75))
fill(os, os1, color = color.rgb(0, 193, 6, 75))
fill(ob2, ob, color =color.rgb(234, 0, 0, 85))
fill(os, os2, color =color.rgb(0, 193, 6, 85))


//Alert Conditions
alertcondition(ta.crossover(mf,mf[1]), title="Buy", message="{{exchange}}:{{ticker}}")
alertcondition(ta.crossunder(mf,mf[1]), title="Sell", message="{{exchange}}:{{ticker}}")
alertcondition((ta.crossover(mf,mf[1]) and mf < 20), title="Firm Buy", message="{{exchange}}:{{ticker}}")
alertcondition((ta.crossunder(mf,mf[1]) and mf > 80), title="Firm Sell", message="{{exchange}}:{{ticker}}")