High Low Index SPY Top 40
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// original code for "High Low Index" by © LonesomeTheBlue
//      - Made modification specifically for Top 40 SPY holdings
//      - Added Market sentiment histogram (Total count green vs red), and SMA line for it
//      - Added arrows for peaks and dips on High Low Index and Market Sentiment MA
// © PtGambler

//@version=5
indicator("High Low Index SPY", explicit_plot_zorder = true)
sym_count = input.string('Top 10', 'Number of Tickers', ['Top 10', 'Top 20', 'Top 30', 'Top 40'], group = "Setup")
timeFrame = input.timeframe(defval = '', title = "Time Frame", group = "Setup")
length = input.int(defval = 10, title = "Length", minval = 1, group = "Setup")
source = input.string(defval = "High/Low", title = "Source", options = ["High/Low", "Close"], group = "Setup")
smoothLength = input.int(defval = 10, title = "Smoothing Length", minval = 1, group = "Setup")
showRecordHighPercent = input.bool(defval = true, title = "Record High Percent", group = "Setup")
showTable= input.bool(defval = true, title = "Show Table", inline = "table", group = "Setup")
textsize = input.string(defval = size.tiny, title = " |  Text Size", options = [size.tiny, size.small, size.normal], inline = "table", group = "Setup")
showEma = input.bool(defval = true, title = "EMA", inline = "ema", group = "Setup")
emaLength = input.int(defval = 10, title = " |  Length", minval = 1, inline = "ema", group = "Setup")
showHisto = input.bool(defval=true, title = "Market Sentiment Histogram", group = "Setup - Market Sentiment")
showHistoMA = input.bool(defval=true, title = "Sentiment MA Line", group = "Setup - Market Sentiment")
HistoLen = input.int(defval = 5, title = "Smoothing Length for Sentiment MA line", minval = 1, group = "Setup - Market Sentiment")
showHLI_PD = input.bool(defval = true, title = "Show Peaks and Dips on High Low Index", group = "Extra")
showHist_PD = input.bool(defval = true, title = "Show Peaks and Dips on Market Sentiment", group = "Extra") 
ShowExchange = input.bool(defval = false, title = "Show Exchange Info in the Table", group = "Extra")

count = switch sym_count
    'Top 10' => 10
    'Top 20' => 20
    'Top 30' => 30
    'Top 40' => 40

symbol1 = input.symbol(defval = "AAPL", group = "Symbols")
symbol2 = input.symbol(defval = "MSFT", group = "Symbols")
symbol3 = input.symbol(defval = "AMZN", group = "Symbols")
symbol4 = input.symbol(defval = "NVDA", group = "Symbols")
symbol5 = input.symbol(defval = "BRK.B", group = "Symbols")
symbol6 = input.symbol(defval = "GOOGL", group = "Symbols")
symbol7 = input.symbol(defval = "TSLA", group = "Symbols")
symbol8 = input.symbol(defval = "GOOG", group = "Symbols")
symbol9 = input.symbol(defval = "UNH", group = "Symbols")
symbol10 = input.symbol(defval = "XOM", group = "Symbols")
symbol11 = input.symbol(defval = "JNJ", group = "Symbols")
symbol12 = input.symbol(defval = "JPM", group = "Symbols")
symbol13 = input.symbol(defval = "META", group = "Symbols")
symbol14 = input.symbol(defval = "V", group = "Symbols")
symbol15 = input.symbol(defval = "PG", group = "Symbols")
symbol16 = input.symbol(defval = "HD", group = "Symbols")
symbol17 = input.symbol(defval = "MA", group = "Symbols")
symbol18 = input.symbol(defval = "CVX", group = "Symbols")
symbol19 = input.symbol(defval = "MRK", group = "Symbols")
symbol20 = input.symbol(defval = "ABBV", group = "Symbols")
symbol21 = input.symbol(defval = "LLY", group = "Symbols")
symbol22 = input.symbol(defval = "PEP", group = "Symbols")
symbol23 = input.symbol(defval = "BAC", group = "Symbols")
symbol24 = input.symbol(defval = "PFE", group = "Symbols") 
symbol25 = input.symbol(defval = "AVGO", group = "Symbols")
symbol26 = input.symbol(defval = "KO", group = "Symbols")
symbol27 = input.symbol(defval = "COST", group = "Symbols")
symbol28 = input.symbol(defval = "TMO", group = "Symbols")
symbol29 = input.symbol(defval = "CSCO", group = "Symbols")
symbol30 = input.symbol(defval = "WMT", group = "Symbols")
symbol31 = input.symbol(defval = "MCD", group = "Symbols")
symbol32 = input.symbol(defval = "DIS", group = "Symbols")
symbol33 = input.symbol(defval = "ABT", group = "Symbols")
symbol34 = input.symbol(defval = "WFC", group = "Symbols")
symbol35 = input.symbol(defval = "ACN", group = "Symbols")
symbol36 = input.symbol(defval = "GS", group = "Symbols")
symbol37 = input.symbol(defval = "CRM", group = "Symbols")
symbol38 = input.symbol(defval = "SNOW", group = "Symbols")
symbol39 = input.symbol(defval = "NFLX", group = "Symbols")
symbol40 = input.symbol(defval = "ADBE", group = "Symbols")

// old list of Top 40
// symbol1 = input.symbol(defval = "AAPL", group = "Symbols")
// symbol2 = input.symbol(defval = "MSFT", group = "Symbols")
// symbol3 = input.symbol(defval = "AMZN", group = "Symbols")
// symbol4 = input.symbol(defval = "GOOG", group = "Symbols")
// symbol5 = input.symbol(defval = "FB", group = "Symbols")
// symbol6 = input.symbol(defval = "TSLA", group = "Symbols")
// symbol7 = input.symbol(defval = "NVDA", group = "Symbols")
// symbol8 = input.symbol(defval = "BRK.B", group = "Symbols")
// symbol9 = input.symbol(defval = "JPM", group = "Symbols")
// symbol10 = input.symbol(defval = "UNH", group = "Symbols")
// symbol11 = input.symbol(defval = "JNJ", group = "Symbols")
// symbol12 = input.symbol(defval = "HD", group = "Symbols")
// symbol13 = input.symbol(defval = "PG", group = "Symbols")
// symbol14 = input.symbol(defval = "V", group = "Symbols")
// symbol15 = input.symbol(defval = "PFE", group = "Symbols")
// symbol16 = input.symbol(defval = "BAC", group = "Symbols")
// symbol17 = input.symbol(defval = "MA", group = "Symbols")
// symbol18 = input.symbol(defval = "ADBE", group = "Symbols")
// symbol19 = input.symbol(defval = "DIS", group = "Symbols")
// symbol20 = input.symbol(defval = "NFLX", group = "Symbols")
// symbol21 = input.symbol(defval = "TMO", group = "Symbols")
// symbol22 = input.symbol(defval = "XOM", group = "Symbols")
// symbol23 = input.symbol(defval = "CRM", group = "Symbols")
// symbol24 = input.symbol(defval = "CSCO", group = "Symbols")
// symbol25 = input.symbol(defval = "COST", group = "Symbols") 
// symbol26 = input.symbol(defval = "ABT", group = "Symbols")
// symbol27 = input.symbol(defval = "PEP", group = "Symbols")
// symbol28 = input.symbol(defval = "ABBV", group = "Symbols")
// symbol29 = input.symbol(defval = "KO", group = "Symbols")
// symbol30 = input.symbol(defval = "PYPL", group = "Symbols")
// symbol31 = input.symbol(defval = "CVX", group = "Symbols")
// symbol32 = input.symbol(defval = "CMCSA", group = "Symbols")
// symbol33 = input.symbol(defval = "LLY", group = "Symbols")
// symbol34 = input.symbol(defval = "QCOM", group = "Symbols")
// symbol35 = input.symbol(defval = "NKE", group = "Symbols")
// symbol36 = input.symbol(defval = "VZ", group = "Symbols")
// symbol37 = input.symbol(defval = "WMT", group = "Symbols")
// symbol38 = input.symbol(defval = "INTC", group = "Symbols")
// symbol39 = input.symbol(defval = "ACN", group = "Symbols")
// symbol40 = input.symbol(defval = "AVGO", group = "Symbols")

HighsLows = array.new_int(count, 0)
getHighLow(length)=>
    highest_ = ta.highest(length)[1]
    lowest_ = ta.lowest(length)[1]
    int ret = 0
    if na(highest_)
        ret := 2
    else
        bool h_ = ((source == "High/Low" ? high : close) > highest_)
        bool l_ = ((source == "High/Low" ? low : close)  < lowest_)
        ret := h_ ? 1 : l_ ? -1 : 0
    ret
    
getHLindexForTheSymbol(symbol, timeFrame, length, index)=>
    highlow = request.security(symbol, timeFrame, getHighLow(length))
    array.set(HighsLows, index, na(highlow) ? 2 : highlow)



getHLindexForTheSymbol(symbol1, timeFrame, length, 0)
getHLindexForTheSymbol(symbol2, timeFrame, length, 1)
getHLindexForTheSymbol(symbol3, timeFrame, length, 2)
getHLindexForTheSymbol(symbol4, timeFrame, length, 3)
getHLindexForTheSymbol(symbol5, timeFrame, length, 4)
getHLindexForTheSymbol(symbol6, timeFrame, length, 5)
getHLindexForTheSymbol(symbol7, timeFrame, length, 6)
getHLindexForTheSymbol(symbol8, timeFrame, length, 7)
getHLindexForTheSymbol(symbol9, timeFrame, length, 8)
getHLindexForTheSymbol(symbol10, timeFrame, length, 9)

if count > 10

    getHLindexForTheSymbol(symbol11, timeFrame, length, 10)
    getHLindexForTheSymbol(symbol12, timeFrame, length, 11)
    getHLindexForTheSymbol(symbol13, timeFrame, length, 12)
    getHLindexForTheSymbol(symbol14, timeFrame, length, 13)
    getHLindexForTheSymbol(symbol15, timeFrame, length, 14)
    getHLindexForTheSymbol(symbol16, timeFrame, length, 15)
    getHLindexForTheSymbol(symbol17, timeFrame, length, 16)
    getHLindexForTheSymbol(symbol18, timeFrame, length, 17)
    getHLindexForTheSymbol(symbol19, timeFrame, length, 18)
    getHLindexForTheSymbol(symbol20, timeFrame, length, 19)

if count > 20

    getHLindexForTheSymbol(symbol21, timeFrame, length, 20)
    getHLindexForTheSymbol(symbol22, timeFrame, length, 21)
    getHLindexForTheSymbol(symbol23, timeFrame, length, 22)
    getHLindexForTheSymbol(symbol24, timeFrame, length, 23)
    getHLindexForTheSymbol(symbol25, timeFrame, length, 24)
    getHLindexForTheSymbol(symbol26, timeFrame, length, 25)
    getHLindexForTheSymbol(symbol27, timeFrame, length, 26)
    getHLindexForTheSymbol(symbol28, timeFrame, length, 27)
    getHLindexForTheSymbol(symbol29, timeFrame, length, 28)
    getHLindexForTheSymbol(symbol30, timeFrame, length, 29)

if count > 30

    getHLindexForTheSymbol(symbol31, timeFrame, length, 30)
    getHLindexForTheSymbol(symbol32, timeFrame, length, 31)
    getHLindexForTheSymbol(symbol33, timeFrame, length, 32)
    getHLindexForTheSymbol(symbol34, timeFrame, length, 33)
    getHLindexForTheSymbol(symbol35, timeFrame, length, 34)
    getHLindexForTheSymbol(symbol36, timeFrame, length, 35)
    getHLindexForTheSymbol(symbol37, timeFrame, length, 36)
    getHLindexForTheSymbol(symbol38, timeFrame, length, 37)
    getHLindexForTheSymbol(symbol39, timeFrame, length, 38)
    getHLindexForTheSymbol(symbol40, timeFrame, length, 39)

highs = 0
lows = 0
total = 0
for x = 0 to count -1
    highs += (array.get(HighsLows, x) == 1 ? 1 : 0)
    lows += (array.get(HighsLows, x) == -1 ? 1 : 0)
    total += (array.get(HighsLows, x) == 1 or array.get(HighsLows, x) == -1 ? 1 : 0)

// calculate & show Record High Percent / High Low Index / EMA
var float RecordHighPercent = 50
HighLowHist = (highs - lows)
HLHist_ma = ta.sma(HighLowHist,HistoLen)
RecordHighPercent := total == 0 ? RecordHighPercent : 100 * highs / total
HighLowIndex = ta.sma(RecordHighPercent, smoothLength)
HLIema = ta.ema(HighLowIndex, emaLength)
hline0 = hline(-count, linestyle = hline.style_dotted)
hline50 = hline(0, linestyle = hline.style_dotted)
hline100 = hline(count, linestyle = hline.style_dotted)
fill(hline0, hline50, color = color.rgb(255, 0, 0, 90))
fill(hline50, hline100, color = color.rgb(0, 255, 0, 90))

plot(showHisto ? HighLowHist : na, style = plot.style_columns, title ="Sentiment Histo", color = HighLowHist > 0 ? color.new(color.green,0) : color.new(color.red,0))
plot(showHistoMA ? HLHist_ma : na, title = "Sentiment Histogram MA", color = color.yellow)
plot(showRecordHighPercent ? RecordHighPercent*(count/5)/10 - count : na, color = showRecordHighPercent ? #9598a1 : na, title = "Record High %")
plot(showEma ? HLIema*(count/5)/10 - count : na, color = showEma ? color.red : na, title = "EMA line")
plot(HighLowIndex*(count/5)/10 - count, color = color.blue, linewidth = 2, title = "High Low Index Line")

bull_x = ta.crossover(HighLowIndex,HLIema)
bear_x = ta.crossunder(HighLowIndex,HLIema)

fill(hline0, hline100, color = bull_x ? color.new(color.green,80) : bear_x ? color.new(color.red,80) : na)

peak = HighLowIndex[2] <= HighLowIndex[1] and HighLowIndex < HighLowIndex[1] and HighLowIndex > 5
dip = HighLowIndex[2] >= HighLowIndex[1] and HighLowIndex > HighLowIndex[1] and HighLowIndex < 95

peak_ma = HLHist_ma[2] <= HLHist_ma[1] and HLHist_ma < HLHist_ma[1]
dip_ma = HLHist_ma[2] >= HLHist_ma[1] and HLHist_ma > HLHist_ma[1]

plotshape(showHLI_PD ? dip : na, style=shape.triangleup, location=location.bottom, offset=-1, color=color.blue)
plotshape(showHLI_PD ? peak : na, style=shape.triangledown, location=location.top, offset=-1, color=color.blue)

plotshape(showHist_PD ? dip_ma : na, style=shape.triangleup, location=location.bottom, offset=-1, color=color.yellow)
plotshape(showHist_PD ? peak_ma : na, style=shape.triangledown, location=location.top, offset=-1, color=color.yellow)

removeexchange(string [] symbols)=>
    for x = 0 to array.size(symbols) - 1
        array.set(symbols, x, array.get(str.split(array.get(symbols, x), ':'), 1))

// Keep symbol names in an array
var symbols = array.from(
 symbol1, symbol2, symbol3, symbol4, symbol5, symbol6, symbol7, symbol8, symbol9, symbol10, 
 symbol11, symbol12, symbol13, symbol14, symbol15, symbol16, symbol17, symbol18, symbol19, symbol20, 
 symbol21, symbol22, symbol23, symbol24, symbol25, symbol26, symbol27, symbol28, symbol29, symbol30, 
 symbol31, symbol32, symbol33, symbol34, symbol35, symbol36, symbol37, symbol38, symbol39, symbol40)

// remove Exchange from the symbols
if barstate.isfirst and not ShowExchange 
    removeexchange(symbols)   
 
 // show the table  
if barstate.islast and showTable
    var Table = table.new(position=position.bottom_left, columns=5, rows= count/5, frame_color=color.gray, frame_width=1, border_width=1, border_color=color.black)
    index = 0
    for c = 0 to 4
        for r = 0 to count/5 - 1
            bcolor = array.get(HighsLows, index) == 1 ? color.lime : array.get(HighsLows, index) == -1 ? color.red : array.get(HighsLows, index) == 2 ? color.black : color.gray
            tcolor = array.get(HighsLows, index) == 1 ? color.black : array.get(HighsLows, index) == -1 ? color.white : array.get(HighsLows, index) == 2 ? color.gray : color.black
            table.cell(table_id=Table, column=c, row=r, text=array.get(symbols, index), bgcolor=bcolor, text_color = tcolor, text_size=textsize)
            index += 1
            