Goertzel Cycle Period Adaptive Fisher Transform [Loxx]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © loxx

//@version=5
indicator("Goertzel Cycle Period Adaptive Fisher Transform [Loxx]", 
     overlay = false, 
     shorttitle="GCPFT [Loxx]",
     timeframe="", 
     timeframe_gaps=true, 
     max_bars_back = 3000)

greencolor = #2DD204
redcolor = #D2042D 


SM02 = 'Zero-Line Cross'
SM03 = 'Signal Crossover'

_goertzel_cycle_period(float src, int per)=>

    float[] goeWork1 = array.new_float(per * 3, 0.) //Power array
    float[] goeWork2 = array.new_float(per * 3, 0.) //Theta array
    float[] goeWork3 = array.new_float(per * 3, 0.) //Store cylces
    float[] goeWork4 = array.new_float(per * 3, 0.) //Store ingest values
    
    float temp1 = nz(src[3 * per - 1]) //get first bar for calculation
    float temp2 = (src - temp1) / (3 * per - 1) // Real 
    float temp3 = 0. //Imaginary 
    
    for k = 3 * per - 1 to 1 
        float temp = src[k - 1] - (temp1 + temp2 * (3 * per - k))
        array.set(goeWork4, k, temp)
        array.set(goeWork3, k, 0.)
        
    for k = 2 to per 
        float w = 0., float x = 0., float y = 0.
        float z = math.pow(k, -1)
        
        temp1 := 2.0 * math.cos(2.0 * math.pi * z)
        
        //The first stage calculates an intermediate sequence, s[n]
        for i = 3 * per-1 to 1  
            w := temp1 * x - y + nz(array.get(goeWork4, i))
            y := x
            x := w

        // The second stage applies the following filter to y[n]
        // 1. The first filter stage can be observed to be a second-order IIR filter with a direct-form structure TBD
        // 2. The second-stage filter can be observed to be a FIR filter, since its calculations do not use any of its past outputs. TBD
        
        // calculate Real component 
        temp2 := x - y / 2.0 * temp1 
        if (temp2 == 0.0)
            temp2 := 0.0000001
            
        // calculate Imaginary component 
        temp3 := y * math.sin(2.0 * math.pi * z) 
        
        // calculate Power
        array.set(goeWork1, k, math.pow(temp2, 2) + math.pow(temp3, 2)) 
        
        // calculate Theta
        array.set(goeWork2, k, math.atan(temp3 / temp2))  
        if (temp2 < 0.0)
            array.set(goeWork2, k, nz(array.get(goeWork2, k)) + math.pi)
        else
            if (temp3 < 0.0) 
                array.set(goeWork2, k, nz(array.get(goeWork2, k)) + 2.0 * math.pi)
                
    // Parse cycles
    for k = 3 to per - 1
        if (nz(array.get(goeWork1, k)) > nz(array.get(goeWork1, k + 1)) and nz(array.get(goeWork1, k)) > nz(array.get(goeWork1, k-1)))
            array.set(goeWork3, k, k * math.pow(10, -4))
        else 
            array.set(goeWork3, k, 0.0)
    
    // count cycles
    int number_of_cycles = 0
    for i = 0 to per + 1
        if (nz(array.get(goeWork3, i)) > 0.0) 
            number_of_cycles := number_of_cycles + 1
    number_of_cycles


signalMode = input.string(SM02, 'Signal Type', options=[SM02, SM03], group = "Basic Settings")

fisherOb1 = input.float(2, minval=1, step = 0.1, title='Overbought Level', group = "Basic Settings")
fisherOs1 = input.float(-2, maxval=-1, step = 0.1,  title='Oversold Level', group = "Basic Settings")

src = input.source(close, "Source", group = "Goertzel Cycle Period Settings")
per = input.int(80, "Max Period", group = "Goertzel Cycle Period Settings")

colorbars = input.bool(false, "Color bars?", group = "UI Options")
showsignals = input.bool(false, "Show signals?", group = "UI Options")

len = _goertzel_cycle_period(src, per)
len := len < 1 ? 1 : len

high_ = ta.highest(hl2, len)
low_ = ta.lowest(hl2, len)


round_(val) => val > .99 ? .999 : val < -.99 ? -.999 : val
value = 0.0
value := round_(.66 * ((hl2 - low_) / (high_ - low_) - .5) + .67 * nz(value[1]))
fish1 = 0.0
fish1 := .5 * math.log((1 + value) / (1 - value)) + .5 * nz(fish1[1])
fish2 = fish1[1]

hth = fisherOb1
lth = fisherOs1

middle = 0.

plot(hth+1.5, color = color.new(color.white, 100), title = "Upper UI Constraint")
plot(lth-1.5, color = color.new(color.white, 100), title = "Lower UI Constraint")

tl = plot(hth+1, color=color.rgb(0, 255, 255, 100), style=plot.style_line, title = "OB Level 2")
tl1 = plot(hth, color=color.rgb(0, 255, 255, 100), style=plot.style_line, title = "OB Level 1")

plot(middle, color=color.new(color.gray, 30), linewidth=1, style=plot.style_circles, title = "Zero")

bl1 = plot(lth, color=color.rgb(255, 255, 255, 100), style=plot.style_line, title = "OS Level 1")
bl = plot(lth-1, color=color.rgb(255, 255, 255, 100), style=plot.style_line, title = "OS Level 2")

fill(tl, tl1, color=color.new(color.gray, 85), title = "Top Boundary Fill Color")
fill(bl, bl1, color=color.new(color.gray, 85), title = "Bottom Boundary Fill Color")


color_out = 
     signalMode == SM03 ? (fish1 > fish2 ? greencolor : redcolor) : 
     (fish1 >= 0 and fish1 > fish2 ? greencolor : 
     fish1 < 0 and fish1 < fish2 ? redcolor : color.gray)

plot(fish1, color = color_out, title='Fisher', linewidth = 3)
plot(fish2, color = color.white, title = "Signale")

barcolor(colorbars ? color_out : na)

goLong = signalMode == SM03  ? ta.crossover(fish1, fish2) : ta.crossover(fish1, 0)
goShort = signalMode == SM03  ? ta.crossunder(fish1, fish2) : ta.crossunder(fish1, 0)

plotshape(goLong and showsignals, title = "Long", color = greencolor, textcolor = greencolor, text = "L", style = shape.triangleup, location = location.bottom, size = size.auto)
plotshape(goShort and showsignals, title = "Short", color = redcolor, textcolor = redcolor, text = "S", style = shape.triangledown, location = location.top, size = size.auto)


alertcondition(goLong, title="Long", message="Goertzel Cycle Period Adaptive Fisher Transform [Loxx]: Long\nSymbol: {{ticker}}\nPrice: {{close}}")
alertcondition(goShort, title="Short", message="Goertzel Cycle Period Adaptive Fisher Transform [Loxx]: Short\nSymbol: {{ticker}}\nPrice: {{close}}")












