Lowry Up/Down % Volume
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// referenced script: Up Volume vs Down Volume by ChartingCycles
// For usage of Up/Down Vol to identify market bottom, see 
// https://recessionalert.com/wp-content/uploads/2021/08/identifying-bear-market-bottoms-and-new-bull-markets.pdf 
// by Lowry’s Reports Inc

//@version=5
indicator(title = "Lowry Up/Down % Volume", shorttitle="Up/Down % Vol", precision=2)
sym(s) => request.security(s, "D", close)

// Calculation
// Get total volume or the sum of Upside Volume and Downside Volume
// Divide Upside Volume by total volume to get Upside % Volume
up_percentage = (sym("USI:UPVOL.NY")) / (sym("USI:TVOL.NY"))
down_percentage = (sym("USI:DNVOL.NY")) / (sym("USI:TVOL.NY"))
is_u80 = up_percentage >= .80 and up_percentage < .90
is_d80 = down_percentage >= .80 and down_percentage < .90
is_u90 = up_percentage >= .90
is_d90 = down_percentage >= .90
up_bar_color = is_u90 ? #7CFC00 : is_u80 ? #7FFFD4 : up_percentage <= .10 ? color.olive : color.olive
down_bar_color = is_d90 ? #FF0000 : is_d80 ? color.fuchsia : down_percentage <= .10 ? color.maroon : color.maroon
plot(up_percentage, "Up Volume Percentage", up_bar_color, style=plot.style_columns)
plot(-down_percentage, "Down Volume Percentage", down_bar_color, style=plot.style_columns)

plotshape((is_u80), title="is 80 Up-day?", style=shape.diamond, size=size.tiny,text="8",textcolor=up_bar_color,color=up_bar_color, location=location.bottom)
plotshape((is_d80), title="is 80 Down-day?", style=shape.diamond, size=size.tiny,text="8",textcolor=down_bar_color,color=down_bar_color, location=location.top)
plotshape((is_u90), title="is 90 Up-day?", style=shape.triangleup, size=size.tiny,text="9",textcolor=up_bar_color,color=up_bar_color, location=location.bottom)
plotshape((is_d90), title="is 90 Down-day?", style=shape.triangledown, size=size.tiny,text="9",textcolor=down_bar_color,color=down_bar_color, location=location.top)

var string GP2 = 'Display'
show_header = timeframe.isdaily ? input.bool(true, title='Show Table Header', inline='10', group=GP2) : false
// show_header = true
string i_tableYpos = input.string('bottom', 'Panel Position', inline='11', options=['top', 'middle', 'bottom'], group=GP2)
string i_tableXpos = input.string('right', '', inline='11', options=['left', 'center', 'right'], group=GP2)
string textsize = input.string('normal', 'Text Size', inline='12', options=['small', 'normal', 'large'], group=GP2)
var table dtrDisplay = table.new(i_tableYpos + '_' + i_tableXpos, 3, 2, frame_width=1, frame_color=color.black, border_width=1, border_color=color.black)
first_time = 0
first_time := bar_index == 0 ? time : first_time[1]
if barstate.islast
    // We only populate the table on the last bar.
    if show_header == true
        table.cell(dtrDisplay, 1, 0, 'Up Volume', bgcolor=color.black, text_size=textsize, text_color=color.white)
        table.cell(dtrDisplay, 1, 1, str.tostring(math.round(up_percentage,3)*100) + '%', bgcolor=color.black, text_size=textsize, text_color=color.white)
        table.cell(dtrDisplay, 2, 0, 'Down Volume', bgcolor=color.black, text_size=textsize, text_color=color.white)
        table.cell(dtrDisplay, 2, 1, str.tostring(math.round(down_percentage,3)*100) + '%', bgcolor=color.black, text_size=textsize, text_color=color.white)