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

//@version=5
indicator('MarketSmith Volumes', format = format.volume, max_labels_count = 500)

// Inputs
colUp   = input(color.rgb(39,54,233,0), title='Up Volume Color', group = 'VOLUMES')
colDn   = input(color.rgb(222,50,174,0), title='Down Volume Color', group = 'VOLUMES')
prevC   = input(true, title='Color Based On Previous Close',  group='VOLUMES')
trunc   = input(false, title='Truncate for MarketSmith type display', group = 'DISPLAY')
labels  = input(true, title='Labels on Volume', group = 'LABELS', inline='1')
labelC  = input(color.rgb(0,0,0,0), title='Color', group = 'LABELS', inline='1')
colChg  = input(color.rgb(39,54,233,0), title='', group = 'LABELS', inline='1')
percChg = input(true, title='%Change on Labels',  group='LABELS')
labelS  = input.string('Normal', 'Text Size', options = ['Tiny', 'Small', 'Normal', 'Large'], group = 'LABELS')
peakL   = input(9, 'Peak Length', group='LABELS')
lenDa   = input(50, 'Length Daily', group = 'MA', inline = '1')
lenWe   = input(10, 'Weekly', group = 'MA', inline = '1')
colMa   = input(color.red, title='MA Color', group = 'MA')
showLab = input(true, title='Label on Last Volume Bar', group = 'CURRENT BAR LABEL')
sizeLab = input.string('Normal', 'Size', options = ['Tiny', 'Small', 'Normal', 'Large'], group = 'CURRENT BAR LABEL')
//curDol  = input(false, title='Average $ Volume', group = 'CURRENT BAR LABEL')
labCol  = input(color.rgb(59,60,66,0), title = 'Label Color', group = 'CURRENT BAR LABEL')
volCol  = input(color.white, title = 'Volume Color', group = 'CURRENT BAR LABEL')
posCol  = input(color.lime, title='Positive Buzz Color', group = 'CURRENT BAR LABEL')
negCol  = input(color.red, title='Negative Buzz Color', group = 'CURRENT BAR LABEL')
showTa  = input(true, title='Show Table', group = 'Table')
sizeTa  = input.string('Small', 'Table Size', options = ['Tiny', 'Small', 'Normal', 'Large'], group = 'Table')
volTa   = input(false, title = 'Current Volume on Table', group = 'Table')
buzzTa  = input(false, title = 'Vol. Buzz on Table', group = 'Table')
TabCol  = input(color.rgb(59,60,66,0), title = 'Label Color', group = 'Table')
txtTCol = input(color.white, title = 'Volume Color', group = 'Table')
posTCol  = input(color.lime, title='Positive Buzz Color', group = 'Table')
negTCol  = input(color.red, title='Negative Buzz Color', group = 'Table')

tf      = timeframe.period
ticker  = syminfo.tickerid

// Switch Label Size
sLabel = switch labelS
    'Normal'  => size.normal
    'Tiny'    => size.tiny
    'Small'   => size.small
    'Large'   => size.large

sLabel2 = switch sizeLab
    'Normal'  => size.normal
    'Tiny'    => size.tiny
    'Small'   => size.small
    'Large'   => size.large

taSize = switch sizeTa
    'Normal'  => size.normal
    'Tiny'    => size.tiny
    'Small'   => size.small
    'Large'   => size.large

// Volume and MA calculation
s       = ticker
vol     = request.security(s, tf, volume)
// Color of Volumes
colr = close>=open ? colUp:colDn
if(prevC)
    colr := close>=close[1] ? colUp:colDn
ma = timeframe.isweekly ? ta.sma(vol, lenWe) : ta.sma(vol, lenDa)

// Display in the style of MarketSmith
// Recalculation of the volume with limitation
vol2 = vol > 2*ma ? 2*ma:vol
// Display
plot(trunc ? vol2:vol, title = 'Volume' , color = colr, style = plot.style_histogram, linewidth = 3)
plot(ma, title = 'MA', color = colMa)

// Formatting Volume
uV = ''
volFormat = math.round(vol, 2)
if(volFormat >= 1000 and volFormat < 1000000)
    volFormat := math.round(volFormat/1000, 2)
    uV       := 'K'
if(volFormat >= 1000000 and volFormat < 1000000000)
    volFormat := math.round(volFormat/1000000, 2)
    uV       := 'M'
if(volFormat >= 1000000000)
    volFormat := math.round(volFormat/1000000000, 2)
    uV       := 'B'

// Average Volume
advVol = math.round(ma, 0)
uVAvg = ''
if(advVol >= 1000 and advVol < 1000000)
    advVol := math.round(advVol/1000, 2)
    uVAvg := 'K'
if(advVol >= 1000000 and advVol < 1000000000)
    advVol := math.round(advVol/1000000, 2)
    uVAvg := 'M'
if(advVol >= 1000000000)
    advVol := math.round(advVol/1000000000, 2)
    uVAvg := 'B'

// Average $ Volume
advDol = math.round(close*ma, 0)
uVDoll = ''
if(advDol >= 1000 and advDol < 1000000)
    advDol := math.round(advDol/1000, 0)
    uVDoll := 'K'
if(advDol >= 1000000 and advDol < 1000000000)
    advDol := math.round(advDol/1000000, 0)
    uVDoll := 'M'
if(advDol >= 1000000000)
    advDol := math.round(advDol/1000000000, 0)
    uVDoll := 'B'

// Labels: Marked Highs Volume Bars Peak     &     %Variation Labels
// For Good Beat after the Peak Volume Bars Period
pivotHigh  = ta.pivothigh(volume, peakL, peakL)
varPercent = 100*(vol[peakL]/ma[peakL])-100
var label volestvol   = na
var label volestvolU  = na
var label volestvol2  = na
var label volestvol2U = na
if(labels)
    // If we have a good % beat in a period lower than the Peak Volume Bars Period
    v = showLab ? 1:0  // If the label is visible, we don't want the last bar to have a text above
    for i=peakL-1 to v
        pivotHigh2 = ta.pivothigh(volume, peakL, i)
        varPercent2 = 100*(vol[i]/ma[i])-100
        if(barstate.islast and pivotHigh2 and varPercent2>25)
            textvol9  = 'N/A'
            textvol9U = 'N/A'
            if(percChg)
                textvol9  := str.tostring(volFormat[i], '0.0')+uV[i]+'\n'
                textvol9U := '+'+str.tostring(varPercent2, '0')+'%'
            if(not percChg)
                textvol9  := str.tostring(volFormat[i], '0.0')+uV[i]
                textvol9U := na
            // Delete Previous Labels to avoid superposition
            if not na(volestvol2)
                label.delete(volestvol2)
            if not na(volestvol2U)
                label.delete(volestvol2U)
            // Create Label
            volestvol2  := label.new(bar_index-i, trunc ? vol2[i]:vol[i], xloc=xloc.bar_index, yloc=yloc.price, style=label.style_none, size = sLabel, text=textvol9 , textcolor=labelC)
            volestvol2U := label.new(bar_index-i, trunc ? vol2[i]:vol[i], xloc=xloc.bar_index, yloc=yloc.price, style=label.style_none, size = sLabel, text=textvol9U, textcolor=colChg)
    // For Label After the Peak Period
    if (pivotHigh and varPercent>25)
        string textvol9  = 'N/A'
        string textvol9U = 'N/A'
        if(percChg)
            textvol9 := str.tostring(volFormat[peakL], '0.0')+uV[peakL]+'\n'
            textvol9U := '+'+str.tostring(varPercent, '0')+'%'
        if(not percChg)
            textvol9 := str.tostring(volFormat[peakL], '0.0')+uV[peakL]
            textvol9U := na
        // Delete Previous Labels2 of the first part to avoid superposition
        if not na(volestvol2)
            label.delete(volestvol2)
        if not na(volestvol2U)
            label.delete(volestvol2U)
        // Create Label
        volestvol  := label.new(bar_index-peakL, trunc ? vol2[peakL]:vol[peakL], xloc=xloc.bar_index, yloc=yloc.price, style=label.style_none, size= sLabel, text=textvol9,  textcolor=labelC)
        volestvolU := label.new(bar_index-peakL, trunc ? vol2[peakL]:vol[peakL], xloc=xloc.bar_index, yloc=yloc.price, style=label.style_none, size= sLabel, text=textvol9U, textcolor=colChg)



// Dynamique Label instead of a table
//Formatting Percentage Variation
volBuzz = 100*(vol/ma)-100
txtCol  = volBuzz >= 0 ? posCol:negCol

// Text on labels
txtVol    = str.tostring(volFormat)+uV+'\n'
txtDolVol = 'N/A'
txtVar    = volBuzz >= 0 ? '\n+'+str.tostring(math.round(volBuzz))+'%':'\n'+str.tostring(math.round(volBuzz))+'%'

// Label Display
if barstate.islast and showLab
    currentVol       = label.new(bar_index, trunc?vol2:vol, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, text=txtVol,    textalign = text.align_left, size=sLabel2, color = labCol, textcolor=volCol)
    currentBuz       = label.new(bar_index, trunc?vol2:vol, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, text=txtVar,    textalign = text.align_left, size=sLabel2, color = color.rgb(0, 0, 0, 100), textcolor=txtCol)
    label.delete(currentVol[1])
    label.delete(currentBuz[1])


// Up/Down Volume Ratio ( Thanks Brandon ;) )
upVol =         close > close[1] ? volume : 0
dnVol =         close < close[1] ? volume : 0
sumUp =         math.sum(upVol, 50)
sumDn =         math.sum(dnVol, 50)
upDnVolRatio =  sumUp / sumDn


// Table
// Display Table
table t = table.new(position.top_right, 2, 5, bgcolor=TabCol)
if barstate.islast and showTa
    // Average Volume
    table.cell(t, 0, 0, "Avg Vol: ", text_color=txtTCol, text_size=taSize)
    table.cell(t, 1, 0, str.tostring(advVol, '0.00')+uVAvg, text_color =txtTCol, text_size=taSize)
    table.cell_set_text_halign(t, 0,  0, text_halign = text.align_right)
    table.cell_set_text_halign(t, 1,  0, text_halign = text.align_right)
    // Average Dollar Volume
    table.cell(t, 0, 1, "Avg $ Vol: ", text_color=txtTCol, text_size=taSize)
    table.cell(t, 1, 1, str.tostring(advDol)+uVDoll, text_color =txtTCol, text_size=taSize)
    table.cell_set_text_halign(t, 0,  1, text_halign = text.align_right)
    table.cell_set_text_halign(t, 1,  1, text_halign = text.align_right)
    // Up/Down Volume
    table.cell(t, 0, 2, "U/D Vol: ", text_color=txtTCol, text_size=taSize)
    table.cell(t, 1, 2, str.tostring(upDnVolRatio, '0.0'), text_color =txtTCol, text_size=taSize)
    table.cell_set_text_halign(t, 0,  2, text_halign = text.align_right)
    table.cell_set_text_halign(t, 1,  2, text_halign = text.align_right)
    if volTa
        table.cell(t, 0, 3, "Vol: ", text_color=txtTCol, text_size=taSize)
        table.cell(t, 1, 3, str.tostring(volFormat, '0.00')+uV, text_color =txtTCol, text_size=taSize)
        table.cell_set_text_halign(t, 0,  3, text_halign = text.align_right)
        table.cell_set_text_halign(t, 1,  3, text_halign = text.align_right)
    if buzzTa
        table.cell(t, 0, 4, "Buzz: ", text_color=txtTCol, text_size=taSize)
        table.cell(t, 1, 4, volBuzz >= 0 ? '+'+str.tostring(math.round(volBuzz))+'%':str.tostring(math.round(volBuzz))+'%', text_color =volBuzz >= 0 ? posTCol:negTCol, text_size=taSize)
        table.cell_set_text_halign(t, 0,  4, text_halign = text.align_right)
        table.cell_set_text_halign(t, 1,  4, text_halign = text.align_right)