KDE Reversals [UAlgo]KDE Reversals is a statistical reversal oscillator that measures where the current price sits inside its recent distribution using a Kernel Density Estimation based cumulative probability model. Instead of relying on fixed momentum formulas or classic overbought and oversold oscillators, the script builds a rolling sample of recent source values, estimates a smoothed empirical distribution, and converts the current value into a percentile style reading from 0 to 100.
The result is a non parametric probability oscillator that answers a simple question: how extreme is the current price relative to the recent sample? Very high readings mean the current value is located near the upper tail of the recent distribution. Very low readings mean it is near the lower tail. The script then uses user defined upper and lower reversal zones to detect potential turning points when the percentile reading exits those extreme regions.
The indicator runs in a separate pane ( overlay=false ) and combines:
A rolling KDE based empirical CDF oscillator
Upper and lower statistical reversal zones
Gradient coloring based on percentile position
Background highlighting in extreme conditions
Optional reversal labels when the percentile exits an extreme zone
This makes the tool especially useful for traders who want a more distribution aware approach to reversal detection rather than a fixed oscillator threshold based only on momentum formulas.
Educational tool only. Not financial advice.
🔹 Features
🔸 1) KDE Based Empirical CDF Oscillator
The core output of the script is a percentile style oscillator built from Kernel Density Estimation. It estimates the cumulative distribution position of the current source value relative to a rolling lookback sample and expresses that result as a percentage from 0 to 100.
This gives a probabilistic location measure rather than a raw momentum reading.
🔸 2) Rolling Lookback Distribution Model
The script stores a rolling window of recent source values in an internal array. As new bars arrive, the oldest values are removed once the array reaches the configured length. This keeps the distribution adaptive to recent market behavior.
Because the model is rolling, the oscillator can adjust as the market shifts from one regime to another.
🔸 3) Non Parametric Reversal Logic
Unlike indicators that assume a fixed normal distribution of prices, this script uses a kernel smoothed empirical distribution. That means the reversal zones are based on the actual recent sample shape, not a rigid assumption about how price should be distributed.
This can make the signal more responsive to skewed, compressed, or uneven recent market structure.
🔸 4) Custom Upper and Lower Reversal Zones
Users can define:
An upper reversal zone in percentile terms
A lower reversal zone in percentile terms
These thresholds determine what counts as statistically stretched relative to the recent sample. This allows the indicator to be tuned for more aggressive or more selective reversal detection.
🔸 5) Reversal Triggers on Exit from Extremes
The script does not trigger simply because the oscillator enters an extreme zone. Instead, it triggers when the percentile reading exits the extreme zone:
A sell trigger occurs when the oscillator crosses back below the upper threshold
A buy trigger occurs when the oscillator crosses back above the lower threshold
This design aims to catch reversal confirmation after an extreme condition begins to unwind.
🔸 6) Dynamic Gradient Coloring
The percentile line is colored with a gradient between bullish and bearish colors based on its position between the lower and upper thresholds. This makes it easy to identify whether the current reading is leaning toward lower tail, neutral, or upper tail conditions.
🔸 7) Upper and Lower Zone Fills
The script includes gradient fills above and below the midline so the oscillator visually emphasizes upper tail and lower tail behavior. This improves readability and helps the user quickly identify whether the current reading is operating in a statistically stretched region.
🔸 8) Extreme Zone Background Highlighting
When the oscillator is above the upper threshold or below the lower threshold, the pane background is lightly highlighted. This creates an immediate visual cue that the current reading is inside a high probability reversal watch zone.
🔸 9) Optional Reversal Labels
When enabled, the script prints:
A downward label after a bearish reversal trigger
An upward label after a bullish reversal trigger
These labels are intentionally simple and keep the pane clean while still marking the event clearly.
🔸 10) Flexible Source Selection
The user can choose which source series to analyze, not only close. This allows the KDE engine to be applied to other price derived series if desired.
🔹 Calculations
1) Rolling Data Queue
The script stores recent source values in a custom KDEData object:
type KDEData
array prices
int length
Each bar, the new value is pushed into the array:
this.prices.push(val)
if this.prices.size() > this.length
this.prices.shift()
This creates a fixed length rolling sample used for the KDE calculation.
2) Interquartile Range (IQR) Calculation
To make the bandwidth estimate more robust, the script computes the interquartile range from a sorted copy of the rolling sample:
int q1_idx = int(math.floor(n * 0.25))
int q3_idx = int(math.floor(n * 0.75))
sorted.get(q3_idx) - sorted.get(q1_idx)
The IQR is later used as part of the spread estimate for kernel bandwidth selection.
3) Robust Spread Estimate
The script combines sample standard deviation and IQR based scaling:
float stdev = this.prices.stdev()
float iqr = this.get_iqr()
float spread = math.min(stdev, iqr / 1.34)
Interpretation:
iqr / 1.34 is a robust estimate of standard deviation under near normal assumptions.
Taking the minimum of stdev and iqr / 1.34 helps reduce the effect of extreme outliers when setting the kernel width.
If the spread collapses to zero, the script falls back to a very small positive value.
4) KDE Bandwidth Selection
The kernel bandwidth is computed using a Silverman style rule:
float h = 1.06 * spread * math.pow(n, -0.2)
This gives the smoothing width used in the Gaussian kernel estimation. Larger sample size reduces the bandwidth, while larger spread increases it.
If the calculated bandwidth is zero, the script forces a small fallback:
if h == 0
h := 0.0001
5) Gaussian Error Function Approximation
The script defines its own approximation of the error function:
erf(float x) =>
...
This function is then used to build the standard normal cumulative distribution function:
norm_cdf(float z) =>
float sqrt2 = math.sqrt(2)
0.5 * (1.0 + erf(z / sqrt2))
This is the mathematical core that converts standardized distances into cumulative probabilities.
6) KDE Based Empirical CDF Calculation
For the current price, the script computes a smoothed empirical CDF by averaging the Gaussian CDF centered on every historical sample point:
for i = 0 to n - 1
float xi = this.prices.get(i)
float z = (current_price - xi) / h
sum_prob += norm_cdf(z)
Then:
(sum_prob / n) * 100.0
Interpretation:
Each historical observation contributes a smooth cumulative probability curve.
Averaging them creates a KDE smoothed empirical percentile estimate for the current price.
This is more stable than a raw rank percentile because it smooths the distribution instead of using hard cutoffs only.
7) Warm Up Condition
The indicator only computes the KDE percentile once the rolling sample is fully populated:
if kde.prices.size() == lengthInput
cdf_percent := kde.get_kde_cdf(srcInput)
Before that, the output remains na , which prevents incomplete early calculations.
8) Oscillator Interpretation
The resulting cdf_percent is a percentile style value:
Near 0 means the current source is near the lower tail of the recent distribution
Near 50 means it is near the middle of the recent distribution
Near 100 means it is near the upper tail of the recent distribution
This is not a momentum ratio. It is a location measure inside the recent smoothed distribution.
9) Threshold Logic
The user defines:
float upperThreshold = input.float(95.0, ...)
float lowerThreshold = input.float(5.0, ...)
These thresholds define statistically extreme regions. For example:
A 95 reading means the source is near the top tail of the recent sample
A 5 reading means the source is near the bottom tail
10) Reversal Trigger Conditions
The script does not trigger on entry into the zone. It triggers when the oscillator exits the zone:
Bearish reversal trigger:
bool sellTrigger = ta.crossunder(cdf_percent, upperThreshold)
Bullish reversal trigger:
bool buyTrigger = ta.crossover(cdf_percent, lowerThreshold)
Interpretation:
A sell trigger means the percentile was above the upper threshold and then moved back below it.
A buy trigger means the percentile was below the lower threshold and then moved back above it.
This acts more like a reversion confirmation than an early warning.
11) Visual Gradient Line
The main oscillator line color is derived from its current percentile position:
color cdfColor = color.from_gradient(cdf_percent, lowerThreshold, upperThreshold, col_bull, col_bear)
This creates a smooth transition from bullish coloring in the lower reversal area toward bearish coloring in the upper reversal area.
12) Gradient Fill Zones
The script fills the area between the percentile line and the hidden midline (50) separately for upper and lower halves:
fill(p_cdf, p_mid, top_value=100, bottom_value=50, ...)
fill(p_cdf, p_mid, top_value=50, bottom_value=0, ...)
This gives the oscillator a cleaner and more informative visual structure than a plain line alone.
13) Background Highlighting
When the oscillator is inside an extreme zone, the pane background is lightly shaded:
bgcolor(cdf_percent >= upperThreshold ? ... : cdf_percent <= lowerThreshold ? ... : na)
This does not trigger a signal by itself. It simply highlights that the reading is currently in a statistically extreme region.
14) Reversal Labels
If labels are enabled, the script marks reversal exits with simple directional arrows:
For bearish reversal:
label.new(bar_index, cdf_percent + 3, "▼", ...)
For bullish reversal:
label.new(bar_index, cdf_percent - 3, "▲", ...)
The labels are plotted near the oscillator value, not on price, which keeps the indicator self contained in its own pane.
Indicador Pine Script®




