mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-28 01:37:43 +00:00
94 lines
2.9 KiB
Plaintext
94 lines
2.9 KiB
Plaintext
// Licensed under the Apache License, Version 2.0
|
|
// © mihakralj
|
|
// Indicator algorithm (C) 2017 John F. Ehlers
|
|
//@version=6
|
|
indicator("Ehlers Reverse EMA (REVERSEEMA)", "REVERSEEMA", overlay=false)
|
|
|
|
//@function Calculates Ehlers Reverse EMA using Z-transform inversion of EMA smoothing
|
|
//@param source Series to calculate Reverse EMA from
|
|
//@param period Lookback period for the base EMA (>= 1)
|
|
//@returns Reverse EMA value with lag removed via 8-stage cascaded inversion
|
|
//@optimized Uses warmup-compensated EMA with O(1) cascaded reverse stages per bar
|
|
reverseema(series float source, simple int period) =>
|
|
if period <= 0
|
|
runtime.error("Period must be positive")
|
|
|
|
float src = na(source) ? 0.0 : source
|
|
|
|
// EMA smoothing factor from period: alpha = 2/(period+1)
|
|
float a = 2.0 / (period + 1)
|
|
float cc = 1.0 - a
|
|
float beta = cc
|
|
|
|
// Precompute powers of cc for the 8 reverse stages
|
|
// Stage k uses cc^(2^(k-1)): 1, 2, 4, 8, 16, 32, 64, 128
|
|
float cc2 = cc * cc
|
|
float cc4 = cc2 * cc2
|
|
float cc8 = cc4 * cc4
|
|
float cc16 = cc8 * cc8
|
|
float cc32 = cc16 * cc16
|
|
float cc64 = cc32 * cc32
|
|
float cc128 = cc64 * cc64
|
|
|
|
// --- Forward EMA with warmup compensator ---
|
|
var bool warmup = true
|
|
var float e = 1.0
|
|
var float ema_raw = 0.0
|
|
var float ema_val = 0.0
|
|
|
|
ema_raw := a * (src - ema_raw) + ema_raw
|
|
if warmup
|
|
e *= beta
|
|
float comp = 1.0 / (1.0 - e)
|
|
ema_val := comp * ema_raw
|
|
warmup := e > 1e-10
|
|
else
|
|
ema_val := ema_raw
|
|
|
|
// --- 8-stage cascaded reverse EMA ---
|
|
// Each stage: RE_k[n] = cc^(2^(k-1)) * RE_{k-1}[n] + RE_{k-1}[n-1]
|
|
// RE1 uses EMA as input: RE1[n] = cc * EMA[n] + EMA[n-1]
|
|
var float re1 = 0.0
|
|
var float re2 = 0.0
|
|
var float re3 = 0.0
|
|
var float re4 = 0.0
|
|
var float re5 = 0.0
|
|
var float re6 = 0.0
|
|
var float re7 = 0.0
|
|
var float re8 = 0.0
|
|
|
|
float prev_ema = nz(ema_val[1])
|
|
float prev_re1 = nz(re1[1])
|
|
float prev_re2 = nz(re2[1])
|
|
float prev_re3 = nz(re3[1])
|
|
float prev_re4 = nz(re4[1])
|
|
float prev_re5 = nz(re5[1])
|
|
float prev_re6 = nz(re6[1])
|
|
float prev_re7 = nz(re7[1])
|
|
|
|
re1 := cc * ema_val + prev_ema
|
|
re2 := cc2 * re1 + prev_re1
|
|
re3 := cc4 * re2 + prev_re2
|
|
re4 := cc8 * re3 + prev_re3
|
|
re5 := cc16 * re4 + prev_re4
|
|
re6 := cc32 * re5 + prev_re5
|
|
re7 := cc64 * re6 + prev_re6
|
|
re8 := cc128 * re7 + prev_re7
|
|
|
|
// Signal = EMA - alpha * RE8
|
|
float signal = ema_val - a * re8
|
|
na(source) ? na : signal
|
|
|
|
// ---------- Main loop ----------
|
|
|
|
// Inputs
|
|
i_period = input.int(20, "Period", minval=1, tooltip="Lookback period for the base EMA")
|
|
i_source = input.source(close, "Source")
|
|
|
|
// Calculation
|
|
reverseema_value = reverseema(i_source, i_period)
|
|
|
|
// Plot
|
|
plot(reverseema_value, "REVERSEEMA", color=color.yellow, linewidth=2)
|
|
hline(0, "Zero", color=color.gray)
|