Percentile Rank Oscillator. Conversion Pinescript a PRT
Forums › ProRealTime foro Español › Soporte ProBuilder › Percentile Rank Oscillator. Conversion Pinescript a PRT
- This topic has 6 replies, 2 voices, and was last updated 18 hours ago by
Pedro.lopez.
-
-
12/18/2025 at 2:17 PM #254628
Hola de nuevo.
Antes de nada, os deseo unas felices fiestas!!!!
Os agradecería pudierais echarme una mano con la conversión del indicador “Percentile Rank Oscillator (Price + VWMA)”
Lo podéis ver en el siguiente enlace:
La descripción que viene es:
“A statistical oscillator designed to identify potential market turning points using percentile-based price analytics and volume-weighted confirmation.
What is PRO?
Percentile Rank Oscillator measures how extreme current price behavior is relative to its own recent history. It calculates a rolling percentile rank of price midpoints and VWMA deviation (volume-weighted price drift). When price reaches historically rare levels – high or low percentiles – it may signal exhaustion and potential reversal conditions.
How it works
- Takes midpoint of each candle ((H+L)/2)
- Ranks the current value vs previous N bars using rolling percentile rank
- Maps percentile to a normalized oscillator scale (-1..+1 or 0–100)
- Optionally evaluates VWMA deviation percentile for volume-confirmed signals
- Highlights extreme conditions and confluence zones
Why percentile rank?
Median-based percentiles ignore outliers and read the market statistically – not by fixed thresholds. Instead of guessing “overbought/oversold” values, the indicator adapts to current volatility and structure.
Key features
- Rolling percentile rank of price action
- Optional VWMA-based percentile confirmation
- Adaptive, noise-robust structure
- User-selectable thresholds (default 95/5)
- Confluence highlighting for price + VWMA extremes
- Optional smoothing (RMA)
- Visual extreme zone fills for rapid signal recognition
How to use
- High percentile values –> statistically extreme upward deviation (potential top)
- Low percentile values –> statistically extreme downward deviation (potential bottom)
- Price + VWMA confluence strengthens reversal context
- Best used as part of a broader trading framework (market structure, order flow, etc.)
Tip: Look for percentile spikes at key HTF levels, after extended moves, or where liquidity sweeps occur. Strong moves into rare percentile territory may precede mean reversion.
Suggested settings
- Default length: 100 bars
- Thresholds: 95 / 5
- Smoothing: 1–3 (optional)”.
El codigo del indicador en Pinescript es el siguiente:
//@version=6
indicator("Percentile Rank Oscillator (Price + VWMA)", shorttitle="PRO", overlay=false, max_lines_count=80)// ----------------- INPUTS -----------------
grp_core = "Core"
len_in = input.int(100, "Lookback (bars for percentile)", minval=3, maxval=500, group=grp_core)
out_mode = input.string("-1..+1", "Output scale", options=["-1..+1","0..100"], group=grp_core)// VWMA options
grp_vw = "VWMA Percentile"
enable_vwma_pct = input.bool(true, "Enable VWMA-based percentile", group=grp_vw)
vwma_len_same = input.bool(true, "Use VWMA length = main lookback", group=grp_vw)
vwma_len_custom = input.int(100, "VWMA length (if custom)", minval=1, group=grp_vw)// Display & thresholds
grp_display = "Display"
smooth = input.int(1, "Smoothing (RMA) length (1 = none)", minval=1, group=grp_display)
high_thresh = input.float(95.0, "Upper percentile threshold (%)", minval=50.0, maxval=100.0, step=0.1, group=grp_display)
low_thresh = input.float(5.0, "Lower percentile threshold (%)", minval=0.0, maxval=50.0, step=0.1, group=grp_display)
fill_extremes = input.bool(true, "Fill background when extremes hit", group=grp_display)// Alerts & colors
grp_alerts = "Alerts & Colors"
alerts_price = input.bool(true, "Enable price percentile alerts", group=grp_alerts)
alerts_vwma = input.bool(true, "Enable VWMA percentile alerts", group=grp_alerts)
alerts_confl = input.bool(true, "Enable confluence alerts (price + VWMA)", group=grp_alerts) // default ON// Colors: single top/bottom colors to keep UI minimal
grp_colors = "Colors (bg fills)"
top_color = input.color(color.new(#A53860, 80), "Top (overbought) BG color", group=grp_colors)
bot_color = input.color(color.new(#4D8B31, 80), "Bottom (oversold) BG color", group=grp_colors)
band_fill_color = input.color(color.new(#423E3B, 95), "Working band fill (between thresholds)", group=grp_colors)// ----------------- HELPERS & SAFETY -----------------
tiny = 1e-9
len = math.max(3, math.min(len_in, 500))// price midpoint
mid = (high + low) / 2.0// ----------------- rolling percentile with linear interpolation -----------------
// returns percentile rank in 0..100 for current value against last n values (including current)
rolling_percentile_interp(src, n) =>
avail = math.min(n, bar_index + 1)
arr = array.new_float(0)
for i = 0 to avail - 1
array.push(arr, nz(src[i]))
array.sort(arr) // ascending
current = nz(src)
less = 0
equal = 0
for j = 0 to array.size(arr) - 1
v = array.get(arr, j)
less := less + (v < current ? 1 : 0) equal := equal + (v == current ? 1 : 0) pct = 0.0 if equal > 0
pct := (less + 0.5 * equal) / math.max(avail, 1) * 100.0
else
if less == 0
pct := 0.0
else if less >= avail
pct := 100.0
else
v_low = array.get(arr, less - 1)
v_high = array.get(arr, less)
denom = v_high - v_low
frac = denom == 0.0 ? 0.5 : math.max(0.0, math.min(1.0, (current - v_low) / denom))
pct := (less + frac) / math.max(avail, 1) * 100.0
pct// ----------------- PRICE percentile -----------------
price_pct = rolling_percentile_interp(mid, len)
price_pct_smooth = smooth > 1 ? ta.rma(price_pct, smooth) : price_pct
map_to_out(x_pct) => out_mode == "-1..+1" ? (x_pct / 50.0 - 1.0) : x_pct
price_out = map_to_out(price_pct_smooth)// ----------------- VWMA percentile (optional) -----------------
// initialize numeric variables to avoid NA-type reassignments
vwma_pct = 0.0
vwma_pct_smooth = 0.0
vw_out = 0.0if enable_vwma_pct
vwlen = vwma_len_same ? len : math.max(1, vwma_len_custom)
vwma_mid = ta.vwma(mid, vwlen)
disp = mid - vwma_mid
vwma_pct := rolling_percentile_interp(disp, len) // percentile of deviation
vwma_pct_smooth := smooth > 1 ? ta.rma(vwma_pct, smooth) : vwma_pct
vw_out := map_to_out(vwma_pct_smooth)
else
// keep numeric defaults (vw_out stays numeric); plotting will use enable_vwma_pct ? vw_out : na
vwma_pct := 0.0
vwma_pct_smooth := 0.0
vw_out := 0.0// ----------------- thresholds & conditions -----------------
is_price_top = price_pct >= high_thresh
is_price_bot = price_pct <= low_thresh is_vw_top = enable_vwma_pct ? (vwma_pct >= high_thresh) : false
is_vw_bot = enable_vwma_pct ? (vwma_pct <= low_thresh) : false confl_top = enable_vwma_pct and is_price_top and is_vw_top confl_bot = enable_vwma_pct and is_price_bot and is_vw_bot // ----------------- background color priority (global) ----------------- // priority: confluence (price+vw) > price > vwma
bg_price = (is_price_top or is_price_bot) and fill_extremes and alerts_price ? (is_price_top ? top_color : bot_color) : na
bg_vw = (is_vw_top or is_vw_bot) and fill_extremes and alerts_vwma ? (is_vw_top ? top_color : bot_color) : na
bg_confl = (confl_top or confl_bot) and fill_extremes and alerts_confl ? (confl_top ? top_color : bot_color) : nafinal_bg = not na(bg_confl) ? bg_confl : not na(bg_price) ? bg_price : not na(bg_vw) ? bg_vw : na
bgcolor(final_bg)// ----------------- plots (global scope) -----------------
p_price = plot(price_out, title="Price Percentile", color=color.new(#5448C8, 0), linewidth=1)
// plot vw_out only when enabled (use ternary in plot call to show na when disabled)
p_vwma = plot(enable_vwma_pct ? vw_out : na, title="VWMA Percentile", color=color.new(#548687, 0), linewidth=1)// working band lines (in output scale)
// compute raw then round to 6 decimal places to avoid showing awkward floats like 0.899999999....
top_line_raw = out_mode == "-1..+1" ? (high_thresh / 50.0 - 1.0) : high_thresh
bot_line_raw = out_mode == "-1..+1" ? (low_thresh / 50.0 - 1.0) : low_thresh
// round
top_line = math.round(top_line_raw * 1e6) / 1e6
bot_line = math.round(bot_line_raw * 1e6) / 1e6h_top = hline(top_line, "Top", color=#A53860)
h_bot = hline(bot_line, "Bot", color=#4D8B31)
h_mid = hline(out_mode == "-1..+1" ? 0.0 : 50.0, "Mid", color=#423E3B)
fill(h_top, h_bot, band_fill_color)// ----------------- alerts (global scope) -----------------
alertcondition(alerts_price and is_price_top, title="Price Percentile TOP", message="Price percentile reached TOP threshold")
alertcondition(alerts_price and is_price_bot, title="Price Percentile BOTTOM", message="Price percentile reached BOTTOM threshold")alertcondition(enable_vwma_pct and alerts_vwma and is_vw_top, title="VWMA Percentile TOP", message="VWMA percentile reached TOP threshold")
alertcondition(enable_vwma_pct and alerts_vwma and is_vw_bot, title="VWMA Percentile BOTTOM", message="VWMA percentile reached BOTTOM threshold")alertcondition(enable_vwma_pct and alerts_confl and confl_top, title="Confluence TOP", message="Price & VWMA percentiles both at TOP extreme")
alertcondition(enable_vwma_pct and alerts_confl and confl_bot, title="Confluence BOTTOM", message="Price & VWMA percentiles both at BOTTOM extreme")// ----------------- final note -----------------
// Price percentile measures how rare the current midpoint is relative to last 'len' bars.
// VWMA percentile measures how rare the current VWMA deviation (mid - VWMA(mid)) is relative to last 'len' bars.
// Both percentiles use linear interpolation for smooth percent rank values.Muchas gracias y disculpad las molestias!!!
12/18/2025 at 2:23 PM #254630Hola (otra vez)
Como continuación del correo anterior os envío mi intento de conversión a PRT del script en Pinescript del indicador “Percentile Rank Oscillator”.
No se donde estoy equivocando y por eso os pido me echéis una mano, si podéis:Percentile Rank Oscillator_PRT123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140LENIN=100//"Lookback (bars for percentile)", minval=3, maxval=500VWMALEN=100//"VWMA length (if custom)", minval=1smooth =1//"Smoothing (RMA) length (1 = none)", minval=1highthresh =95.0//"Upper percentile threshold (%)", minval=50.0, maxval=100.0, step=0.1lowthresh =5.0//"Lower percentile threshold (%)", minval=0.0, maxval=50.0, step=0.1// ----------------- HELPERS & SAFETY -----------------len =max(3,min(lenin, 500))// price midpointmid =(high + low) / 2.0// ----------------- PRICE percentile -----------------avail = min(len, barindex + 1)for i = 0 to avail - 1$ARR[I]=mid[I]NEXTARRAYSORT($ARR,ASCEND)CURRENT=midless = 0equal = 0for j = 0 to avail-1v = $ARR[J]IF V<CURRENT THENLESS=LESS+1ELSELESS=LESSENDIFIF V=CURRENT THENEQUAL=EQUAL+1ELSEEQUAL=EQUALENDIFNEXTPRICEPCT = 0.0if equal > 0 THENPRICEPCT= ((less + 0.5 * equal) / max(avail, 1) )* 100.0elsif less= 0 THENPRICEPCT= 0.0elsif less >= avail THENPRICEPCT=100.0elsevlow= $arr[less - 1]vhigh= $arr[less]denom= vhigh - vlowIF DENOM= 0 THENFRAC=0.5ELSEFRAC= max(0.0,min(1.0, (current - vlow) / denom))ENDIFPRICEPCT= ((less + frac) / max(avail, 1)) * 100.0ENDIFIF SMOOTH>1 THENALPHA=1/SMOOTHSUM1=0PRICEPCTSMOOTH=ALPHA*PRICEPCT+(1-ALPHA)*SUM1[1]ELSEPRICEPCTSMOOTH=PRICEPCTENDIFpriceout=(PRICEPCTSMOOTH/50)-1// ----------------- VWMA percentile (optional) -----------------// initialize numeric variables to avoid NA-type reassignmentsvw = volume*midvwsum = SUMMATION[VWMALEN](vw)volsum = SUMMATION[VWMALEN](volume)vwmaMID = vwsum/volsumDISP=ABS(MID-VWMAMID)avail1 = min(LEN, barindex + 1)for k = 0 to avail1 - 1$ARR1[k]=DISP[k]NEXTARRAYSORT($ARR1,ASCEND)CURRENT1=DISPless = 0equal = 0for l = 0 to AVAIL1-1v1 = $ARR1[l]IF V1<CURRENT1 THENLESS1=LESS1+1ELSELESS1=LESS1ENDIFIF V1=CURRENT1 THENEQUAL1=EQUAL1+1ELSEEQUAL1=EQUAL1ENDIFNEXTWVMAPCT = 0.0if equal1 > 0 THENWVMAPCT = ((less1 + 0.5 * equal1) / max(avail1, 1) )* 100.0elsif less1 = 0 THENWVMAPCT = 0.0elsif less1 >= avail1 THENWVMAPCT=100.0elsevlow1 = $arr1[less1 - 1]vhigh1 = $arr1[less1]denom1 = vhigh1 - vlow1IF DENOM1=0 THENFRAC1=0.5ELSEFRAC1=max(0.0,min(1.0, (current1 - vlow1) / denom1))ENDIFWVMAPCT=((less1 + frac1) / max(avail1, 1)) * 100.0ENDIFIF SMOOTH>1 THENALPHA=1/SMOOTHSUM2=0WVMAPCTSMOOTH=ALPHA*WVMAPCT+(1-ALPHA)*SUM2[1]ELSEWVMAPCTSMOOTH=WVMAPCTENDIFvwout=(WVMAPCTSMOOTH/50)-1HIGHTHRESH1=((HIGHTHRESH/50)-1)LOWTHRESH1=((LOWTHRESH/50)-1)IF PRICEPCT <= lowthresh1 AND WVMAPCT <= lowthresh1 THENDRAWARROWUP(barindex,priceout)coloured("GREEN")ENDIFIF PRICEPCT >= highthresh1 AND WVMAPCT >= highthresh1 THENDRAWARROWDOWN(barindex,priceout)coloured("RED")ENDIFRETURN priceout AS "PRICE", highthresh1 AS "TOP", lowthresh1 AS "BOT", vwout AS "VWAP"12/18/2025 at 2:28 PM #254631Disculpad pero no he publicado el enlace donde encontrar el indicador. Os lo pego a continuacion:
https://www.tradingview.com/script/vbIUmSZW-Percentile-Rank-Oscillator-Price-VWMA/
A ver si ahora lo he pegado bien.
Disculpad las molestias.
12/18/2025 at 6:36 PM #254638Buenas tardes! Aquí tienes el indicador traducido. Es lo más parecido al original que he conseguido.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193// ----------------------------// PRC_Percentile Rank Oscillator (Price + VWMA)//version = 0//18.12.2025//Iván González @ www.prorealcode.com//Sharing ProRealTime knowledge// ----------------------------LenIn=100// Lookback (periodos)UseScaleMinusOne=1//1 = Escala -1 a +1, 0 = Escala 0 a 100EnableVwmaPct=1 // 1 = Activar VWMA, 0 = DesactivarVwmaLenSame=1 // 1 = Usar mismo periodo para VWMAVwmaLenCustom=100// Periodo VWMA (si VwmaLenSame=0)Smooth=1 // Suavizado (1 = ninguno)HighThresh=95 // 95.0 Umbral SuperiorLowThresh=5 //5.0 Umbral InferiorFillExtremes=1 // 1 = Colorear fondo en extremos// --------------------------------// --- VARIABLES & PARAMETERS ---// --------------------------------// Color Definitions (R, G, B, Alpha)// Top Color (#A53860) -> 165, 56, 96ColorTopR = 165ColorTopG = 56ColorTopB = 96// Bottom Color (#4D8B31) -> 77, 139, 49ColorBotR = 77ColorBotG = 139ColorBotB = 49// Mid/Fill Color (#423E3B) -> 66, 62, 59ColorMidR = 66ColorMidG = 62ColorMidB = 59// Calculation limitsRealLen = MAX(3, LenIn)MidP = (high + low) / 2// --------------------------------// 1. PRICE PERCENTILE CALCULATION// --------------------------------// We calculate how many previous values are lower than the current MidPcountLess = 0countEqual = 0// Loop through history to calculate rankFOR i = 0 TO RealLen - 1 DOIF MidP[i] < MidP THENcountLess = countLess + 1ELSIF MidP[i] = MidP THENcountEqual = countEqual + 1ENDIFNEXT// Calculate raw percentile (0 to 100)// Logic: (Less + 0.5 * Equal) / N * 100PricePct = (countLess + (0.5 * countEqual)) / RealLen * 100// SmoothingIF Smooth > 1 THENPricePctSmooth = WilderAverage[Smooth](PricePct)ELSEPricePctSmooth = PricePctENDIF// Map to Output ScaleIF UseScaleMinusOne THENPriceOut = (PricePctSmooth / 50.0) - 1.0ELSEPriceOut = PricePctSmoothENDIF// --------------------------------// 2. VWMA PERCENTILE CALCULATION// --------------------------------VwmaPct = 0VwOut = 0IF EnableVwmaPct THEN// Determine VWMA LengthIF VwmaLenSame THENCurrentVwLen = RealLenELSECurrentVwLen = MAX(1, VwmaLenCustom)ENDIF// Manual VWMA Calculation: Sum(Price*Vol) / Sum(Vol)VwmaNum = Summation[CurrentVwLen](MidP * Volume)VwmaDenom = Summation[CurrentVwLen](Volume)IF VwmaDenom > 0 THENMyVwma = VwmaNum / VwmaDenomELSEMyVwma = MidPENDIFDisp = MidP - MyVwma// Rolling Percentile for DeviationvCountLess = 0vCountEqual = 0FOR j = 0 TO RealLen - 1 DOHistDisp = Disp[j]IF HistDisp < Disp THENvCountLess = vCountLess + 1ELSIF HistDisp = Disp THENvCountEqual = vCountEqual + 1ENDIFNEXTVwmaPct = (vCountLess + (0.5 * vCountEqual)) / RealLen * 100// SmoothingIF Smooth > 1 THENVwmaPctSmooth = WilderAverage[Smooth](VwmaPct)ELSEVwmaPctSmooth = VwmaPctENDIF// Map OutputIF UseScaleMinusOne THENVwOut = (VwmaPctSmooth / 50.0) - 1.0ELSEVwOut = VwmaPctSmoothENDIFENDIF// --------------------------------// 3. THRESHOLDS & CONDITIONS// --------------------------------// Determine Thresholds based on scaleIF UseScaleMinusOne THENTopLine = (HighThresh / 50.0) - 1.0BotLine = (LowThresh / 50.0) - 1.0MidLine = 0ELSETopLine = HighThreshBotLine = LowThreshMidLine = 50ENDIF// FlagsIsPriceTop = PricePct >= HighThreshIsPriceBot = PricePct <= LowThreshIsVwTop = 0IsVwBot = 0IF EnableVwmaPct THENIsVwTop = VwmaPct >= HighThreshIsVwBot = VwmaPct <= LowThreshENDIFConflTop = EnableVwmaPct AND IsPriceTop AND IsVwTopConflBot = EnableVwmaPct AND IsPriceBot AND IsVwBot// --------------------------------// 4. VISUALIZATION & BACKGROUND// --------------------------------// Background Logic (Priority: Confluence > Price > VWMA)IF FillExtremes THENIF ConflTop THENBACKGROUNDCOLOR(ColorTopR, ColorTopG, ColorTopB, 50)ELSIF ConflBot THENBACKGROUNDCOLOR(ColorBotR, ColorBotG, ColorBotB, 50)ELSIF IsPriceTop THENBACKGROUNDCOLOR(ColorTopR, ColorTopG, ColorTopB, 30)ELSIF IsPriceBot THENBACKGROUNDCOLOR(ColorBotR, ColorBotG, ColorBotB, 30)ELSIF IsVwTop THENBACKGROUNDCOLOR(ColorTopR, ColorTopG, ColorTopB, 20)ELSIF IsVwBot THENBACKGROUNDCOLOR(ColorBotR, ColorBotG, ColorBotB, 20)ENDIFENDIF// Draw Lines// Price Percentile (Blue-ish)// VWMA Percentile (Cyan-ish)// We set VWMA to undefined if disabled so it doesn't plot 0 lineIF EnableVwmaPct = 0 THENVwOut = undefinedENDIF// Fill the band between thresholdsCOLORBETWEEN(TopLine, BotLine, ColorMidR, ColorMidG, ColorMidB, 30)// --------------------------------RETURN PriceOut COLOURED(84, 84, 200) STYLE(Line, 2) AS "Price Percentile", VwOut COLOURED(84, 134, 135) STYLE(Line, 1) AS "VWMA Percentile", TopLine COLOURED(165, 56, 96) AS "Top Threshold", BotLine COLOURED(77, 139, 49) AS "Bot Threshold", MidLine COLOURED(66, 62, 59) STYLE(dottedline) AS "Mid Line"12/19/2025 at 10:50 AM #254649Muchas gracias, Iván!!!!
Tan solo una pregunta, en el código en PineScript utilizan un array y veo que en tu código no lo utilizas, luego realmente no era necesario dicho array, no?
Te lo pregunto porque viendo otros scripts de TradingvView en los que se hace una clasificación por percentiles utilizan arrays (de hecho en informacion que he visto en la www para el calculo de percentiles en excel o a mano el procedimiento parece -o es lo que entiendo, si no estoy equivocado- que hacen uso de los arrays). Es decir, la poblacion de n-datos que se quiere clasificar primero se guarda en arrays, luego se ordena y finalmente se hace la clasificacion por percentiles mediante una formula. La verdad es que el tema me tiene desconcertado y estoy intentando entender la logica.
Recibe un cordial saludo.
12/19/2025 at 11:30 AM #254653Buenas! Tienes toda la razón… en la estadística clásica y en herramientas como Excel, el procedimiento estándar para hallar un percentil implica guardar los datos en una lista (array) y ordenarlos de menor a mayor.
Sin embargo, aquí he optado por un enfoque matemático diferente pero equivalente (rango percentil).
Aquí te explico la lógica exacta para que desaparezca el desconcierto:
1. El enfoque de “Array y Ordenación” (Pine Script / Excel)
Imagina que tienes 100 alumnos en una clase y quieres saber en qué percentil está la nota de un alumno específico. El método clásico es:
-
Poner a los 100 alumnos en fila.
-
Ordenarlos por nota de menor a mayor.
-
Ver en qué posición física ha caído tu alumno (por ejemplo, la posición 95).
2. El enfoque de “Comparación Directa” (el código que te acabo de pasar)
Hacer esa “reordenación de la fila” en cada una de las miles de velas del gráfico consumiría demasiados recursos del sistema. Por eso usamos este otro método:
-
Tomamos al alumno actual (el precio actual).
-
Lo comparamos uno por uno con los otros 99 alumnos anteriores (los precios pasados), sin moverlos de su sitio.
-
Simplemente contamos: “¿Es tu nota más alta que la de este? Sí (+1 punto). ¿Y que la de este? No (+0 puntos)”.
Si al final del recuento, su nota es superior a la de 95 alumnos, matemáticamente está en el Percentil 95.
En fin, espero haberme explicado bien 🙂
12/19/2025 at 12:00 PM #254658Entendido!!!
Muchas gracias, Ivan!!!
Recibe un cordial saludo.
-
AuthorPosts
Find exclusive trading pro-tools on