Files

86 lines
2.8 KiB
Plaintext

//@version=6
// EPA: Ehlers Phasor Analysis
// From John F. Ehlers, "Recurring Phase Of Cycle Analysis"
// (Stocks & Commodities, November 2022)
indicator("EPA - Ehlers Phasor Analysis", shorttitle="EPA", overlay=false)
period = input.int(28, "Period", minval=2)
src = input.source(close, "Source")
var float prevAngle = 0.0
var float prevDeltaAngle = 0.0
var float derivedPeriod = 0.0
// Correlate price with Cosine wave (Pearson correlation → Real)
float sx_r = 0.0, float sy_r = 0.0
float sxx_r = 0.0, float sxy_r = 0.0, float syy_r = 0.0
for k = 0 to period - 1
float x = nz(src[k])
float y = math.cos(2.0 * math.pi * k / period)
sx_r += x
sy_r += y
sxx_r += x * x
sxy_r += x * y
syy_r += y * y
float dp_r = (period * sxx_r - sx_r * sx_r) * (period * syy_r - sy_r * sy_r)
float real = dp_r > 0 ? math.max(-1.0, math.min(1.0, (period * sxy_r - sx_r * sy_r) / math.sqrt(dp_r))) : 0.0
// Correlate price with -Sine wave (Pearson correlation → Imag)
float sx_i = 0.0, float sy_i = 0.0
float sxx_i = 0.0, float sxy_i = 0.0, float syy_i = 0.0
for k = 0 to period - 1
float x = nz(src[k])
float y = -math.sin(2.0 * math.pi * k / period)
sx_i += x
sy_i += y
sxx_i += x * x
sxy_i += x * y
syy_i += y * y
float dp_i = (period * sxx_i - sx_i * sx_i) * (period * syy_i - sy_i * sy_i)
float imag = dp_i > 0 ? math.max(-1.0, math.min(1.0, (period * sxy_i - sx_i * sy_i) / math.sqrt(dp_i))) : 0.0
// Angle = 90 - atan(Imag/Real) with quadrant fix
float angle = 0.0
if real != 0
angle := 90.0 - math.todegrees(math.atan(imag / real))
if real < 0
angle -= 180.0
// Wraparound compensation
if math.abs(angle) - math.abs(prevAngle - 360.0) < angle - prevAngle and prevAngle > 90.0 and angle < -90.0
angle -= 360.0
// Angle cannot go backwards (with conditional exceptions)
if angle < prevAngle and ((prevAngle > -135.0 and prevAngle < 135.0) or (angle < -90.0 and prevAngle < -90.0))
angle := prevAngle
// DerivedPeriod from angle rate-of-change
float deltaAngle = angle - prevAngle
if deltaAngle <= 0
deltaAngle := prevDeltaAngle
if deltaAngle > 0
derivedPeriod := 360.0 / deltaAngle
prevDeltaAngle := deltaAngle
if derivedPeriod > 60
derivedPeriod := 60.0
// Trend state
int trendState = 0
float angleChange = angle - prevAngle
if angleChange <= 6.0
if angle >= 90.0 or angle <= -90.0
trendState := 1 // trending long
else if angle > -90.0 and angle < 90.0
trendState := -1 // trending short
prevAngle := angle
plot(angle, "Angle", color.yellow, 2)
hline(0, "Zero", color.white)
hline(90, "+90", color.new(color.cyan, 50))
hline(-90, "-90", color.new(color.cyan, 50))
plot(derivedPeriod, "DerivedPeriod", color.cyan, 1, display=display.none)
plot(trendState, "TrendState", color.red, 2, display=display.none)