Files
Miha Kralj 35a6702b06 fix(docs): correct .md documentation across errors, dynamics, filters, forecasts, momentum, numerics, oscillators, reversals, statistics, trends, volatility, volume
Deep review of all indicator categories verified .md headers against .cs WarmupPeriod, parameters, inputs, and outputs. Fixes include warmup corrections, parameter documentation, output type accuracy, and Pine Script alignment.
2026-03-10 18:38:23 -07:00

89 lines
3.2 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Ehlers Reflex Indicator (REFLEX)", "REFLEX", overlay = false)
//@function Ehlers Reflex — a zero-lag oscillator that measures the reflex (reversal
// tendency) of price by comparing the SSF-filtered price against a linear
// extrapolation from N bars ago. Applies a 2-pole Super Smoother pre-filter
// at half the specified period, then computes slope = (Filt[N] - Filt) / N,
// sums deviations of the extrapolated line from actual filtered values over
// the window, and normalizes by exponential RMS. Values above 0 suggest
// uptrend, below 0 suggest downtrend; crossovers signal reversals.
//@param source Series to analyze
//@param period Lookback window / assumed cycle period (>= 2)
//@returns Reflex oscillator value (normalized, roughly ±σ scale)
//@reference Ehlers, J.F. (2020). "Reflex: A New Zero-Lag Indicator."
// Technical Analysis of Stocks & Commodities, Feb 2020.
//@optimized O(period) per bar for the summation loop; SSF is O(1) IIR
reflex(series float source, simple int period) =>
if period < 2
runtime.error("Period must be at least 2")
float price = nz(source)
// --- 2-Pole Super Smoother Filter (half-period cutoff) ---
float half_period = period * 0.5
float a1 = math.exp(-1.414 * math.pi / half_period)
float b1 = 2.0 * a1 * math.cos(1.414 * math.pi / half_period)
float c2 = b1
float c3 = -(a1 * a1)
float c1 = 1.0 - c2 - c3
var float filt = 0.0
var float filt1 = 0.0
var float filt2 = 0.0
float src1 = nz(source[1])
filt2 := filt1
filt1 := filt
filt := c1 * (price + src1) * 0.5 + c2 * filt1 + c3 * filt2
// --- Circular buffer to store filtered values for lookback ---
var array<float> buf = array.new_float(period + 1, 0.0)
var int head = 0
array.set(buf, head, filt)
int count = math.min(bar_index + 1, period)
// --- Slope: (Filt[Length] - Filt) / Length ---
int lag_idx = (head - period + period + 1) % (period + 1)
float filt_lag = array.get(buf, lag_idx)
float slope = (filt_lag - filt) / period
// --- Sum the differences ---
// Sum = Σ(i=1..Length) [(Filt + i*Slope) - Filt[i]] / Length
float the_sum = 0.0
if count >= period
for i = 1 to period
int idx = (head - i + period + 1) % (period + 1)
float filt_i = array.get(buf, idx)
the_sum += (filt + float(i) * slope) - filt_i
the_sum /= period
// --- Advance head ---
head := (head + 1) % (period + 1)
// --- Normalize in terms of Standard Deviations ---
// MS = 0.04 * Sum² + 0.96 * MS[1] (exponential RMS)
var float ms = 0.0
ms := 0.04 * the_sum * the_sum + 0.96 * ms
float result = 0.0
if ms > 0.0
result := the_sum / math.sqrt(ms)
result
// ── Inputs ──
int p_period = input.int(20, "Period", minval = 2)
float p_src = input.source(close, "Source")
// ── Calculation ──
float out = reflex(p_src, p_period)
// ── Plot ──
plot(out, "REFLEX", color.yellow, 2)
hline(0, "Zero", color.gray)
hline(1.0, "+1σ", color.new(color.red, 60))
hline(-1.0, "-1σ", color.new(color.green, 60))