Stock Data Table
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © valpatrad

//@version=5
indicator('Stock Data Table', 'Stock Data', true)



////////////////////////////
//                        //
//          Menu          //
//                        //
////////////////////////////

	//Company
show_sec = input.bool(true, 'Sector', group='Company')
show_ind = input.bool(true, 'Industry', group='Company')

	//Correlation
show_indexes = input.bool(true, 'Index / Index', group='Correlation')
show_ticker = input.bool(true, 'Index / Ticker', group='Correlation')
show_len = input.bool(true, 'Length͏͏     ', group='Correlation', inline='Length')
corr_len = input.int(9, '', 1, group='Correlation', inline='Length')
prec = input.int(3, 'Precision          ', 1, 4, group='Correlation', inline='Precision')
corr_col = input.string('Green → Red', 'Color gradient  ', ['Red → Green', 'Green → Red'], group='Correlation', inline='Color', tooltip='Color gradient from 0 to 1 and from 0 to -1')

	//Shares
show_TSO = input.bool(true, 'Total Shares Outstanding', group='Shares')
show_MCap = input.bool(true, 'Market Capitalization', group='Shares', tooltip='Mega Cap ..... $200 billion or more\nLarge Cap ..... Between $10 billion and $200 billion\nMid Cap ....... Between $2 billion and $10 billion\nSmall Cap ..... Between $300 million and $2 billion\nMicro Cap ..... Less than $300 million')
show_FSO = input.bool(true, 'Float', group='Shares')
show_FFMCap = input.bool(true, 'Free-Float Market Cap', group='Shares', inline='FSO')
always_FFMCap = input.bool(false, 'Always', group='Shares', inline='FSO', tooltip='Leave the "Always" option unchecked to show the Float or the Free-Float Market Cap only when the Free-Float Market Cap is different from the Market Cap.')
penny = input.float(5, 'Penny Stocks   <', 0, step=0.01, group='Shares', inline='Penny', tooltip='Stocks below this value will be considered Penny Stocks. Set the number to zero if you are not interested in this classification.')
SHR_th = input.bool(true, '', group='Shares', inline='Shares Threshold')
shares_min = input.int(30000000, 'Thresholds ', group='Shares', inline='Shares Threshold')
shares_max = input.int(600000000, '— ', group='Shares', inline='Shares Threshold')
shares_col = input.color(color.new(#FF5252, 35), '                        ', group='Shares', inline='Colors')
shares_min_col = input.color(color.new(#FFEB3B, 35), '', group='Shares', inline='Colors')
shares_max_col = input.color(color.new(#4CAF50, 35), '', group='Shares', inline='Colors', tooltip='From the left:\n1) Color before the first threshold\n2) Color from the first threshold\n3) Color from the second threshold')

	//ATR
ATR_len = input.int(14, 'Length              ', 1, group='Average True Range', inline='ATR Length')
ATR_tf = input.timeframe('', title='Time Frame      ', group='Average True Range', inline='ATR TF')
show_ATR = input.bool(true, 'ATR           ', group='Average True Range')
ATR_th = input.bool(false, '', group='Average True Range', inline='ATR Threshold')
ATR_min = input.float(0.5, 'Thresholds ', 0.01, step=0.5, group='Average True Range', inline='ATR Threshold')
ATR_max = input.float(15, '— ', 0.01, step=0.5, group='Average True Range', inline='ATR Threshold')
ATR_col = input.color(color.new(#FF5252, 35), '                        ', group='Average True Range', inline='ATR Colors')
ATR_min_col = input.color(color.new(#4CAF50, 35), '', group='Average True Range', inline='ATR Colors')
ATR_max_col = input.color(color.new(#FFEB3B, 35), '', group='Average True Range', inline='ATR Colors')
show_ATRP = input.bool(true, 'Normalized ATR', group='Average True Range')
ATRP_th = input.bool(false, '', group='Average True Range', inline='ATRP Threshold')
ATRP_min = input.float(1.75, 'Thresholds ', 0.01, step=0.5, group='Average True Range', inline='ATRP Threshold')
ATRP_max = input.float(5, '— ', 0.01, step=0.5, group='Average True Range', inline='ATRP Threshold')
ATRP_col = input.color(color.new(#FF5252, 35), '                        ', group='Average True Range', inline='ATRP Colors')
ATRP_min_col = input.color(color.new(#4CAF50, 35), '', group='Average True Range', inline='ATRP Colors')
ATRP_max_col = input.color(color.new(#FFEB3B, 35), '', group='Average True Range', inline='ATRP Colors')

	//Average Daily Volume
show_ADV = input.bool(true, 'Average Daily Volume', group='Average Daily Volume', tooltip='This row will be visible on daily charts only.')
ADV_len = input.int(20, 'Length              ', 1, group='Average Daily Volume', inline='ADV Length')
ADV_th = input.bool(true, '', group='Average Daily Volume', inline='ADV Threshold')
ADV_min = input.int(2000000, 'Thresholds ', group='Average Daily Volume', inline='ADV Threshold')
ADV_max = input.int(10000000, '— ', group='Average Daily Volume', inline='ADV Threshold')
ADV_col = input.color(color.new(#FF5252, 35), '                        ', group='Average Daily Volume', inline='Colors')
ADV_min_col = input.color(color.new(#FFEB3B, 35), '', group='Average Daily Volume', inline='Colors')
ADV_max_col = input.color(color.new(#4CAF50, 35), '', group='Average Daily Volume', inline='Colors')

	//Daily Change %
show_DChg = input.bool(true, 'Daily Change %', group='Daily Change %', tooltip='This row will be visible on daily charts only.')

	//Extended Session Change %
show_ExtChg = input.bool(true, 'Extended Session Change %', group='Extended Session Change %', inline='ExtChg')
show_ExtChg_al = input.bool(true, 'Always', group='Extended Session Change %', inline='ExtChg', tooltip='This row will be visible on intraday charts only. Leave the "Always" option checked to show the value even outside the selected time range.')
ExtChg_time_in = input.session('1600-0930', 'Ext. Session      ', group='Extended Session Change %', inline='Market Time')

	//Pre-Market Volume
show_PMVol = input.bool(true, 'Pre-Market Volume', group='Pre-Market Volume', inline='PM Vol')
show_PMVol_al = input.bool(true, 'Always', group='Pre-Market Volume', inline='PM Vol', tooltip='This row will be visible on intraday charts only. Leave the "Always" option checked to show the value even outside the selected time range.')
PM_time_in = input.session('0400-0930', 'Pre-Market        ', group='Pre-Market Volume', inline='PM Time')
PMVol_th = input.bool(true, '', group='Pre-Market Volume', inline='PM Vol Threshold')
PMVol_min = input.int(50000, 'Thresholds ', group='Pre-Market Volume', inline='PM Vol Threshold')
PMVol_max = input.int(1500000, '— ', group='Pre-Market Volume', inline='PM Vol Threshold')
PMVol_col = input.color(color.new(#FF5252, 35), '                        ', group='Pre-Market Volume', inline='Colors')
PMVol_min_col = input.color(color.new(#4CAF50, 35), '', group='Pre-Market Volume', inline='Colors')
PMVol_max_col = input.color(color.new(#FFEB3B, 35), '', group='Pre-Market Volume', inline='Colors')

	//Table
title_TSO = input.string('SHR', 'Total Shares Outstanding', group='Table', tooltip='Clear the fields to remove the corresponding cell.')
title_FSO = input.string('Float', 'Float', group='Table')
title_ATR = input.string('ATR', 'Average True Range', group='Table')
title_ADV = input.string('ADV', 'Average Daily Volume', group='Table')
title_DChg = input.string('DLY Chg', 'Daily Change %', group='Table')
title_ExtChg = input.string('Ext Chg', 'Extended Session Change %', group='Table')
title_PMVol = input.string('PM Vol', 'Pre-Market Volume', group='Table')
brackets = input.string('Round', 'Brackets', ['Square', 'Round'], group='Table')
invert_corr = input.bool(false, 'Invert Correlation rows', group='Table')
invert_shares = input.bool(false, 'Invert Shares rows', group='Table')
invert_daily = input.bool(false, 'Invert Daily rows', group='Table')
invert_intraday = input.bool(false, 'Invert Intraday rows', group='Table')
sec_col = input.color(#B2B5BE, 'Sector                    ', group='Table', inline='Line 0')
ind_col = input.color(#B2B5BE, 'Industry                      ', group='Table', inline='Line 0')
SPY_col = input.color(#C4162E, 'SPY                        ', group='Table', inline='Line 0')
QQQ_col = input.color(#2750FF, ' QQQ                           ', group='Table', inline='Line 0')
len_col = input.color(#DDDDDD, 'Correlation Length  ', group='Table', inline='Line 1')
ticker_col = input.color(#00FFFF, ' Ticker                        ', group='Table', inline='Line 1')
MCap_col = input.color(#008EFF, 'Market Cap             ', group='Table', inline='Line 2')
FFMCap_col = input.color(#F423FC, ' Free-Float Market Cap', group='Table', inline='Line 2')
no_th_col = input.color(#DDDDDD, 'No-Threshold cells ', group='Table', inline='Line 3')
title_col = input.color(#9598A1, 'Titles                      ', group='Table', inline='Line 4')
cust_col = input.bool(false, 'Monochromatic Text     ', group='Table', inline='Text Color')
in_font = input.string('Default', 'Text Font' , ['Default', 'Monospace'], group='Table')
font = switch in_font
	'Default' => font.family_default
	'Monospace' => font.family_monospace
monotxt_col = input.color(#B2B5BE, '', group='Table', inline='Text Color')
txt_transp = input.int(0, 'Text Transparency', 0, 100, group='Table')
transp = input.int(70, 'BKGD Transparency', 0, 100, group='Table')
b_width = input.int(1, 'Border Width', 0, group='Table')
in_tab_size = input.string('Normal', 'Size', ['Auto', 'Tiny', 'Small', 'Normal', 'Large', 'Huge'], group='Table')
tab_size = switch in_tab_size
	'Auto'   => size.auto
	'Tiny'   => size.tiny
	'Small'  => size.small
	'Normal' => size.normal
	'Large'  => size.large
	'Huge'   => size.huge
in_tab_pos = input.string('Top Right', 'Position', ['Top Left', 'Top Center', 'Top Right', 'Middle Left', 'Middle Center', 'Middle Right', 'Bottom Left', 'Bottom Center', 'Bottom Right'], group='Table')
tab_pos = switch in_tab_pos
	'Top Left' => position.top_left
	'Top Center' => position.top_center
	'Top Right' => position.top_right
	'Middle Left' => position.middle_left
	'Middle Center' => position.middle_center
	'Middle Right' => position.middle_right
	'Bottom Left'  => position.bottom_left
	'Bottom Center' => position.bottom_center
	'Bottom Right' => position.bottom_right



/////////////////////////////
//                         //
//        Variables        //
//                         //
/////////////////////////////

	//Correlation
ID = syminfo.ticker
SPY = ticker.new('AMEX', 'SPY', session.extended)
QQQ = ticker.new('NASDAQ', 'QQQ', session.extended)
SPY_c = request.security(SPY, '', close)
QQQ_c = request.security(QQQ, '', close)
corr_index = ta.correlation(SPY_c, QQQ_c, corr_len)
corr_ticker = ta.correlation(syminfo.prefix == 'NASDAQ' ? QQQ_c : SPY_c, close, corr_len)
bg_col(c) =>
	if corr_col == 'Red → Green'
		if c <= -0.7
			color.from_gradient(c, -1, -0.7, #00FF00, #FFFF00)
		else if c > -0.7 and c < -0.35
			color.from_gradient(c, -0.7, -0.35, #FFFF00, #FFA500)
		else if c > -0.35 and c < 0
			color.from_gradient(c, -0.35, 0, #FFA500, #FF0000)
		else if c == 0
			#FF0000
		else if c > 0 and c < 0.35
			color.from_gradient(c, 0, 0.35, #FF0000, #FFA500)
		else if c > 0.35 and c < 0.7
			color.from_gradient(c, 0.35, 0.7, #FFA500, #FFFF00)
		else if c >= 0.7
			color.from_gradient(c, 0.7, 1, #FFFF00, #00FF00)
	else
		if c <= -0.7
			color.from_gradient(c, -1, -0.7, #FF0000, #FFA500)
		else if c > -0.7 and c < -0.35
			color.from_gradient(c, -0.7, -0.35, #FFA500, #FFFF00)
		else if c > -0.35 and c < 0
			color.from_gradient(c, -0.35, 0, #FFFF00, #00FF00)
		else if c == 0
			#00FF00
		else if c > 0 and c < 0.35
			color.from_gradient(c, 0, 0.35, #00FF00, #FFFF00)
		else if c > 0.35 and c < 0.7
			color.from_gradient(c, 0.35, 0.7, #FFFF00, #FFA500)
		else if c >= 0.7
			color.from_gradient(c, 0.7, 1, #FFA500, #FF0000)

	//Shares
TSO = syminfo.shares_outstanding_total
FSO = syminfo.shares_outstanding_float
MCap = TSO * close
FFMCap = FSO * close
cap(c) =>
	if c >= 200000000000
		'Mega Cap'
	else if c >= 10000000000 and c < 200000000000
		'Large Cap'
	else if c >= 2000000000 and c < 10000000000 
		'Mid Cap'
	else if c >= 300000000 and c < 2000000000
		'Small Cap'
	else if c < 300000000 and close >= penny
		'Micro Cap'
	else if c < 300000000 and close < penny
		'Penny Stock'

	//ATR
ATR = request.security(syminfo.tickerid, ATR_tf, ta.atr(ATR_len))
ATRP = (ATR/close)*100

	//Average Daily Volume
D_vol = request.security(syminfo.tickerid, 'D', volume)
ADV = ta.sma(D_vol, ADV_len)
D_vol_Y = request.security(syminfo.tickerid, 'D', volume[1])
ADV_Y  = ta.sma(D_vol_Y, ADV_len)

	//Daily Change %
D_close = request.security(syminfo.tickerid, 'D', close)
DChg = (D_close-D_close[1])/D_close[1]*100

	//Extended Session Change %
Ext_time = time(timeframe.period, ExtChg_time_in)
var float close_1 = na
if na(Ext_time)[1] and not na(Ext_time)
	close_1 := close[1]
var float Ext_Chg = na
if not na(Ext_time)
	Ext_Chg := (close-close_1)/close_1*100

	//Pre-Market Volume
PM_time = time(timeframe.period, PM_time_in)
var float PMVol = na
if na(PM_time)[1] and not na(PM_time)
	PMVol := 0
if not na(PM_time)
	PMVol := PMVol + volume

	//Table
tab = table.new(tab_pos, 4, 11, border_width=b_width)
int clm = 0
int row = 0
float corr = 0.
_info(row, txt, col) =>
	clmn = show_ticker or show_indexes ? 0 : 1
	table.cell(tab, clmn, row, txt, text_color=color.new(col, txt_transp), text_size=tab_size, bgcolor=color.new(col, transp), text_font_family=font)
	table.merge_cells(tab, clmn, row, 3, row)
_index(clm, row, txt, col1, col2) =>
	table.cell(tab, clm, row, txt, text_color=cust_col ? color.new(monotxt_col, txt_transp) : syminfo.prefix == 'NASDAQ' ? color.new(col1, txt_transp) : color.new(col2, txt_transp), text_size=tab_size, bgcolor=syminfo.prefix == 'NASDAQ' ? color.new(col1, transp) : color.new(col2, transp), text_font_family=font)
_corr(row, corr) =>
	table.cell(tab, 3, row, str.tostring(corr, prec == 4 ? '#.####' : prec == 3 ? '#.###' : prec == 2 ? '#.##' : '#.#'), text_color=cust_col ? color.new(monotxt_col, txt_transp) : color.new(bg_col(corr), txt_transp), text_size=tab_size, bgcolor=color.new(bg_col(corr), transp), text_font_family=font)
_title(txt, clm, row) =>
	if txt != ''
		table.cell(tab, clm, row, txt == title_ATR and brackets == 'Square' ? txt + '[' + str.tostring(ATR_len) + ']' : txt == title_ATR ? txt + '(' + str.tostring(ATR_len) + ')' : txt == title_ADV and brackets == 'Square' ? txt + '[' + str.tostring(ADV_len) + ']' : txt == title_ADV ? txt + '(' + str.tostring(ADV_len) + ')' : txt, text_color=cust_col ? color.new(monotxt_col, txt_transp) : color.new(title_col, txt_transp), text_size=tab_size, bgcolor=color.new(title_col, transp), text_font_family=font)
_val(clm, row, txt, col) =>
	table.cell(tab, clm, row, txt, text_color=cust_col ? color.new(monotxt_col, txt_transp) : color.new(col, txt_transp), text_size=tab_size, bgcolor=color.new(col, transp), text_font_family=font)
th_col(th, v, min, max, c, c_min, c_max) =>
	if th
		v < min ? c : v >= min and v < max ? c_min : c_max
	else
		no_th_col
chg_col(c) =>
	c > 0 ? color.green : c < 0 ? color.red : color.white



////////////////////////////
//                        //
//          Plot          //
//                        //
////////////////////////////

	//Company
if show_sec and not na(syminfo.sector) and not (syminfo.type == 'fund')
	row := 0
	_info(row, str.tostring(syminfo.sector), sec_col)
if show_ind and not na(syminfo.industry)
	row := 1
	_info(row, str.tostring(syminfo.industry), ind_col)

	//Correlation
if (show_ticker and ID != 'SPY' and ID != 'QQQ' and not na(corr_ticker)) or (show_indexes and not na(corr_index))
	row := 2
	_index(show_len ? 0 : 1, row, syminfo.prefix == 'NASDAQ' ? 'QQQ' : 'SPY', QQQ_col, SPY_col)
	if show_len
		_val(1, row, show_len and brackets == 'Square' ? 'r[' + str.tostring(corr_len) + ']' : show_len and brackets == 'Round' ? 'r(' + str.tostring(corr_len) + ')' : na, len_col)
if show_ticker and show_indexes and ID != 'SPY' and ID != 'QQQ' and not na(corr_ticker) and not na(corr_index)
	table.merge_cells(tab, show_len ? 0 : 1, row, show_len ? 0 : 1, row+1)
	if show_len
		table.merge_cells(tab, 1, row, 1, row+1)
if show_indexes and not na(corr_index)
	row := show_ticker and invert_corr ? 3 : 2
	_index(2, row, syminfo.prefix == 'NASDAQ' ? 'SPY' : 'QQQ', SPY_col, QQQ_col)
	_corr(row, corr_index)
if show_ticker and ID != 'SPY' and ID != 'QQQ' and not na(corr_ticker)
	row := not show_indexes or invert_corr ? 2 : 3
	_val(2, row, str.tostring(syminfo.ticker), ticker_col)
	_corr(row, corr_ticker)

	//Shares
if not na(TSO)
	if show_TSO or show_MCap
		row := invert_shares ? 5 : 4
		_title(title_TSO, 1, row)
	if show_TSO
		_val(2, row, str.tostring(TSO, format.volume), th_col(SHR_th, TSO, shares_min, shares_max, shares_col, shares_min_col, shares_max_col))
		if 	not show_MCap
			table.merge_cells(tab, 2, row, 3, row)
	if show_MCap
		clm := not show_TSO ? 2 : 3
		table.merge_cells(tab, clm, row, 3, row)
		_val(clm, row, str.tostring(cap(MCap)), MCap_col)

if not na(FSO)
	if always_FFMCap or cap(MCap) != cap(FFMCap)
		if show_FSO or show_FFMCap
			row := invert_shares ? 4 : 5
			_title(title_FSO, 1, row)
		if show_FSO
			_val(2, row, str.tostring(FSO, format.volume), th_col(SHR_th, FSO, shares_min, shares_max, shares_col, shares_min_col, shares_max_col))
			if not show_FFMCap
				table.merge_cells(tab, 2, row, 3, row)
		if show_FFMCap
			clm := not show_FSO ? 2 : 3
			table.merge_cells(tab, clm, row, 3, row)
			_val(clm, row, str.tostring(cap(FFMCap)), FFMCap_col)

	//ATR
if not na(ATR)
	if show_ATR or show_ATRP
		row := 6
		_title(title_ATR, show_ATR and show_ATRP ? 1 : 2, row)
	if show_ATR
		_val(show_ATRP ? 2 : 3, row, str.tostring(ATR, '#.##'), th_col(ATR_th, ATR, ATR_min, ATR_max, ATR_col, ATR_min_col, ATR_max_col))
	if show_ATRP
		_val(3, row, str.tostring(ATRP, format.percent), th_col(ATRP_th, ATRP, ATRP_min, ATRP_max, ATRP_col, ATRP_min_col, ATRP_max_col))

	//Average Daily Volume
if timeframe.isdaily
	if show_ADV and not na(ADV)
		row := invert_daily ? 8 : 7
		_title(title_ADV, 2, row)
		_val(3, row, str.tostring(ADV, format.volume), th_col(ADV_th, ADV, ADV_min, ADV_max, ADV_col, ADV_min_col, ADV_max_col))

	//Daily Change %
	if show_DChg and not na(DChg)
		row := invert_daily ? 7 : 8
		_title(title_DChg, 2, row)
		_val(3, row, DChg > 0 ? '+' + str.tostring(DChg, format.percent) : str.tostring(DChg, format.percent), chg_col(DChg))

	//Extended Session Change %
if timeframe.isintraday
	if show_ExtChg and not na(Ext_Chg)
		if not na(Ext_time) or (show_ExtChg_al and na(Ext_time))
			row := invert_intraday ? 10 : 9
			_title(title_ExtChg, 2, row)
			_val(3, row, Ext_Chg > 0 ? '+' + str.tostring(Ext_Chg, format.percent) : str.tostring(Ext_Chg, format.percent), chg_col(Ext_Chg))

	//Pre-Market Volume
	if show_PMVol and not na(PMVol)
		if not na(PM_time) or (show_PMVol_al and na(PM_time))
			row := invert_intraday ? 9 : 10
			_title(title_PMVol, 2, row)
			_val(3, row, str.tostring(PMVol, format.volume), th_col(PMVol_th, PMVol, PMVol_min, PMVol_max, PMVol_col, PMVol_min_col, PMVol_max_col))