Time and Sales
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © S_Codes

//@version=5
indicator("Time and Sales | Tape | Filters | Tick Speed | Visualization", shorttitle="TNS [S-Codes]", overlay = true)

a_allLabels = label.all
if array.size(a_allLabels) > 0
	for i = 0 to array.size(a_allLabels)-1
		label.delete(array.get(a_allLabels, i))
a_allLines = line.all
if array.size(a_allLines) > 0
	for i = 0 to array.size(a_allLines)-1
		line.delete(array.get(a_allLines, i))


var t1 = input.string(defval="middle", options=["top", "middle", "bottom"], inline="tp", title="Table Position")
var t2 = input.string(defval="right", options=["left", "center", "right"], inline="tp", title="")

var r = input.int(defval=10, minval=1, maxval=100, title="Number of past ticks to show", inline="rr")
var showTotalRowSpeed = input.bool(defval=true, title="Show speed", inline="rr", tooltip="This will show the speed at which the past selected ticks are appearing")

var showSpeedViz = input.bool(defval=true, title="Visualize Speed", inline="r")
var t1s = input.string(defval="top", options=["top", "middle", "bottom"], inline="r", title="")
var t2s = input.string(defval="right", options=["left", "center", "right"], inline="r", title="")

var showTickCount = input.bool(defval=true, title="Show total ticks")
var showhv = input.bool(defval=true, title="Show Highest Volume", inline="hv")
var showBarMark = input.bool(defval=true, title="highlight on bar with", inline="hv")
var barMarkChar = input.string(defval="↡", inline="hv", title="")
var barMarkCol = input.color(defval=color.gray, inline="hv", title="")
var colAccTabHV = input.bool(defval=true, title="Color this highlight as it appears in the table", tooltip="This ignores the color chosen above")
var showHvLineLabel = input.bool(defval=true, title="Show label and line on Highest Vol Price")

var showPerTickSpeed = input.bool(defval=true, title="Show per tick speed")
var showSpread = input.bool(defval=true, title="Show Spread Column")

var tab = table.new(position=t1+"_"+t2, columns=5, rows=500, bgcolor=color.new(#2962ff,95), frame_color=#2962ff, frame_width=1) //, border_color=#0044ff, border_width=1
var tab_ = table.new(position=t1s+"_"+t2s, columns=120, rows=1, bgcolor=color.new(color.white,95), frame_color=#2962ff, frame_width=1) //, border_color=#0044ff, border_width=1

var r1s = input.bool(defval=true, title="Show Filters Seperately", group="Filters", inline="r1")
var r1 = input.int(defval=8, minval=1, maxval=100, title="", group="Filters", inline="r1")
var f1 = input.bool(defval=true, title="", group="Filters", inline="f1")
var gT = input.float(defval=0.0, title="Volume greater than equal to", group="Filters", inline="f1")
var f2 = input.bool(defval=false, title="", group="Filters", inline="f2")
var lT = input.float(defval=0.0, title="Volume less than equal to", group="Filters", inline="f2")

var showBarMarkF = input.bool(defval=true, title="highlight on bar with", inline="hvf")
var barMarkCharF = input.string(defval="⇧", inline="hvf", title="")
var barMarkColF = input.color(defval=color.gray, inline="hvf", title="")
var colAccTabF = input.bool(defval=true, title="Color this highlight as it appears in the table", tooltip="This ignores the color chosen above")
var showFilLineLabel = input.bool(defval=true, title="Show label and line on Filtered prices")

if barstate.islast
    table.cell(tab, 1, 1, "LTP" , bgcolor=#2962ff, text_color=color.white)
    table.cell(tab, 2, 1, "Vol" , bgcolor=#2962ff, text_color=color.white)
    table.cell(tab, 0, 1, "Time", bgcolor=#2962ff, text_color=color.white)
    if showSpread
        table.cell(tab, 3, 1, "Spread" , bgcolor=#2962ff, text_color=color.white)

//highest volume
varip hv = 0.0
varip hc = 0.0
varip ht = timenow
varip hs = 0.0
label hl = na
varip tl = timenow
line lihv = na
label lahv = na

//full data
varip tnsdata_vol = array.new_float(r+2, 0)
varip tnsdata_cls = array.new_float(r+2, 0)
varip tnsdata_tmn = array.new_int(r+2, timenow)
varip tnsdata_spr = array.new_float(r+2, 0.0)
var tnsdata_col = array.new_color(r+2, color.gray)

//for seperate
varip tnsdata_vol1 = array.new_float(r1, 0)
varip tnsdata_cls1 = array.new_float(r1, 0)
varip tnsdata_tmn1 = array.new_int(r1, timenow)
varip tnsdata_spr1 = array.new_float(r1, 0.0)
varip hi = array.new_int(r1, 0)
varip hvc = 0
//1 = red
//2 = green
//0 = gray
line filline = na
label fillabel = na

var spl = array.new_label(r1, na)
varip spv = array.new_float(r1, 0)
varip spt = array.new_int(r1, timenow)

//logic
varip tickCount = 0
varip lastvol = volume 
var v = 0.0
varip lastclose = close
var c = 0.0

var sign = ""

varip total_time = 0
varip total_time_1 = 0

if lastvol != volume and barstate.isrealtime
    tickCount := tickCount + 1
    v := (volume - lastvol) < 0 ? volume : (volume - lastvol)
    if v > hv and tickCount > 1
        hv := v
        hc := close
        ht := timenow
        tl := time
        hs := math.abs(lastclose - close)
        if lastclose > close
            hvc := 1
        else if lastclose < close
            hvc := 2
        else
            hvc := 0
    c := close
    
    total_time := array.get(tnsdata_tmn, array.size(tnsdata_tmn)-1) - array.get(tnsdata_tmn, 2)
    total_time_1 := array.get(tnsdata_tmn, array.size(tnsdata_tmn)-1) - array.get(tnsdata_tmn, array.size(tnsdata_tmn)-2)
    
    if tickCount > 1
        if r1s
            if f1 and not f2
                if v >= gT
                    sign := sign + ">="
                    array.push(tnsdata_vol1, v)
                    array.shift(tnsdata_vol1)
                    array.push(tnsdata_cls1, c)
                    array.shift(tnsdata_cls1)
                    array.push(tnsdata_tmn1, timenow)
                    array.shift(tnsdata_tmn1)
                    array.push(spt, time)
                    array.shift(spt)
                    array.push(spv, v)
                    array.shift(spv)
                    array.push(tnsdata_spr1, math.abs(lastclose-close))
                    array.shift(tnsdata_spr1)
                    if lastclose > close
                        array.push(hi, 1)
                        array.shift(hi)
                    else if lastclose < close
                        array.push(hi, 2)
                        array.shift(hi)
                    else
                        array.push(hi, 0)
                        array.shift(hi)
            else if f2 and not f1
                if v <= lT
                    sign := sign + "<="
                    array.push(tnsdata_vol1, v)
                    array.shift(tnsdata_vol1)
                    array.push(tnsdata_cls1, c)
                    array.shift(tnsdata_cls1)
                    array.push(tnsdata_tmn1, timenow)
                    array.shift(tnsdata_tmn1)
                    array.push(spt, time)
                    array.shift(spt)
                    array.push(spv, v)
                    array.shift(spv)
                    array.push(tnsdata_spr1, math.abs(lastclose-close))
                    array.shift(tnsdata_spr1)
                    if lastclose > close
                        array.push(hi, 1)
                        array.shift(hi)
                    else if lastclose < close
                        array.push(hi, 2)
                        array.shift(hi)
                    else
                        array.push(hi, 0)
                        array.shift(hi)
            else if f1 and f2
                if v <= lT and v >= gT
                    sign := sign + "<="
                    array.push(tnsdata_vol1, v)
                    array.shift(tnsdata_vol1)
                    array.push(tnsdata_cls1, c)
                    array.shift(tnsdata_cls1)
                    array.push(tnsdata_tmn1, timenow)
                    array.shift(tnsdata_tmn1)
                    array.push(spt, time)
                    array.shift(spt)
                    array.push(spv, v)
                    array.shift(spv)
                    array.push(tnsdata_spr1, math.abs(lastclose-close))
                    array.shift(tnsdata_spr1)
                    if lastclose > close
                        array.push(hi, 1)
                        array.shift(hi)
                    else if lastclose < close
                        array.push(hi, 2)
                        array.shift(hi)
                    else
                        array.push(hi, 0)
                        array.shift(hi)
            
            array.push(tnsdata_vol, v)
            array.shift(tnsdata_vol)
            array.push(tnsdata_cls, c)
            array.shift(tnsdata_cls)
            array.push(tnsdata_tmn, timenow)
            array.shift(tnsdata_tmn)
        else
            if f1 or f2
                if f1 and not f2
                    if v >= gT
                        array.push(tnsdata_vol, v)
                        array.shift(tnsdata_vol)
                        array.push(tnsdata_cls, c)
                        array.shift(tnsdata_cls)
                        array.push(tnsdata_tmn, timenow)
                        array.shift(tnsdata_tmn)
                if f2 and not f1
                    if v <= lT
                        array.push(tnsdata_vol, v)
                        array.shift(tnsdata_vol)
                        array.push(tnsdata_cls, c)
                        array.shift(tnsdata_cls)
                        array.push(tnsdata_tmn, timenow)
                        array.shift(tnsdata_tmn)
                if f1 and f2
                    if v >= gT and v <= lT
                        array.push(tnsdata_vol, v)
                        array.shift(tnsdata_vol)
                        array.push(tnsdata_cls, c)
                        array.shift(tnsdata_cls)
                        array.push(tnsdata_tmn, timenow)
                        array.shift(tnsdata_tmn)
            else
                array.push(tnsdata_vol, v)
                array.shift(tnsdata_vol)
                array.push(tnsdata_cls, c)
                array.shift(tnsdata_cls)
                array.push(tnsdata_tmn, timenow)
                array.shift(tnsdata_tmn)
    lastvol := volume
    lastclose := close
sprd = "Avg\n"
sprd := sprd + str.tostring(math.round_to_mintick(array.avg(tnsdata_spr)))

//plots
if showTickCount
    table.cell(tab, 2, 0,"total\n" + str.tostring(tickCount) + "\nticks")//str.tostring(total_time)

if showTotalRowSpeed
    table.cell(tab, 0, 0, str.tostring(r)+" ticks per\n" +
                     str.tostring(math.floor(total_time/60000)) + "m " + 
                     str.tostring(math.round(total_time/1000)%60) + "s")//str.tostring(total_time)
if showPerTickSpeed
    table.cell(tab, 1, 0, "1 tick per\n" +
                     str.tostring(math.floor(total_time_1/60000)) + "m " + 
                     str.tostring(math.round(total_time_1/1000)>=60?math.round(total_time_1/1000)-60:math.round(total_time_1/1000)) + "s")//str.tostring(total_time)
if showSpread
    table.cell(tab, 3, 0, sprd)

if barstate.isrealtime and showSpeedViz
    s = math.round(total_time/1000)%60
    if r < s
        s := r
    for i=r to s
        table.cell(tab_, i, 0, "", bgcolor=color.red, text_size=size.tiny)
    if s > 0
        for i=0 to s
            table.cell(tab_, i, 0, "", bgcolor=color.white, text_size=size.tiny)

one_candle = time-time[1]
if barstate.isrealtime and showhv
    table.cell(tab, 1, 25, "Highest" , bgcolor=#2962ff, text_color=color.white)
    table.cell(tab, 2, 25, "Vol" , bgcolor=#2962ff, text_color=color.white)
    table.cell(tab, 0, 25, "", bgcolor=#2962ff, text_color=color.white)
    if showSpread
        table.cell(tab, 3, 25, "", bgcolor=#2962ff, text_color=color.white)
    if colAccTabHV
        barMarkCol := hvc == 0 ? color.gray : hvc == 1 ? color.new(#b71c1c, 0) : hvc == 2 ? color.new(#00796b, 0) : na
    table.cell(tab, 0, 26, str.tostring(hour(int(ht)), "##") + ":" + 
                          str.tostring(minute(int(ht)), "##") + ":" + 
                          str.tostring(second(int(ht)), "##"), text_color=barMarkCol)
    table.cell(tab, 1, 26, str.tostring(hc), text_color=barMarkCol)
    table.cell(tab, 2, 26, str.tostring(hv), text_color=barMarkCol)
    if showSpread
        table.cell(tab, 3, 26, str.tostring(hs), text_color=barMarkCol)
    if showBarMark
        hl := label.new(x=tl, y=0, text=barMarkChar, textcolor=barMarkCol, xloc=xloc.bar_time, yloc=yloc.abovebar, style=label.style_none, size=size.large, tooltip=str.tostring(hv))
        label.delete(hl[1])
    
    if showHvLineLabel and hc != 0.0
        lihv := line.new(x1=ht-one_candle, y1=hc, x2=time+25*one_candle, y2=hc,
                         xloc=xloc.bar_time, color=barMarkCol, 
                         style=line.style_dotted, width=2)
        lahv := label.new(x=time+25*one_candle, y=hc, xloc=xloc.bar_time, yloc=yloc.price, 
                         style=label.style_label_left, textcolor=color.white, color=barMarkCol,
                         text=str.tostring(hc)+"\n"+str.tostring(hv))

if array.size(tnsdata_vol) > 0 and barstate.isrealtime
    j = array.size(tnsdata_vol)-1
    for i=2  to array.size(tnsdata_vol)-1 //array.size(tnsdata_vol)
        if j > 1
            if array.get(tnsdata_cls, j) > array.get(tnsdata_cls, j-1)
                array.set(tnsdata_col, j, #00796b)
                array.set(tnsdata_spr, j, math.abs(array.get(tnsdata_cls, j)-array.get(tnsdata_cls, j-1)))
            else if array.get(tnsdata_cls, j) < array.get(tnsdata_cls, j-1)
                array.set(tnsdata_col, j, #b71c1c)
                array.set(tnsdata_spr, j, math.abs(array.get(tnsdata_cls, j)-array.get(tnsdata_cls, j-1)))
            else
                array.set(tnsdata_col, j, color.gray)
                array.set(tnsdata_spr, j, math.abs(array.get(tnsdata_cls, j)-array.get(tnsdata_cls, j-1)))
        col = array.get(tnsdata_col, j)
        table.cell(tab, 0, i, str.tostring(hour(int(array.get(tnsdata_tmn, j))), "##") + ":" + 
                              str.tostring(minute(int(array.get(tnsdata_tmn, j))), "##") + ":" + 
                              str.tostring(second(int(array.get(tnsdata_tmn, j))), "##")
                              , text_color=col)
        table.cell(tab, 2, i, str.tostring(array.get(tnsdata_vol, j)), text_color=col)
        table.cell(tab, 1, i, str.tostring(array.get(tnsdata_cls, j)), text_color=col)
        if showSpread
            table.cell(tab, 3, i, str.tostring(array.get(tnsdata_spr, j)), text_color=col)
        j := j - 1

if barstate.isrealtime and r1s
    table.cell(tab, 0, 28, "Volume" , bgcolor=#2962ff, text_color=color.white)
    if showSpread
        table.cell(tab, 3, 28, "" , bgcolor=#2962ff, text_color=color.white)
    table.cell(tab, 1, 28, f1 and not f2?">=":f2 and not f1?"<=":f1 and f2?"b/w":"", bgcolor=#2962ff, text_color=color.white)
    table.cell(tab, 2, 28, f1 and not f2?(str.tostring(gT)):
                           f2 and not f1?(str.tostring(lT)):
                           f1 and f2?(str.tostring(gT) + " & \n" + str.tostring(lT)):"", bgcolor=#2962ff, text_color=color.white)
    
    j = 29 + array.size(tnsdata_vol1)-1
    for i = 0 to array.size(tnsdata_vol1)-1
        col = array.get(hi, i) == 0 ? color.gray : array.get(hi, i) == 1 ? color.new(#b71c1c, 0) : array.get(hi, i) == 2 ? color.new(#00796b, 0) : na
        table.cell(tab, 0, j, str.tostring(hour(int(array.get(tnsdata_tmn1, i))), "##") + ":" + 
                              str.tostring(minute(int(array.get(tnsdata_tmn1, i))), "##") + ":" + 
                              str.tostring(second(int(array.get(tnsdata_tmn1, i))), "##")
                              , text_color=col)
        table.cell(tab, 2, j, str.tostring(array.get(tnsdata_vol1, i)), text_color=col)
        table.cell(tab, 1, j, str.tostring(array.get(tnsdata_cls1, i)), text_color=col)
        if showSpread
            table.cell(tab, 3, j, str.tostring(array.get(tnsdata_spr1, i)), text_color=col)
        if array.get(tnsdata_vol1, i) > 0 and showBarMarkF
            if colAccTabF
                barMarkColF := col
            label.new(x=array.get(spt, i), y=0, text=barMarkCharF, textcolor=barMarkColF, xloc=xloc.bar_time, yloc=yloc.belowbar, style=label.style_none, 
                 size=size.large, tooltip=str.tostring(array.get(spv, i)))
        
        
        if showFilLineLabel and array.get(tnsdata_cls1, i) != 0.0
            filline := line.new(x1=array.get(tnsdata_tmn1, i)-one_candle, y1=array.get(tnsdata_cls1, i), 
                             x2=time+5*one_candle, y2=array.get(tnsdata_cls1, i),
                             xloc=xloc.bar_time, color=barMarkColF, 
                             style=line.style_dashed, width=2)
            fillabel := label.new(x=time+5*one_candle, y=array.get(tnsdata_cls1, i), xloc=xloc.bar_time, yloc=yloc.price, 
                             style=label.style_label_left, textcolor=color.white, color=barMarkColF, size=size.small,
                             text=str.tostring(array.get(tnsdata_cls1, i))+" ("+str.tostring(array.get(tnsdata_vol1, i))+")")
        j := j - 1