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

//@version=5

// @description Display a table showing monthly returns vs market
// Only one function is exported : displayMonthlyPnL(int precision)
// When precision is 0 value will be rounded as int, precision=1
// value will be rounded to 1 number after decimal point, etc.
//
library("MonthlyReturnsVsMarket")


getCellColor(pnl, bh)  => 
    if pnl > 0
        if bh < 0 or pnl > bh
            color.new(color.green, transp = 20)
        else
            color.new(color.green, transp = 70)
    else
        if bh > 0 or pnl < -0.2 or pnl < bh
            color.new(color.red, transp = 20)
        else
            color.new(color.red, transp = 70)

export displayMonthlyPnL(simple int precision, simple bool showMonthlyMarketPerformance= true, simple bool showYearlyMarketPerformance= true) => 
    cur_month_pnl = 0.0
    eq = strategy.equity
    bar_pnl = eq / eq[1] - 1
    bar_bh = (close-close[1])/close[1]

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

    cur_year_pnl  = 0.0
    cur_month_bh = 0.0
    cur_year_bh  = 0.0

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

    // Current Yearly P&L
    cur_year_pnl := new_year ? 0.0 : 
                 (1 + cur_year_pnl[1]) * (1 + bar_pnl) - 1
    cur_year_bh := new_year ? 0.0 : 
                 (1 + cur_year_bh[1]) * (1 + bar_bh) - 1

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

    var year_pnl  = array.new_float(0)
    var year_time = array.new_int(0)
    var year_bh  = array.new_float(0)

    end_time = false

    end_time:= time_close + (time_close - time_close[1]) > timenow or barstate.islastconfirmedhistory

    if (not na(cur_month_pnl[1]) and (new_month or end_time))
        if (end_time[1])
            array.pop(month_pnl)
            array.pop(month_time)
        array.push(month_pnl , cur_month_pnl[1])
        array.push(month_time, time[1])
        array.push(month_bh , cur_month_bh[1])

    if (not na(cur_year_pnl[1]) and (new_year or end_time))
        if (end_time[1])
            array.pop(year_pnl)
            array.pop(year_time)
        array.push(year_pnl , cur_year_pnl[1])
        array.push(year_time, time[1])
        array.push(year_bh , cur_year_bh[1])

    // Monthly P&L Table    
    var monthly_table = table(na)
    if end_time
        monthly_table := table.new(position.bottom_right, columns = 14, rows = array.size(year_pnl) + 1, border_width = 1, bgcolor=color.white)
        table.cell(monthly_table, 0,  0, "",     bgcolor = #cccccc)
        table.cell(monthly_table, 1,  0, "Jan",  bgcolor = #cccccc)
        table.cell(monthly_table, 2,  0, "Feb",  bgcolor = #cccccc)
        table.cell(monthly_table, 3,  0, "Mar",  bgcolor = #cccccc)
        table.cell(monthly_table, 4,  0, "Apr",  bgcolor = #cccccc)
        table.cell(monthly_table, 5,  0, "May",  bgcolor = #cccccc)
        table.cell(monthly_table, 6,  0, "Jun",  bgcolor = #cccccc)
        table.cell(monthly_table, 7,  0, "Jul",  bgcolor = #cccccc)
        table.cell(monthly_table, 8,  0, "Aug",  bgcolor = #cccccc)
        table.cell(monthly_table, 9,  0, "Sep",  bgcolor = #cccccc)
        table.cell(monthly_table, 10, 0, "Oct",  bgcolor = #cccccc)
        table.cell(monthly_table, 11, 0, "Nov",  bgcolor = #cccccc)
        table.cell(monthly_table, 12, 0, "Dec",  bgcolor = #cccccc)
        table.cell(monthly_table, 13, 0, "Year", bgcolor = #999999)

        for yi = 0 to array.size(year_pnl) - 1
            table.cell(monthly_table, 0,  yi + 1, str.tostring(year(array.get(year_time, yi))), bgcolor = #cccccc)
            y_color = getCellColor(array.get(year_pnl, yi), array.get(year_bh, yi))
            cellText= str.tostring(math.round(array.get(year_pnl, yi) * 100,precision))
            if showYearlyMarketPerformance
                cellText:= cellText + " (" + str.tostring(math.round(array.get(year_bh, yi) * 100,precision)) + ")"
            table.cell(monthly_table, 13, yi + 1, cellText , bgcolor = y_color)
        
        for mi = 0 to array.size(month_time) - 1
            m_row   = year(array.get(month_time, mi))  - year(array.get(year_time, 0)) + 1
            m_col   = month(array.get(month_time, mi)) 
            m_color = getCellColor(array.get(month_pnl, mi), array.get(month_bh, mi))
            cellText= str.tostring(math.round(array.get(month_pnl, mi) * 100,precision))
            if showMonthlyMarketPerformance
                cellText:= cellText + " (" + str.tostring(math.round(array.get(month_bh, mi) * 100,precision)) +")"
            table.cell(monthly_table, m_col, m_row, cellText, bgcolor = m_color)

