Multiple Percentile Ranks (up to 5 sources at a time)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Dean_Crypto

//@version=5
indicator("Multiple Percentile Ranks", shorttitle = "MPR", max_labels_count = 500)


inline1 = "i1"
inline2 = "i2"
inline3 = "i3"
inline4 = "i4"
inline5 = "i5"
groupOptions = "options"
group1 = "source 1"
group2 = "source 2"
group3 = "source 3"
group4 = "source 4"
group5 = "source 5"

sources = input.float(defval = 5, minval = 1, maxval = 5, step = 1, group = groupOptions)
sourceLabelPctLocation = input.int(defval = 2, group = groupOptions, title = "label % position")
sourceLabelLocation = input.int(defval = 6, group = groupOptions, title = "label position")

textSizeOption = input.string(title = "label text size", defval = "large", options = ["auto", "huge",  "large", "normal",  "small",  "tiny"], group = groupOptions)
displayCurrentPct = input.bool(defval = true, title = "display current % label")


hlSourceOpt1 = input.string(defval = "volume",      title = "source 1", options =  ["atr (MA)", "cci (MA)", "cog (MA)", "close", "close percent", "dollar value", "eom", "gaps", "high", "low", "mfi (MA)", "obv", "open", "range (MA)", "rsi (MA)", "rvi (MA)", "timeClose (MA)", "volume", "volume (MA)"], inline = inline1, group = group1)
hlSourceOpt2 = input.string(defval = "volume (MA)", title = "source 2", options =  ["atr (MA)", "cci (MA)", "cog (MA)", "close", "close percent", "dollar value", "eom", "gaps", "high", "low", "mfi (MA)", "obv", "open", "range (MA)", "rsi (MA)", "rvi (MA)", "timeClose (MA)", "volume", "volume (MA)"], inline = inline1, group = group2)
hlSourceOpt3 = input.string(defval = "range (MA)",  title = "source 3", options =  ["atr (MA)", "cci (MA)", "cog (MA)", "close", "close percent", "dollar value", "eom", "gaps", "high", "low", "mfi (MA)", "obv", "open", "range (MA)", "rsi (MA)", "rvi (MA)", "timeClose (MA)", "volume", "volume (MA)"], inline = inline1, group = group3)
hlSourceOpt4 = input.string(defval = "mfi (MA)",    title = "source 4", options =  ["atr (MA)", "cci (MA)", "cog (MA)", "close", "close percent", "dollar value", "eom", "gaps", "high", "low", "mfi (MA)", "obv", "open", "range (MA)", "rsi (MA)", "rvi (MA)", "timeClose (MA)", "volume", "volume (MA)"], inline = inline1, group = group4)
hlSourceOpt5 = input.string(defval = "eom",         title = "source 5", options =  ["atr (MA)", "cci (MA)", "cog (MA)", "close", "close percent", "dollar value", "eom", "gaps", "high", "low", "mfi (MA)", "obv", "open", "range (MA)", "rsi (MA)", "rvi (MA)", "timeClose (MA)", "volume", "volume (MA)"], inline = inline1, group = group5)

// len only applies if a source with (MA) has been selected
sDataLen1 = input.int(defval  = 20 , minval = 1, title = "ma length", inline = inline1, group = group1)
sDataLen2 = input.int(defval  = 20 , minval = 1, title = "ma length", inline = inline1, group = group2)
sDataLen3 = input.int(defval  = 20 , minval = 1, title = "ma length", inline = inline1, group = group3)
sDataLen4 = input.int(defval  = 20 , minval = 1, title = "ma length", inline = inline1, group = group4)
sDataLen5 = input.int(defval  = 20 , minval = 1, title = "ma length", inline = inline1, group = group5)


aget(id, i)     => array.get(id, i)
aget0(id)       => array.get(id, 0)
aset(id, i, v)  => array.set(id, i, v)
asize(id)       => array.size(id)
apush(id, v)    => array.push(id, v)
apop(id)        => array.pop(id)
ashift(id)      => array.shift(id)
unshift(id, v)  => array.unshift(id, v)
bi = bar_index
lbi = last_bar_index

closePercentChange() => (nz(close[1], low) < close ? 1 : -1) * (high - low) / low * 100
closePercent = closePercentChange()

// switch function for source choices
sources(optionName, len) =>
    switch optionName
        "atr (MA)"           => ta.atr(len)
        "cci (MA)"           => ta.cci(close, len)
        "close percent"      => closePercent
        "close"              => close
        "cog (MA)"           => ta.cog(close, len)
        "dollar value"       => volume * hlc3
        "eom"                => volume / closePercent
        "gaps"               => open - close[1]
        "high"               => high
        "low"                => low
        "mfi (MA)"           => ta.mfi(hlc3, len)
        "obv"                => ta.obv
        "open"               => open
        "range (MA)"         => math.round(((ta.highest(high, len) - ta.lowest(low, len)) / ta.highest(high, len) * 100), 1)
        "rsi (MA)"           => ta.rsi(close, len)   
        "rvi (MA)"           => ta.ema((close - open) / (high - low) * volume, len)
        "timeClose (MA)"     => ta.sma(time_close, 10)
        "volume (MA)"        => ta.ema(volume, len)
        "volume"             => volume


size(sizeName) =>
    switch sizeName
        "auto" => size.auto
        "huge" => size.huge
        "large" => size.large
        "normal" => size.normal
        "small" => size.small
        "tiny" => size.tiny


labelTextSize = size(textSizeOption)
//assigning each source to a switch option
source1 = sources(hlSourceOpt1, sDataLen1)
source2 = sources(hlSourceOpt2, sDataLen2)
source3 = sources(hlSourceOpt3, sDataLen3)
source4 = sources(hlSourceOpt4, sDataLen4)
source5 = sources(hlSourceOpt5, sDataLen5)


lowestPercentileRankInput1 = input.int(defval = 5,  step = 1, minval = 0, maxval = 100, title = "bottom %", inline = inline2, group = group1)
highestPercentileRankInput1 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "top %", inline = inline2, group = group1)
highestPercentileRank1 = 100 - highestPercentileRankInput1

lowestPercentileRankInput2 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "bottom %", inline = inline2 , group = group2)
highestPercentileRankInput2 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "top %", inline = inline2, group = group2)
highestPercentileRank2 = 100 - highestPercentileRankInput2

lowestPercentileRankInput3 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "bottom %", inline = inline2 , group = group3)
highestPercentileRankInput3 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "top %", inline = inline2, group = group3)
highestPercentileRank3 = 100 - highestPercentileRankInput3

lowestPercentileRankInput4 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "bottom %", inline = inline2 , group = group4)
highestPercentileRankInput4 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "top %", inline = inline2, group = group4)
highestPercentileRank4 = 100 - highestPercentileRankInput4

lowestPercentileRankInput5 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "bottom %", inline = inline2 , group = group5)
highestPercentileRankInput5 = input.int(defval = 5, step = 1, minval = 0, maxval = 100, title = "top %", inline = inline2, group = group5)
highestPercentileRank5 = 100 - highestPercentileRankInput5



var sourceBars1 = array.new_float()
var sourceBars2 = array.new_float()
var sourceBars3 = array.new_float()
var sourceBars4 = array.new_float()
var sourceBars5 = array.new_float()


array.push(sourceBars1, source1)
if sources > 1
    array.push(sourceBars2, source2)
if sources > 2
    array.push(sourceBars3, source3)
if sources > 3
    array.push(sourceBars4, source4)
if sources > 4
    array.push(sourceBars5, source5)


pRank1 = array.percentrank(sourceBars1, bar_index)
pRank2 = array.percentrank(sourceBars2, bar_index)
pRank3 = array.percentrank(sourceBars3, bar_index)
pRank4 = array.percentrank(sourceBars4, bar_index)
pRank5 = array.percentrank(sourceBars5, bar_index)


plotColorCondition(sourceValue, lowestFilter, highestFilter) =>
    sourceValue <= lowestFilter ? color.red : sourceValue >= highestFilter ? color.green : color.blue


hline(0)
hline(sources > 0 ? 1 : na)
hline(sources > 1 ? 2 : na)
hline(sources > 2 ? 3 : na)
hline(sources > 3 ? 4 : na)
hline(sources > 4 ? 5 : na)

sourceLabelCondition2 = sources > 1 ? bar_index + sourceLabelLocation : na
sourceLabelCondition3 = sources > 2 ? bar_index + sourceLabelLocation : na
sourceLabelCondition4 = sources > 3 ? bar_index + sourceLabelLocation : na
sourceLabelCondition5 = sources > 4 ? bar_index + sourceLabelLocation : na

pRankCondition1 = sources > 0 ? (pRank1 / 100) : na
pRankCondition2 = sources > 1 ? (pRank2 / 100) + 1 : na
pRankCondition3 = sources > 2 ? (pRank3 / 100) + 2 : na
pRankCondition4 = sources > 3 ? (pRank4 / 100) + 3 : na
pRankCondition5 = sources > 4 ? (pRank5 / 100) + 4 : na

plot(pRankCondition1, color = plotColorCondition(pRank1, lowestPercentileRankInput1, 
     highestPercentileRank1), style = plot.style_line, display = display.pane)
plot(pRankCondition2, color = plotColorCondition(pRank2, lowestPercentileRankInput2,
     highestPercentileRank2), style = plot.style_line, display = display.pane)
plot(pRankCondition3, color = plotColorCondition(pRank3, lowestPercentileRankInput3,
     highestPercentileRank3), style = plot.style_line, display = display.pane)
plot(pRankCondition4, color = plotColorCondition(pRank4, lowestPercentileRankInput4,
     highestPercentileRank4), style = plot.style_line, display = display.pane)
plot(pRankCondition5, color = plotColorCondition(pRank5, lowestPercentileRankInput5,
     highestPercentileRank5), style = plot.style_line, display = display.pane)

pctToStr(percentile, sourceNumber) => str.tostring(math.round(((percentile - sourceNumber) * 100), 1)) + "%"

sourceLabel1 = label.new(x = bar_index + sourceLabelLocation, y = 0.5, text = hlSourceOpt1, color = color.rgb(0, 0, 0),
     textcolor = color.white, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabel1[1])

sourceLabelPercentile1 = label.new(x = displayCurrentPct ? bar_index + sourceLabelPctLocation : na, y = 0.5, 
     text      = pctToStr(pRankCondition1, 0),
     color     = color.rgb(0, 0, 0), 
     textcolor = plotColorCondition(pRank1, lowestPercentileRankInput1, highestPercentileRank1), 
     style     = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelPercentile1[1])

sourceLabel2 = label.new(x = sourceLabelCondition2, y = 1.5, text = hlSourceOpt2, color = color.rgb(0, 0, 0), textcolor = color.white, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabel2[1])

sourceLabelPercentile2 = label.new(x = displayCurrentPct and (sources > 1) ? bar_index + sourceLabelPctLocation : na, 
     y = 1.5,
     text      = pctToStr(pRankCondition2, 1),
     color     = color.rgb(0, 0, 0),
     textcolor = plotColorCondition(pRank2, lowestPercentileRankInput2, highestPercentileRank2),
     style     = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelPercentile2[1])

sourceLabel3 = label.new(x = sourceLabelCondition3, y = 2.5, text = hlSourceOpt3, color = color.rgb(0, 0, 0), textcolor = color.white, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabel3[1])

sourceLabelPercentile3 = label.new(x = displayCurrentPct and (sources > 2) ? bar_index + sourceLabelPctLocation : na, 
     y = 2.5,
     text      = pctToStr(pRankCondition3, 2),
     color     = color.rgb(0, 0, 0),
     textcolor = plotColorCondition(pRank3, lowestPercentileRankInput3, highestPercentileRank3),
     style     = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelPercentile3[1])

sourceLabel4 = label.new(x = sourceLabelCondition4, y = 3.5, text = hlSourceOpt4, color = color.rgb(0, 0, 0), textcolor = color.white, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabel4[1])

sourceLabelPercentile4 = label.new(x = displayCurrentPct and (sources > 3) ? bar_index + sourceLabelPctLocation : na, 
     y = 3.5,
     text      = pctToStr(pRankCondition4, 3),
     color     = color.rgb(0, 0, 0),
     textcolor = plotColorCondition(pRank4, lowestPercentileRankInput4, highestPercentileRank4),
     style     = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelPercentile4[1])



sourceLabel5 = label.new(x = sourceLabelCondition5, y = 4.5, text = hlSourceOpt5, color = color.rgb(0, 0, 0), textcolor = color.white, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabel5[1])

sourceLabelPercentile5 = label.new(x = displayCurrentPct and (sources > 4) ? bar_index + sourceLabelPctLocation : na, 
     y = 4.5,
     text      = pctToStr(pRankCondition5, 4),
     color     = color.rgb(0, 0, 0),
     textcolor = plotColorCondition(pRank5, lowestPercentileRankInput5, highestPercentileRank5),
     style     = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelPercentile5[1])

sourceLabelHighest1 = label.new(x = bar_index + sourceLabelLocation, y = 0.75, text = "top " + str.tostring(highestPercentileRankInput1) + "%", color = color.rgb(0, 0, 0), textcolor = color.green, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelHighest1[1])

sourceLabelLowest1 = label.new(x = bar_index + sourceLabelLocation, y = 0.25, text = "bottom " + str.tostring(lowestPercentileRankInput1) + "%", color = color.rgb(0, 0, 0), textcolor = color.red, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelLowest1[1])

sourceLabelHighest2 = label.new(x = sourceLabelCondition2, y = 1.75, text = "top " + str.tostring(highestPercentileRankInput2) + "%", color = color.rgb(0, 0, 0), textcolor = color.green, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelHighest2[1])

sourceLabelLowest2 = label.new(x = sourceLabelCondition2, y = 1.25, text = "bottom " + str.tostring(lowestPercentileRankInput2) + "%", color = color.rgb(0, 0, 0), textcolor = color.red, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelLowest2[1])

sourceLabelHighest3 = label.new(x = sourceLabelCondition3, y = 2.75, text = "top " + str.tostring(highestPercentileRankInput3) + "%", color = color.rgb(0, 0, 0), textcolor = color.green, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelHighest3[1])

sourceLabelLowest3 = label.new(x = sourceLabelCondition3, y = 2.25, text = "bottom " + str.tostring(lowestPercentileRankInput3) + "%", color = color.rgb(0, 0, 0), textcolor = color.red, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelLowest3[1])

sourceLabelHighest4 = label.new(x = sourceLabelCondition4, y = 3.75, text = "top " + str.tostring(highestPercentileRankInput4) + "%", color = color.rgb(0, 0, 0), textcolor = color.green, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelHighest4[1])

sourceLabelLowest4 = label.new(x = sourceLabelCondition4, y = 3.25, text = "bottom " + str.tostring(lowestPercentileRankInput4) + "%", color = color.rgb(0, 0, 0), textcolor = color.red, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelLowest4[1])

sourceLabelHighest5 = label.new(x = sourceLabelCondition5, y = 4.75, text = "top " + str.tostring(highestPercentileRankInput5) + "%", color = color.rgb(0, 0, 0), textcolor = color.green, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelHighest5[1])

sourceLabelLowest5 = label.new(x = sourceLabelCondition5, y = 4.25, text = "bottom " + str.tostring(lowestPercentileRankInput5) + "%", color = color.rgb(0, 0, 0), textcolor = color.red, style = label.style_text_outline, size = labelTextSize)
label.delete(sourceLabelLowest5[1])


