mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-29 18:17:43 +00:00
86 lines
2.8 KiB
Plaintext
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)
|