TPO Profile with Day Stat
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Vignesh_vish

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

//@version=5
indicator("TPO with Day Stat", overlay=true, max_bars_back=5000, max_boxes_count = 500, max_lines_count = 500)
IBRcheck=input.bool(true,"Show/ Hide IBR",group="IBR")
dataLabelCheck=input.bool(true,"Show/ Hide Data-Label",group="Data Label")
dataLabelColor=input.color(color.black,"Data Label Color",group="Data Label")
IBlineColor=input.color(color.new(color.white,0),"IB Line Color",group="IBR")
secu(tikr, res, source,pre=0) =>
    request.security(tikr, res, source[pre], lookahead=barmerge.lookahead_on)
currentBarState=secu(syminfo.ticker,"D",barstate.isrealtime)?1:0
mkt_start_hh=09
mkt_start_mm=15
mkt_end_hh=15
mkt_end_mm=25
ib_session=""
session = ""
MM=15
if syminfo.prefix=="NSE" or syminfo.prefix=="BSE"
    mkt_start_hh:=09
    mkt_start_mm:=15
    mkt_end_hh:=15
    mkt_end_mm:=25
    ib_session:="0915-1015"   
    session:="0915-1515"
    MM:=30
else if syminfo.prefix=="MCX"
    mkt_start_mm:=00
    mkt_end_hh:=23
    ib_session:="0900-1000"
    session:="0000-2300"//input.session("0930-1600", title="Session calculation", group="Session configuration")
    MM:=15

var string Ibtimes= na
var ibRange = 0.0
var ibhigh =0.0
var iblow =0.0
var sessionBarCount = 0
ib = na(time('D', ib_session + ':23456')) == false
drange() =>
    math.round(secu(syminfo.ticker, 'D', high) - secu(syminfo.ticker, 'D', low),0)
if ib
    ibRange := math.round(secu(syminfo.ticker, '60', high) - secu(syminfo.ticker, '60', low),0)
    ibhigh := secu(syminfo.ticker, '60', high)
    iblow :=secu(syminfo.ticker, '60', low)
nl = '\n'
Ibtimes:=math.round((drange()/ibRange),1)>=2?" ("+str.tostring(int((drange()/ibRange)))+"-IB)":na
//1000->1K 100000->1L Convertor
numberShort(number) =>
    len = str.length(str.tostring(number))
    if len >= 4  //and len <= 5
        str.tostring(math.round(number / 1000, 0)) + "K"
    else if len < 4
        str.tostring(number)
vix() =>
    str.tostring(math.round(secu("INDIAVIX","D",close),2))+" ("+str.tostring(math.round(((secu("INDIAVIX","D",close)-secu("INDIAVIX","D",close[1]))/secu("INDIAVIX","D",close[1]))*100,2))+"%)"
_atr() =>
    str.tostring(math.round(ta.atr(14), 0))+" ("+str.tostring(math.round(secu(syminfo.ticker,"D",ta.atr(14)), 0))+")"

dayVolume(tf) =>
    dayVol = if syminfo.ticker == 'NIFTY'
        secu('NIFTY1!', tf, volume)
    else if syminfo.ticker == 'BANKNIFTY'
        secu('BANKNIFTY1!', tf, volume)
    else
        secu(syminfo.ticker, tf, volume)
    numberShort(dayVol)

futspotpricediff = if syminfo.ticker == 'NIFTY' or syminfo.ticker == 'NIFTY1!'
    secu('NIFTY1!', timeframe.period, close) - secu('NIFTY', timeframe.period, close) 
else if syminfo.ticker == 'BANKNIFTY' or syminfo.ticker == 'BANKNIFTY1!'
    secu('BANKNIFTY1!', timeframe.period, close) - secu('BANKNIFTY', timeframe.period, close)

//combaine Text and and formatting 
strHelper(prefix, stat) =>
    res = str.tostring(stat)
    nl + prefix + ': ' + res
dynamicText() =>
    strHelper('IBR', str.tostring(ibRange)+Ibtimes) + strHelper('Day Range', drange()) + strHelper('Day Volume', dayVolume('D')) + strHelper('ATR', _atr()) + strHelper('VIX', vix()) + strHelper('Fut Delta', futspotpricediff)
_label(barInx) =>
    x1=timestamp(year(time), month(time), dayofmonth(time), mkt_start_hh, mkt_start_mm, 00)
    x2=timestamp(year(time), month(time), dayofmonth(time), mkt_end_hh, mkt_end_mm, 00)
    if dataLabelCheck
        lbl = label.new(x1, secu(syminfo.ticker, 'D', low) - (syminfo.mintick * 5), dynamicText(),xloc=xloc.bar_time, style=label.style_label_upper_left, textalign=text.align_left, color=color.new(#d0cec2,100), textcolor=dataLabelColor)
        if ta.barssince(ta.change(time("D")))>=1 or dayofmonth(time)==dayofmonth(chart.left_visible_bar_time)
            label.delete(lbl[1])
    if IBRcheck
        linib=line.new(x1,ibhigh,x1,iblow,xloc.bar_time,color=IBlineColor,width=2,style= line.style_solid)

        if ta.barssince(ta.change(time("D")))>=1 or dayofmonth(time)==dayofmonth(chart.left_visible_bar_time)
            line.delete(linib[1])
    
    day= switch dayofweek(timenow)
        1 => "Sunday"
        2 => "Monday"
        3 => "Tuesday"
        4 => "Wednesday"
        5 => "Thursday"
        6 => "Friday"
        7 => "Saturday"
    holydaycolor= dayofweek(timenow) == 1 or dayofweek(timenow) == 7 ?color.new(color.red,50):color.new(color.teal,50)
    
    daytable=table.new(position.bottom_right,columns=1,rows=1,border_width=0)
    if barstate.islast
        table.cell(table_id=daytable,column=0,row=0,text=day,bgcolor=color.new(color.teal,50))

if ta.change(time("D"))
    sessionBarCount:=1
if (ta.barssince(ta.change(time("D")))>=1 or ta.change(time("D"))) and (timeframe.period== "30" or timeframe.period == "5" or timeframe.period=="15" or timeframe.period=="1") and (syminfo.prefix=="NSE" or syminfo.prefix=="BSE_DLY" or syminfo.prefix=="MCX") and time >=  chart.left_visible_bar_time and time <=  chart.right_visible_bar_time
    sessionBarCount += 1
    _label(sessionBarCount - 2)
from=0
var TRangeRMA=0.0

// auto tick size
TrRMA(tf,pre)=>
    tRange=math.max(secu(syminfo.ticker,tf,high)-secu(syminfo.ticker,tf,low),math.abs(secu(syminfo.ticker,tf,high)-secu(syminfo.ticker,tf,close[1])),math.abs(secu(syminfo.ticker,tf,low)-secu(syminfo.ticker,tf,close[1])))
    ta.rma(tRange,pre)
if timeframe.period=="30" 
    from:= 90
    if ta.change(time("D"))
        divisor=0.0
        if syminfo.prefix=="NSE" or syminfo.prefix=="BSE"
            divisor:=2.5
        else if syminfo.prefix=="MCX"
            divisor:=80
        TRangeRMA:=(TrRMA("D",14))/divisor


auto_tick_size = input.bool(true,"Auto Tick Size",group="Global Options")
float tick_size = 0.0

if auto_tick_size==true
    tick_size :=(TRangeRMA*syminfo.mintick)
else
    tick_size := input.int(25, "Ticks number per tpo", group="Global Options") * syminfo.mintick

round_values = input.bool(true, "Round TPO levels on Tick number", group="Global Options")
letters_size = input.string("Small", options=["Normal", "Small", "Tiny"], title="TPO letters size", group="Global Options")
extend_levels = input.bool(false, "Extend yesterday's levels", group="Global Options")
show_va_background = input.bool(false, "Show Market Profile calculation area background", group="Global Options")
va_bg_color=input.color(color.new(#2a2e39,90), "Value area background color", group="Global Options")
show_histogram = input.bool(true, "Show TPO's", group="Global Options")
tpo_va_color = input.color(color.new(#2962ff,0), "TPO Color (in VA)", group="Global Options")//787b86
tpo_nova_color = input.color(color.new(color.black,0), "TPO Color (out of VA)", group="Global Options")//9598a1
limits_color=input.color(color.black, "High and Low colors", group="Highs & Lows")
show_high_low = input.bool(false, "Show high/low levels", group="Highs & Lows")

poc_col = input.color(#2962ff, "POC color", group="POC Options")//
show_poc = input.bool(true, "Show POC as line", group="POC Options")
show_poc_tpo_count=input.bool(true, "Show POC TPO Count", group="POC Options")
show_initial_balance = input.bool(true, "Show Initial Balance", group="IB")
ib_color = input.color(#ffffff, "IB Color", group="IB")

va_line_color = input.color(#ffffff, "Value area lines color", group="Value area options")
show_va_lines = input.bool(true, "Show value areas as lines",group="Value area options")
va_percent = input.int(70, "Value area percent",group="Value area options") / 100

show_singleprints = input.bool(true, "Highlight Single prints", group="Single prints")
sp_col = input.color(#0F4D92, "Single prints color", group="Single prints")//#2962ff 8caad8
extend_sp = input.bool(true, "Extend single prints", group="Single prints")
sp_ext_trans = input.int(80, "Extended single prints transparency", group="Single prints", minval=0, maxval=100)
open_color = input.color(color.yellow, "Opening price color", group="Open/Close")
close_color = input.color(#ff0000, "Closing price color", group="Open/Close")

tpo_spacing = input.int(1, "TPO Spacing")

inSession(sess) => na(time(timeframe.period, sess)) == false
sess = inSession(session)
session_over = sess[1] and not sess
session_start = sess and not sess[1]
currentDate=timestamp(year(timenow),month(timenow),dayofmonth(timenow))
plot_history=false
print_as_bars=false
if timestamp(year(timenow),month(timenow),dayofmonth(timenow),hour(timenow),minute(timenow),second(timenow))<=timestamp(year(currentDate),month(currentDate),dayofmonth(currentDate),mkt_end_hh,MM,00)
    plot_history:=timestamp(year(time),month(time),dayofmonth(time))>=chart.left_visible_bar_time and timestamp(year(time),month(time),dayofmonth(time))<=timestamp(year(chart.right_visible_bar_time),month(chart.right_visible_bar_time),dayofmonth(chart.right_visible_bar_time))and not(currentBarState)
    print_as_bars:=false//input.bool(false, "Display TPO as bars", group="Global Options")// plot_history?true:
else if session_over
    plot_history:=timestamp(year(time),month(time),dayofmonth(time))>=chart.left_visible_bar_time and timestamp(year(time),month(time),dayofmonth(time))<=timestamp(year(chart.right_visible_bar_time),month(chart.right_visible_bar_time),dayofmonth(chart.right_visible_bar_time))and not(currentBarState)//timestamp(year(time),month(time),dayofmonth(time))>=timestamp(year(currentDate),month(currentDate),dayofmonth(currentDate)-from)
    print_as_bars:=false
letters = input.string("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$€£{}[]()*+-/=%&?!", "TPO letters")

letters_size_value = letters_size == "Small" ? size.small : letters_size == "Normal" ? size.normal : size.tiny

bars_nb = 0

var line topline = na
var line botline = na
var line vah_line = na
var line val_line = na
var line poc = na
var label poc_tpo_count= na

//var max_score = 0
var poc_price = 0.0
var bars_score = array.new<int>(bars_nb)
var profile_bars = array.new<box>(bars_nb)
var letter_bars = array.new<string>(bars_nb)
var vah_offset = 0
var val_offset = 0

spaces_string(n) =>
    r = ""
    if n > 0
        for i=0 to n
            r += " "
    r

find_h(x) =>
    h = high
    for i=0 to x
        if high[i] > h
            h := high[i]
    h
find_l(x) =>
    l = low
    for i=0 to x
        if low[i] < l
            l := low[i]
    l
count_tpo(x, y) =>
    r = 0
    for i=x to y
        r +=  array.get(bars_score,i)
    r

biggest_tpo(x, y) =>
    t1 = array.get(bars_score, x)
    t2 = array.get(bars_score, y)
    t1 >= t2 ? "h": "l"

var session_candles_count = 0
var poc_position = 0
var total_tpo = 0
var open_price = open

if session_start
    session_candles_count := 0
    open_price := open    
//if candle_offset == candles_nb -1
if session_over and plot_history
    ext = extend_levels ? extend.right: extend.none
    if extend_levels
        line.set_extend(vah_line, extend.none)
        line.set_extend(val_line, extend.none)
        line.set_extend(poc, extend.none)
        line.set_extend(topline, extend.none)
        line.set_extend(botline, extend.none)
        
    max_score = 0
    total_tpo := 0
    h = find_h(session_candles_count)
    highest=h
    l = find_l(session_candles_count)
    bars_nb := int((h-l) / tick_size)
    if round_values
        h := h+tick_size - (h % tick_size)
        l := l - (l % tick_size)
    box_width = (h-l) / bars_nb

    if show_poc
        poc := line.new(bar_index-session_candles_count, 0.0, bar_index, 0.0, color=poc_col, width=2, extend=ext)
    if show_poc_tpo_count
        poc_tpo_count :=label.new(bar_index,0.0,text="",style=label.style_none, textcolor=tpo_nova_color, size=letters_size_value,textalign=text.align_left)
    if show_high_low
        topline := line.new(bar_index-session_candles_count, h, bar_index, h, color=limits_color, extend=ext)
        botline := line.new(bar_index-session_candles_count, l, bar_index, l, color=limits_color, extend=ext)

    // TPO calculation
    for i=0 to bars_nb-1
        tpo_letters = ""
        score = 0
        for x=0 to session_candles_count
            in_bar = (high[x] <= h and high[x] >= h-box_width) or (low[x] <= h and low[x] >= h-box_width) or (high[x] > h and low[x] < h-box_width)
            if in_bar
                score += 1
                if x <= session_candles_count and session_candles_count <= str.length(letters) -1
                    tpo_letters := str.substring(letters, session_candles_count-(x), (session_candles_count - (x-1)))+spaces_string(tpo_spacing)+tpo_letters
                
        if score >= max_score
            max_score := score
            poc_price :=  h - (box_width/2)
            poc_position := i
        
        line.set_y1(poc, poc_price)
        line.set_y2(poc, poc_price)
        label.set_y(poc_tpo_count,poc_price)
        label.set_text(poc_tpo_count,str.tostring(max_score))
        if print_as_bars
            tpo_letters := ""
        array.insert(profile_bars,i, box.new(bar_index-session_candles_count, h, bar_index-(session_candles_count-score), h-box_width, text=tpo_letters, text_size=letters_size_value, text_color=tpo_va_color, text_halign=text.align_left, bgcolor=#00000000, border_color=#00000000))
        array.insert(bars_score, i, score)
        h -= box_width
        total_tpo += score

    // VA Calculation
    vah_price = 0.0
    val_price = 0.0
    vah_offset := poc_position > 0 ? poc_position -1 : poc_position
    val_offset := poc_position < bars_nb -1 ? poc_position +1 : poc_position

    for i=0 to bars_nb-1
        d = vah_offset == 0 ? "l" : val_offset == bars_nb-1 ? "h" : biggest_tpo(vah_offset, val_offset)
        if d == "h"
            vah_offset := vah_offset > 0 ? vah_offset -1 : vah_offset
        else
            val_offset := val_offset < bars_nb-1 ? val_offset +1 : val_offset
        if count_tpo(vah_offset, val_offset) / total_tpo >= va_percent
            break

    vah_price := box.get_top(array.get(profile_bars, vah_offset))-(box_width/2)
    val_price := box.get_bottom(array.get(profile_bars, val_offset))+(box_width/2)

    // Set tpo colors
    for i=0 to bars_nb-1
        bar_col = #00000000
        text_col =  (i > vah_offset and i < val_offset ? tpo_va_color : tpo_nova_color)
        text_col := val_offset == i or vah_offset == i ? tpo_va_color : poc_position == i ? poc_col : (array.get(bars_score, i) == 1 and show_singleprints) ? sp_col : text_col
        if not show_histogram
            text_col := #00000000
        if print_as_bars
            if not extend_sp
                box.set_border_color(array.get(profile_bars, i), text_col)
            box.set_bgcolor(array.get(profile_bars, i), text_col)
        if extend_sp and (array.get(bars_score, i) == 1 and show_singleprints)
            extended_sp_color = color.new(sp_col, transp=sp_ext_trans)
            box.set_right(array.get(profile_bars, i), bar_index-11)
            box.set_bgcolor(array.get(profile_bars, i), extended_sp_color)
            
        box.set_text_color(array.get(profile_bars, i), text_col)
        
    //plot vah/val
    if show_va_lines
        vah_line := line.new(bar_index-session_candles_count, vah_price, bar_index, vah_price, color=va_line_color, extend=ext)
        val_line := line.new(bar_index-session_candles_count, val_price, bar_index, val_price, color=va_line_color, extend=ext)

    label.new(bar_index-session_candles_count-1, open_price, text="🢒", style=label.style_none, textcolor=open_color)
    label.new(bar_index-session_candles_count-1, close, text="x", style=label.style_none, textcolor=close_color)
    
    line.new(bar_index-session_candles_count-1, open_price, bar_index-session_candles_count, open_price, color=open_color, width=3)
    line.new(bar_index-session_candles_count-1, close, bar_index-session_candles_count, close, color=close_color, width=3)
    
    //if show_initial_balance
    //    ibh = high[session_candles_count] > high[session_candles_count-1] ? high[session_candles_count] : high[session_candles_count-1]
    //    ibl = low[session_candles_count] < low[session_candles_count-1] ? low[session_candles_count] : low[session_candles_count-1]
    //    line.new(bar_index-session_candles_count, ibh, bar_index-session_candles_count, ibl, color=ib_color, width=2)
    if show_va_background
        box.new(bar_index-session_candles_count, vah_price, bar_index, val_price, bgcolor=va_bg_color, border_color=#00000000)
    label.new(bar_index-(session_candles_count/2), highest,text=str.tostring(total_tpo)+" TPO\n", style=label.style_none, textcolor=tpo_va_color, size=size.small)

session_candles_count += 1


