Percentile Rank Oscillator. Conversion Pinescript a PRT

Forums ProRealTime foro Español Soporte ProBuilder Percentile Rank Oscillator. Conversion Pinescript a PRT

Viewing 7 posts - 1 through 7 (of 7 total)
  • #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

    1. Takes midpoint of each candle ((H+L)/2)
    2. Ranks the current value vs previous N bars using rolling percentile rank
    3. Maps percentile to a normalized oscillator scale (-1..+1 or 0–100)
    4. Optionally evaluates VWMA deviation percentile for volume-confirmed signals
    5. 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.0

    if 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) : na

    final_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) / 1e6

    h_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!!!

     

    #254630

    Hola (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:

    #254631

    Disculpad 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.

    #254638

    Buenas tardes! Aquí tienes el indicador traducido. Es lo más parecido al original que he conseguido.

     

    #254649

    Muchas 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.

    #254653

    Buenas! 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:

    1. Poner a los 100 alumnos en fila.

    2. Ordenarlos por nota de menor a mayor.

    3. 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:

    1. Tomamos al alumno actual (el precio actual).

    2. Lo comparamos uno por uno con los otros 99 alumnos anteriores (los precios pasados), sin moverlos de su sitio.

    3. 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 🙂

    #254658

    Entendido!!!

    Muchas gracias, Ivan!!!

    Recibe un cordial saludo.

Viewing 7 posts - 1 through 7 (of 7 total)

Create your free account now and post your request to benefit from the help of the community
Register or Login