Files

52 lines
1.7 KiB
Plaintext

// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0
// https://mozilla.org/MPL/2.0/
// © QuanTAlib
//@version=6
indicator("FSI - Ehlers Fourier Series Indicator", shorttitle="FSI", overlay=false)
// Inputs
x = input.int(20, "Fundamental Cycle Period", minval=6)
bw = input.float(0.1, "Bandwidth", minval=0.001, step=0.01)
// Fundamental bandpass coefficients
L1 = math.cos(2 * math.pi / x)
G1 = math.cos(bw * 2 * math.pi / x)
S1 = 1 / G1 - math.sqrt(1 / (G1 * G1) - 1)
// 2nd harmonic coefficients (period = x/2)
L2 = math.cos(2 * math.pi / (x / 2))
G2 = math.cos(bw * 2 * math.pi / (x / 2))
S2 = 1 / G2 - math.sqrt(1 / (G2 * G2) - 1)
// 3rd harmonic coefficients (period = x/3)
L3 = math.cos(2 * math.pi / (x / 3))
G3 = math.cos(bw * 2 * math.pi / (x / 3))
S3 = 1 / G3 - math.sqrt(1 / (G3 * G3) - 1)
// Bandpass filters
float BP1 = 0.0
BP1 := 0.5 * (1 - S1) * (close - close[2]) + L1 * (1 + S1) * nz(BP1[1]) - S1 * nz(BP1[2])
float BP2 = 0.0
BP2 := 0.5 * (1 - S2) * (close - close[2]) + L2 * (1 + S2) * nz(BP2[1]) - S2 * nz(BP2[2])
float BP3 = 0.0
BP3 := 0.5 * (1 - S3) * (close - close[2]) + L3 * (1 + S3) * nz(BP3[1]) - S3 * nz(BP3[2])
// Quadrature components (differentiation-based 90° phase shift)
Q1 = (x / (2 * math.pi)) * (BP1 - nz(BP1[1]))
Q2 = (x / (2 * math.pi)) * (BP2 - nz(BP2[1]))
Q3 = (x / (2 * math.pi)) * (BP3 - nz(BP3[1]))
// Power estimation (rolling sum over x bars)
P1 = math.sum(BP1 * BP1 + Q1 * Q1, x)
P2 = math.sum(BP2 * BP2 + Q2 * Q2, x)
P3 = math.sum(BP3 * BP3 + Q3 * Q3, x)
// Amplitude-weighted reconstruction
fsi = P1 > 0 ? BP1 + math.sqrt(P2 / P1) * BP2 + math.sqrt(P3 / P1) * BP3 : BP1
plot(fsi, "FSI", color.yellow, 2)
hline(0, "Zero", color.gray, hline.style_dotted)