Search code examples
pine-scriptpine-script-v5

Improve Pinescript Efficiency - Array calculation and plotting?


I have the following code to try and count the number of occurences of a certain indicator value before plotting relevant lines, however it's very inefficient and slow with my current method - leading it to break frequently. I imagine it would be better if it only calculated on the most recent candle to save computing power, but I haven't figured how to accomplish that. I'm sure there's also some array function to calculate this all from 'i = 0 to 100' or something; but I've tried so many different variations and just can't get it to stick without having this unnecessarily long code:

freq_50 = ta.cum(math.round(ta.rsi(close, 14)) == 50 ? 1 : 0)
freq_49 = ta.cum(math.round(ta.rsi(close, 14)) == 49 ? 1 : 0)
* All the way from 100 to 0

ratio_sort = array.from(freq_100, freq_99, freq_98, freq_97, freq_96, freq_95, freq_94, freq_93, freq_92, freq_91, freq_90,
   freq_89, freq_88, freq_87, freq_86, freq_85, freq_84, freq_83, freq_82, freq_81, freq_80, freq_79, freq_78, freq_77,
   freq_76, freq_75, freq_74, freq_73, freq_72, freq_71, freq_70, freq_69, freq_68, freq_67, freq_66, freq_65, freq_64,
   freq_63, freq_62, freq_61, freq_60, freq_59, freq_58, freq_57, freq_56, freq_55, freq_54, freq_53, freq_52, freq_51,
   freq_50, freq_49, freq_48, freq_47, freq_46, freq_45, freq_44, freq_43, freq_42, freq_41, freq_40, freq_39, freq_38,
   freq_37, freq_36, freq_35, freq_34, freq_33, freq_32, freq_31, freq_30, freq_29, freq_28, freq_27, freq_26, freq_25,
   freq_24, freq_23, freq_22, freq_21, freq_20, freq_19, freq_18, freq_17, freq_16, freq_15, freq_14, freq_13, freq_12,
   freq_11, freq_10, freq_9, freq_8, freq_7, freq_6, freq_5, freq_4, freq_3, freq_2, freq_1, freq_0)
ratio_max = array.max(ratio_sort, 0)

line_50 = line.new(0, array.percentile_linear_interpolation(ratio_rank, 50), bar_index, array.percentile_linear_interpolation(ratio_rank, 50))
line.set_x1(line_50, bar_index + (math.round(ratio_max) - math.round(freq_50)))
line.set_x2(line_50, bar_index + math.round(ratio_max))
line.delete(line_50[1])

line_49 = line.new(0, array.percentile_linear_interpolation(ratio_rank, 49), bar_index, array.percentile_linear_interpolation(ratio_rank, 49))
line.set_x1(line_49, bar_index + (math.round(ratio_max) - math.round(freq_49)))
line.set_x2(line_49, bar_index + math.round(ratio_max))
line.delete(line_49[1])
* Again all the way from 100 to 0

Any help would be appreciated! Let me know if you have anymore questions.

My best attempt, however no lines were displayed:

lineArr = array.new_line()

ratio_max = math.max(array.percentile_linear_interpolation(ta.rsi(close, 14), 0), array.percentile_linear_interpolation(ta.rsi(close, 14), 100))

if barstate.isconfirmed
    for i = 100 to 0
        freq = ta.cum(math.round(ta.rsi(close, 14)) == i ? 1 : 0)
        line_var = line.new(bar_index, array.percentile_linear_interpolation(ta.rsi(close, 14), i), bar_index + math.round(freq), array.percentile_linear_interpolation(ta.rsi(close, 14), i))
        array.push(lineArr, line_var)

for i = 1 to array.size(lineArr) - 1
    line.delete(array.get(lineArr, i))

for i = 0 to array.size(lineArr) - 1
    line_var = array.get(lineArr, i)
    line.set_x1(line_var, bar_index)
    line.set_x2(line_var, bar_index + math.round(line.get_x2(line_var) - bar_index))

Solution

  • You were on the right track to reduce the amount of code by using arrays. To make the execution more efficient, you need to get rid of re-creating lines on each bar, it is better to create them once and store them in an array, and change their parameters if necessary. Because creation and deletion require additional resources.

    An optimized example of your code with comments:

    //@version=5
    indicator("My script")
    
    method increaseCounterByIndex(int[] arr, index) =>
        currentValue = arr.get(index)
        increasedValue = currentValue + 1
        arr.set(index, increasedValue)
    
    createLinesArray(int size, float[] ratio_rank) =>
        _linesArr = array.new<line>()
        for i = 0 to size-1
            l = line.new(na, na, na, na)
            _linesArr.push(l)
        _linesArr
    
    freqArr = array.new<int>(100, 0) // Create array with 100 entries, every entry is a key for int(rsi) freq value
    rsi = ta.rsi(close, 14)
    arrIndex = math.round(rsi) // Calc the key index for rsi value
    freqArr.increaseCounterByIndex(arrIndex) // Increase freq by this index, so every array element store how many times rsi value equals array index
    
    ratio_max = freqArr.max()
    var linesArray = createLinesArray(100, freqArr) // Create array of lines, change lines parameters is more efficient than create new lines and delete from previous bar
    
    for [idx, currentLine] in linesArray
        // Iterate by all lines and set them new params
        // idx+1 is a half-open interval (0, 100] for percentile_linear_interpolation func. Correct if needed
        // don't quite understand what you mean that ratio_rank is rsi, if so you couldn't to use array.percentile_linear_interpolation because its required an array as first argumetn
        // Fix this if needed according to your idea, for the example I use array of frequencies
        line.set_xy1(currentLine, bar_index + (math.round(ratio_max) - math.round(freqArr.get(idx))), array.percentile_linear_interpolation(freqArr, idx+1))
        line.set_xy2(currentLine, bar_index + math.round(ratio_max), array.percentile_linear_interpolation(freqArr, idx+1))
    

    I don't fully understand how you calculate array.percentile_linear_interpolation because this function requires an array as the first argument and you are suggesting to use rsi. For example, I used an array with a frequency of rsi. I believe this will not affect the general understanding of the example and you will correct if a different calculation was implied there.