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

//@version=5

// @description TODO: add library description here
library("PerformanceTable")

// Global Function
tableTextSize = size.small

truncate(number, decimals) =>
    factor = math.pow(10, decimals)
    int(number * factor) / factor

f_fillCell(_table, _column, _row, _title, _value, _bgcolor, _txtcolor) =>
    _cellText = _title + "\n" + _value
    table.cell(_table, _column, _row, _cellText, bgcolor=_bgcolor, text_color=_txtcolor, text_size=tableTextSize)

// Performance Table
export PerformanceTable() =>
	var bgcolor = color.new(color.black,0)
		
	// Keep track of Wins/Losses streaks
	newWin  = (strategy.wintrades  > strategy.wintrades[1]) and (strategy.losstrades == strategy.losstrades[1]) and (strategy.eventrades == strategy.eventrades[1])
	newLoss = (strategy.wintrades == strategy.wintrades[1]) and (strategy.losstrades  > strategy.losstrades[1]) and (strategy.eventrades == strategy.eventrades[1])

	varip int winRow     = 0
	varip int lossRow    = 0
	varip int maxWinRow  = 0
	varip int maxLossRow = 0

	if newWin
		lossRow := 0
		winRow := winRow + 1
	if winRow > maxWinRow
		maxWinRow := winRow
		
	if newLoss
		winRow := 0
		lossRow := lossRow + 1
	if lossRow > maxLossRow
		maxLossRow := lossRow

	// Prepare stats table
	var table dashTable = table.new(position.top_right, 2, 15, border_width=1)

	if barstate.islastconfirmedhistory
		// Update table
		_positionsize   = truncate(strategy.position_size, 5)
		_coinvalue      = truncate(math.abs(strategy.position_size*close), 2)
		_pnl            = truncate(strategy.openprofit, 2)
		_ROE            = truncate(strategy.openprofit / math.abs(strategy.position_size) / strategy.position_avg_price * 100, 2)
		_equity         = truncate(strategy.equity, 2)
		_leverage       = truncate(math.abs(strategy.position_size*open/strategy.equity), 2)
		dollarReturn = strategy.netprofit
		_end = strategy.position_size == 0 ? strategy.closedtrades.exit_time(strategy.closedtrades-1) : time
		_profit = (strategy.netprofit / strategy.initial_capital) * 100
		_numOfDaysInStrategy = truncate((strategy.opentrades.entry_time(0) - strategy.closedtrades.entry_time(0)) / (1000 * 3600 * 24),2)
		_winRate = (strategy.wintrades / strategy.closedtrades ) * 100
		_profitFactor = strategy.grossprofit / strategy.grossloss
		_maxdd = (strategy.max_drawdown / strategy.equity) * 100

		f_fillCell(dashTable, 0, 0, "Total Trades:", str.tostring(strategy.closedtrades), bgcolor, color.white)
		f_fillCell(dashTable, 0, 1, "Max.Win / Max.Loss", str.tostring(maxWinRow) + " / " + str.tostring(maxLossRow) , bgcolor, color.white)
		f_fillCell(dashTable, 0, 2, "Equity:", str.tostring(_equity) , bgcolor, color.white)
		f_fillCell(dashTable, 0, 3, "Leverage:", str.tostring(_leverage) +"x", bgcolor, color.white)
		f_fillCell(dashTable, 0, 4, "Coin Value:", str.tostring(_coinvalue), bgcolor, color.white)
		f_fillCell(dashTable, 0, 5, "PnL: "+ str.tostring(_pnl), "(" + str.tostring(_ROE) + "%)" , _pnl > 0 ? color.rgb(56, 173, 59) : _pnl < 0 ? color.red : bgcolor, color.white)
		f_fillCell(dashTable, 0, 6, "Position Size:", str.tostring(_positionsize) , _positionsize > 0 ? color.rgb(56, 173, 59) : _positionsize < 0 ? color.red : bgcolor, color.white)

		f_fillCell(dashTable, 1, 0, "Start:", str.format("{0,date,y-MM-dd}", strategy.closedtrades.entry_time(0)) , bgcolor, color.white) // + str.format(" {0,time,HH:mm}", strategy.closedtrades.entry_time(0)) 
		f_fillCell(dashTable, 1, 1, "End:", str.format("{0,date,y-MM-dd}", _end) , bgcolor, color.white) // + str.format(" {0,time,HH:mm}", strategy.opentrades.entry_time(0))
		f_fillCell(dashTable, 1, 2, "Net Profit %:", str.tostring(_profit, '##.##') + "%", _profit > 0 ? color.teal : color.maroon, color.white)
		f_fillCell(dashTable, 1, 3, "% Per Day", str.tostring(_profit / _numOfDaysInStrategy, '################.##')+"%", _profit > 0 ? color.teal : color.maroon, color.white)
		f_fillCell(dashTable, 1, 4, "% Profitable:", str.tostring(_winRate, '##.##') + "%", _winRate < 50 ? color.maroon : _winRate < 75 ? #999900 : color.teal, color.white)
		f_fillCell(dashTable, 1, 5, "Profit Factor:", str.tostring(_profitFactor, '##.##'), strategy.grossprofit > strategy.grossloss ? color.teal : color.maroon, color.white)
		f_fillCell(dashTable, 1, 6, "MaxDD:", str.tostring(_maxdd, '###.##') + "%", color.red, color.white)

// Monthly Table
export MonthlyTable() =>

    new_month = month(time) != month(time[1])
    new_year  = year(time)  != year(time[1])
    
    eq = strategy.equity
    
    bar_pnl = eq / eq[1] - 1
    
    cur_month_pnl = 0.0
    cur_year_pnl  = 0.0
    
    // Current Monthly P&L
    cur_month_pnl := new_month ? 0.0 : 
                     (1 + cur_month_pnl[1]) * (1 + bar_pnl) - 1 
    
    // Current Yearly P&L
    cur_year_pnl := new_year ? 0.0 : 
                     (1 + cur_year_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)
    
    var year_pnl  = array.new_float(0)
    var year_time = array.new_int(0)
    
    last_computed = false
    
    if (not na(cur_month_pnl[1]) and (new_month or barstate.islastconfirmedhistory))
        if (last_computed[1])
            array.pop(month_pnl)
            array.pop(month_time)
            
        array.push(month_pnl , cur_month_pnl[1])
        array.push(month_time, time[1])
    
    if (not na(cur_year_pnl[1]) and (new_year or barstate.islastconfirmedhistory))
        if (last_computed[1])
            array.pop(year_pnl)
            array.pop(year_time)
            
        array.push(year_pnl , cur_year_pnl[1])
        array.push(year_time, time[1])
    
    last_computed := barstate.islastconfirmedhistory ? true : nz(last_computed[1])
    
    // Monthly P&L Table    
    var monthly_table = table(na)
    
    if (barstate.islastconfirmedhistory)
        monthly_table := table.new(position.bottom_right, columns = 14, rows = array.size(year_pnl) + 1, border_width = 1)
    
        table.cell(monthly_table, 0,  0, "",     bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 1,  0, "Jan",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 2,  0, "Feb",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 3,  0, "Mar",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 4,  0, "Apr",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 5,  0, "May",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 6,  0, "Jun",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 7,  0, "Jul",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 8,  0, "Aug",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 9,  0, "Sep",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 10, 0, "Oct",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 11, 0, "Nov",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 12, 0, "Dec",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 13, 0, "Year", bgcolor = #999999, text_size=tableTextSize)
    
        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, text_size=tableTextSize)
            
            y_color = array.get(year_pnl, yi) > 0 ? color.new(color.teal, transp = 40) : color.new(color.gray, transp = 40)
            table.cell(monthly_table, 13, yi + 1, str.tostring(math.round(array.get(year_pnl, yi) * 100, 2)), bgcolor = y_color, text_color=color.new(color.white, 0),text_size=tableTextSize)
            
        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 = array.get(month_pnl, mi) > 0 ? color.new(color.teal, transp = 40) : color.new(color.maroon, transp = 40)
            
            table.cell(monthly_table, m_col, m_row, str.tostring(math.round(array.get(month_pnl, mi) * 100, 2)), bgcolor = m_color, text_color=color.new(color.white, 0), text_size=tableTextSize)
