Monthly Returns of a Strategy in a Chart
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © QuantNomad

//@version=5
strategy("Monthly Returns in a Chart", overlay = true, 
     default_qty_type = strategy.percent_of_equity, default_qty_value = 25, 
     calc_on_every_tick = true, commission_type = strategy.commission.percent, 
     commission_value = 0.1)

// |++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++|
// |                                   INPUTS                                   |
// |++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++|

leftBars  = input.int(2, title = "Left Bars" )
rightBars = input.int(1, title = "Right Bars")
prec      = input.int(2, title = "Precision" )

// Plot Location
inPlotPos  = input.string(title="Plot Location", defval= "Bottom Center", 
     options =["Top Right" , "Middle Right" , "Bottom Right" , 
               "Top Center", "Middle Center", "Bottom Center", 
               "Top Left"  , "Middle Left"  , "Bottom Left" ], group= "Plot Setting")

// Bar Size
barHigh = input.float(0.3,  "Bar Height", minval = 0.2, step= 0.01, group = "Plot Setting")
barWdth = input.float(1.15, "Bar Width" , minval = 1,   step= 0.01, group = "Plot Setting")

// Plot Color
pltCol  = input.color(#696969,      title="Background ", group = "Plot Setting", inline = "1")
borCol  = input.color(color.silver, title="Border   ",   group = "Plot Setting", inline = "2")
txtCol  = input.color(color.white,  title="Text    ",    group = "Plot Setting", inline = "3")

// Bar Color
posCol  = input.color(color.green, title=" UP  ", group = "Plot Setting", inline = "1")
negCol  = input.color(color.red,   title=" DOWN", group = "Plot Setting", inline = "2")


// |++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++|
// |                             CALCULATION                                    |
// |++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++|

// +++++++++++++++++++++++++++++++++++ STRATEGY
// Pivot Points 
swh = ta.pivothigh(leftBars, rightBars)
swl = ta.pivotlow(leftBars, rightBars)

hprice = 0.0
hprice := not na(swh) ? swh : hprice[1]

lprice = 0.0
lprice := not na(swl) ? swl : lprice[1]

le = false
le := not na(swh) ? true : (le[1] and high > hprice ? false : le[1])

se = false
se := not na(swl) ? true : (se[1] and low < lprice ? false : se[1])

if (le)
	strategy.entry("PivRevLE", strategy.long, comment="PivRevLE", stop=hprice + syminfo.mintick)

if (se)
	strategy.entry("PivRevSE", strategy.short, comment="PivRevSE", stop=lprice - syminfo.mintick)

plot(hprice, color = color.green, linewidth = 2)
plot(lprice, color = color.red,   linewidth = 2)


// +++++++++++++++++++++++++++++++++++ OUTPUT ARRAY

new_month = month(time) != month(time[1])

bar_pnl   = strategy.equity / strategy.equity[1] - 1

cur_month_pnl = 0.0

// Current Monthly P&L
cur_month_pnl := new_month ? 0.0 : 
                 (1 + cur_month_pnl[1]) * (1 + bar_pnl) - 1 

// Arrays to store Yearly and Monthly P&Ls
var month_pnl  = array.new_float(0)
var month_time = array.new_int(0)

last_computed = false

if (not na(cur_month_pnl[1]) and (new_month or barstate.islast))
    if (last_computed[1])
        array.pop(month_pnl)
        array.pop(month_time)
        
    array.push(month_pnl , 100 * cur_month_pnl[1])
    array.push(month_time, time[1])

last_computed := barstate.islast ? true : nz(last_computed[1])


if array.size(month_pnl) > 71
    array.shift(month_pnl)
    array.shift(month_time)

// Find the Maximum Number for Plot Scale
scalCoef = 32/math.max(math.abs(array.min(month_pnl)), math.abs(array.max(month_pnl)))

// |++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++|
// |                                   TABLE                                    |
// |++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++|
// Get months Name
monNme(m) =>
    mnth = array.from("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", 
                      "SEP", "OCT", "NOV", "DEC")
    array.get(mnth, m-1)

// Get Table Position
pltPos(p) => 
    switch p
        "Top Right"     => position.top_right     
        "Middle Right"  => position.middle_right   
        "Bottom Right"  => position.bottom_right  
        "Top Center"    => position.top_center    
        "Middle Center" => position.middle_center
        "Bottom Center" => position.bottom_center
        "Top Left"      => position.top_left      
        "Middle Left"   => position.middle_left 
        => position.bottom_left


// Find Number of Columns
numCol = array.size(month_pnl) * 2 + 2 < 10 ? 10 : array.size(month_pnl) * 2 + 2


// Set Up Plot Table
var plt = table.new(position.bottom_left, 1 , 1)
plt    := table.new(pltPos(inPlotPos), numCol, 69, bgcolor = color.new(pltCol, 50),
          frame_width  = 1,  frame_color  = borCol,
          border_width = 0,  border_color = color.new(txtCol, 100))

// Plot Cell
pltCell (x, y, w, h, col, tt) =>
    table.cell(plt, x, y, width = w, height = h, bgcolor = col)
    table.cell_set_tooltip(plt, x, y, tt)

// Plot Columns
pltCol (x, y, k, col, tt) =>
    pltCell(x, y > 0 ? 33 - k : y < 0 ? 35 - k : 33, barWdth, barHigh, 
     color.new(col,   0), tt)
    pltCell(x, y > 0 ? 35 + k : y < 0 ? 33 + k : 33, barWdth, barHigh, 
     color.new(col, 100), tt)

// Label Y-Axis Increments
yInc(y, val) =>
    table.cell(plt, 0, y-2, text = str.tostring(val, "#.#"), 
         text_halign = text.align_center, text_valign = text.align_center, 
         text_color = txtCol, text_size = size.small, width = 1.5, height = 0.2)
    table.merge_cells(plt, 0, y-2, 0, y+2)

// Plot Title
pltTtl(x, xf, txt, txtCol) =>
    table.cell(plt, x, 0, width = barWdth, height = 2, 
             bgcolor = color.new(color.black, 100), 
             text_halign = text.align_center, text_valign = text.align_top, 
             text = txt, text_color = txtCol, text_size = size.small)
    table.merge_cells(plt, x, 0, xf, 0)


if barstate.islast
    if array.size(month_pnl) > 0
        
        // Reset Table. 
        for i = 0 to numCol - 1
            for j = 0 to 68
                pltCell(i, j, 0.001, 0.001, color.new(txtCol, 100), "")
        
        w = 0
        for i = 1 to array.size(month_pnl) * 2 + 1  
            if i%2 == 0 
                barCol = array.get(month_pnl, w) > 0 ? posCol :
                         array.get(month_pnl, w) < 0 ? negCol : borCol
                    
                //Plot Columns
                if scalCoef * array.get(month_pnl, w) == 0
                    pltCol(i, array.get(month_pnl, w), 0, barCol, "")
                    
                else 
                    for k = 0 to math.round(scalCoef * array.get(month_pnl, w))
                        pltCol(i, array.get(month_pnl, w), k, barCol,
                         str.tostring(math.round(array.get(month_pnl, w), prec)) 
                         + "\n" + monNme(month(array.get(month_time, w))) + "-" +
                         str.tostring(year(array.get(month_time, w))))
                
                // X-Axis Increments
                table.cell(plt, i, 68, width = barWdth, text_color = txtCol,
                 text =  monNme(month(array.get(month_time, w))) + "\n" +
                         str.tostring(year(array.get(month_time, w)) - 2000), 
                         text_size = size.tiny)
                table.merge_cells(plt, i, 68, i+1, 68)
                
                w := w + 1
                
            else 
                for j = 0 to 68
                    pltCell(i, j, 0.05, 0.001, color.new(pltCol, 94), "")
            
            if i >= 2 and i <= array.size(month_pnl) * 2
                pltCell(i, 34, 0.05, 0.005, borCol, "")


        // Y-Axis Increments
        table.cell(plt, 0, 1, width = barWdth, height = 1)
        yInc( 2, math.ceil( 32/scalCoef))
        yInc(18, math.round(16/scalCoef))
        yInc(34, 0/scalCoef)
        yInc(49, math.round(-16/scalCoef))
        yInc(66, math.floor(-32/scalCoef))
        
        // Title
        pltTtl( 1, 8, "Monthly P&L", txtCol)

