Search code examples
pine-scriptpine-script-v5

print percentile rank for each bar


I want to display the percentile rank for each candle bar, i.e. compare its range with other bars in a specified period.

My code:

//@version=5
indicator("Percentile rank", overlay=true, max_labels_count = 500)

bar_range() =>
    candle_range = math.abs(high[0] - low[0])
    candle_range

candle_percentile_rank(comparison_interval) =>
    candle_range = bar_range()
    //select bars from the specified interval
    candle_range_array = array.new_float(0)
    for i = 0 to comparison_interval
        array.push(candle_range_array, candle_range[i])
    //calculate percentile rank
    current_bar_percentile = array.percentile_nearest_rank(candle_range_array, 0) * 100
    current_bar_percentile

//plot bar ranges
candle_range = bar_range()
l1 = label.new(bar_index, high, str.tostring(candle_range), yloc = yloc.abovebar)

//plot bar range percentiles
comparison_interval = 100
percentiles = candle_percentile_rank(comparison_interval)
l2 = label.new(bar_index, high, str.tostring(percentiles), yloc = yloc.belowbar)

The problem is that the percentile printed is the same for all bars or for many consecutive bars until another value shows up again with all the following bars.

E.g. on TSLA 5min Sep 5, 2023 the big bar at 10:05 shows rank 39 as all the surrounding (much smaller) bars.


Solution

  • A workaround solution, since for my use case, I don't necessarily need to know the bar's rank. Instead, I can calculate the desired percentile value as a threshold value and compare it to the current bar. This way, I avoid the use of the array.percentile_nearest_rank().

    //@version=5
    indicator("Percentile test", overlay=true, max_labels_count = 500)
    
    percentile = input.int(defval = 80, title="Percentile")
    
    bar_range() =>
        candle_range = math.abs(high[0] - low[0])
        candle_range
    
    candle_percentile_threshold(comparison_interval, percentile) =>
        candle_range = bar_range()
        //select bars from the specified interval
        candle_range_array = array.new_float(0)
        for i = 0 to comparison_interval - 1
            array.push(candle_range_array, candle_range[i])
        //calculate the value for the specified percentile
        array.sort(candle_range_array, order.ascending)
        index = math.round((percentile / 100) * comparison_interval) - 1
        threshold_value = array.remove(candle_range_array, index)
        //current_bar_percentile = array.percentile_nearest_rank(candle_range_array, 0) * 100
        threshold_value
    
    //plot bar ranges
    candle_range = bar_range()
    l1 = label.new(bar_index, high, str.tostring(candle_range), yloc = yloc.abovebar)
    
    //plot bar range percentiles
    comparison_interval = 100
    threshold_value = candle_percentile_threshold(comparison_interval, percentile)
    bar_above_threshold = candle_range[0] >= threshold_value ? true : false
    l2 = label.new(bar_index, low, str.tostring(bar_above_threshold), yloc = yloc.belowbar)