Financial Data 8 Years
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © morzor61

// Reference Works
// https://www.tradingview.com/script/Lbxl9OqZ-Financials-on-Chart/
// https://www.tradingview.com/script/yRFF3z4J-Financials-Comparing-Companies/

//@version=5
indicator("Financial Data 8 Years", shorttitle = 'FD8Y', format = format.volume)
//【 ◉ Constants    】――――――――――――――――――――――――――――――――――――――――――――――――――――――{

// Financial Periods.
string FQ = 'FQ'
string FY = 'FY'
string TTM = 'TTM'

// Financial Legends. 
string F_NA = "⸺"
string F000 = "█ INCOME STATEMENTS █"
string F037 = " Total revenue"
string F004 = " Cost of goods"
string F015 = " Gross profit"
string F005 = " Deprecation and amortization"
string F018 = " Interest expense on debt"
string F034 = " Selling/general/admin expenses, total"
string F025 = " Operating income"
string F026 = " Operating expenses (excl. COGS)"
string F036 = " Total operating expenses"
string F013 = " EBITDA"
string F021 = " Net income"
string F010 = " Basic EPS"
string F100 = "█ BALANCE SHEET █"
string F155 = " Total assets"
string F156 = " Total current assets"
string F163 = " Total non-current assets"
string F161 = " Total liabilities"
string F157 = " Total current liabilities"
string F164 = " Total non-current liabilities"
string F159 = " Total equity"
string F162 = " Total liabilities & shareholders' equities"
string F158 = " Total debt"
string F152 = " Short term debt"
string F129 = " Long term debt"
string F101 = " Accounts payable"
string F102 = " Accounts receivable - trade, net"
string F160 = " Total inventory"
string F200 = "█ CASHFLOW █"
string F205 = " Cash from financing activities"
string F206 = " Cash from investing activities"
string F207 = " Cash from operating activities"
string F203 = " Capital expenditures"
string F219 = " Free cash flow"
string F300 = "█ STATISTICS █"
string F314 = " Debt to EBITDA ratio"
string F315 = " Debt to equity ratio"
string F319 = " Dividends per share - common stock primary issue"
string F323 = " EBITDA margin %"
string F335 = " Gross margin %"
string F347 = " Operating margin %"
string F344 = " Net margin %"
string F359 = " Return on equity %"
string F357 = " Return on assets %"
string F337 = " Interest coverage"
string F339 = " Inventory turnover"
string F400 = "█ CALCULATED █"
string F401 = " Market Capitalization"
string F402 = " Earnings Yield"
string F403 = " Price Book Ratio"
string F404 = " Price Earnings Ratio"
string F405 = " Price-To-Sales Ratio"


// --------------------------------------

var fqTimestamp = array.new_int()
var fyTimestamp = array.new_int()

var fqData1 = array.new_float()

var fyData1 = array.new_float()
var fyData2 = array.new_float()
var fyData3 = array.new_float()
var fyData4 = array.new_float()

// Create Year List
yearList = array.new_string()

// }

if timeframe.in_seconds(timeframe.period) < 86400
    runtime.error("Please set the time frame 1Day or higher")

//【 ◉ Inputs       】――――――――――――――――――――――――――――――――――――――――――――――――――――――{
//
numOfYear = input.int(defval = 5, title = 'Number of Year', minval = 1, maxval = 9)

AnnualGroup = "Annual Data"
QuarterlyGroup = "Quarterly Data"

inputFyPeriod = input.string(FY, title = 'Period', options = [FY, FQ, TTM], group = AnnualGroup)
inputFqPeriod = input.string(FQ, title = 'Period', options = [FY, FQ, TTM], group = QuarterlyGroup)

fyItem1 = input.string(F037, title = 'Annual Data 1', options =[
     F_NA, F000, F037, F004, F015, F005, F018, F034, F025, F026, F036, 
     F162, F158, F152, F129, F101, F160, F200, F205, F206, F207, F203, F219, F300, 
     F314, F315, F319, F323, F335, F347, F344, F359, F357, F337, F339, 
     F400, F401, F402, F403, F404, F405] , group = AnnualGroup)
fyItem2 = input.string(F021, title = 'Annual Data 2', options =[
     F_NA, F000, F037, F004, F015, F005, F018, F034, F025, F026, F036, 
     F013, F021, F010, F100, F155, F156, F163, F161, F157, F164, F159, 
     F162, F158, F152, F129, F101, F160, F200, F205, F206, F207, F203, F219, F300, 
     F314, F315, F319, F323, F335, F347, F344, F359, F357, F337, F339, 
     F400, F401, F402, F403, F404, F405], group = AnnualGroup)
fyItem3 = input.string(F013, title = 'Annual Data 3', options =[
     F_NA, F000, F037, F004, F015, F005, F018, F034, F025, F026, F036, 
     F013, F021, F010, F100, F155, F156, F163, F161, F157, F164, F159, 
     F162, F158, F152, F129, F101, F160, F200, F205, F206, F207, F203, F219, F300, 
     F314, F315, F319, F323, F335, F347, F344, F359, F357, F337, F339, 
     F400, F401, F402, F403, F404, F405], group = AnnualGroup)
fyItem4 = input.string(F010, title = 'Annual Data 4', options =[
     F_NA, F000, F037, F004, F015, F005, F018, F034, F025, F026, F036, 
     F013, F021, F010, F100, F155, F156, F163, F161, F157, F164, F159, 
     F162, F158, F152, F129, F101, F160, F200, F205, F206, F207, F203, F219, F300, 
     F314, F315, F319, F323, F335, F347, F344, F359, F357, F337, F339, 
     F400, F401, F402, F403, F404, F405], group = AnnualGroup)

//
refFq = request.financial(syminfo.tickerid,"total_revenue", FQ, barmerge.gaps_on, ignore_invalid_symbol = true)
refFy = request.financial(syminfo.tickerid,"total_revenue", FY, barmerge.gaps_on, ignore_invalid_symbol = true)



tablePosY = input.string(defval = 'middle', title = 'Table Position', options = ['bottom', 'middle', 'top'], inline = 'table position'   )
tablePosX = input.string(defval = 'left',   title = '', options = ['left', 'center', 'right'], inline = 'table position'   )

txtSize = input.string(defval = size.small, title = "Text Size", options = [size.tiny, size.small, size.normal, size.large])
dataDivider = input.float(defval = 1000000, title = "Million Divider")
// }
//
//【 ◉ Funtions     】――――――――――――――――――――――――――――――――――――――――――――――――――――――{

getId(simple string financialItem) =>

    string result = switch financialItem

        F004 => "COST_OF_GOODS" 
        F005 => "DEP_AMORT_EXP_INCOME_S" 

        F010 => "EARNINGS_PER_SHARE_BASIC" 

        F013 => "EBITDA" 

        F015 => "GROSS_PROFIT" 

        F018 => "INTEREST_EXPENSE_ON_DEBT" 

        F021 => "NET_INCOME" 

        F025 => "OPER_INCOME" 
        F026 => "OPERATING_EXPENSES" 
 
        F034 => "SELL_GEN_ADMIN_EXP_TOTAL" 

        F036 => "TOTAL_OPER_EXPENSE" 
        F037 => "TOTAL_REVENUE" 

        F101 => "ACCOUNTS_PAYABLE" 
        F102 => "ACCOUNTS_RECEIVABLES_NET" 
 
        F129 => "LONG_TERM_DEBT" 
       
        F152 => "SHORT_TERM_DEBT" 
        F155 => "TOTAL_ASSETS" 
        F156 => "TOTAL_CURRENT_ASSETS" 
        F157 => "TOTAL_CURRENT_LIABILITIES" 
        F158 => "TOTAL_DEBT" 
        F159 => "TOTAL_EQUITY" 
        F160 => "TOTAL_INVENTORY" 
        F161 => "TOTAL_LIABILITIES" 
        F162 => "TOTAL_LIABILITIES_SHRHLDRS_EQUITY" 
        F163 => "TOTAL_NON_CURRENT_ASSETS" 
        F164 => "TOTAL_NON_CURRENT_LIABILITIES" 

        F203 => "CAPITAL_EXPENDITURES" 

        F205 => "CASHffINANCING_ACTIVITIES" 
        F206 => "CASHf_INVESTING_ACTIVITIES" 
        F207 => "CASHf_OPERATING_ACTIVITIES" 
        F219 => "FREE_CASHfLOW" 
    
        F314 => "DEBT_TO_EBITDA" 
        F315 => "DEBT_TO_EQUITY" 

        F319 => "DPS_COMMON_STOCK_PRIM_ISSUE" 
 
        F323 => "EBITDA_MARGIN" 


        F335 => "GROSS_MARGIN" 

        F337 => "INTERST_COVER" 

        F339 => "INVENT_TURNOVER" 

        F344 => "NET_MARGIN" 

        F347 => "OPERATING_MARGIN" 

        F357 => "RETURN_ON_ASSETS" 
        F359 => "RETURN_ON_EQUITY" 
        =>      ""
//
getFinancialData(item, period)=>
    data = request.financial(syminfo.tickerid,getId(item), period,barmerge.gaps_off, ignore_invalid_symbol = true)
//

getDivider(item)=> 
    bool singleDigit = (
         item == F010 or item == F314 or item == F315 or item == F319 or item == F323 or item == F335 or item == F347 or item == F344 or item == F359 or item == F357 or item == F337 or item == F339 or item == F400 or item == F401 or item == F402 or item == F403 or item == F404 or item == F405)
    divider = singleDigit ? 1 : dataDivider

getSuffix(item)=> 
    div = getDivider(item)
    len = str.tostring(div)
    numberOfZero = str.length(len)-1

    string suffix = na
    if numberOfZero == 6
        suffix := ",M"
    else if numberOfZero == 9
        suffix := ",B"
    else if numberOfZero == 12
        suffix := ",T"
    else if numberOfZero == 3
        suffix := ",K"
    else
        suffix := ""
//} 
//
//【 ◉ Calculation  】――――――――――――――――――――――――――――――――――――――――――――――――――――――{

if refFq
    array.unshift(fqTimestamp, time)
    array.unshift(fqData1, getFinancialData(fyItem4, inputFqPeriod )/getDivider(fyItem4))

if refFy
    array.unshift(fyTimestamp, time)
    array.unshift(fyData1, getFinancialData(fyItem1, inputFyPeriod )/getDivider(fyItem1))
    array.unshift(fyData2, getFinancialData(fyItem2, inputFyPeriod )/getDivider(fyItem2))
    array.unshift(fyData3, getFinancialData(fyItem3, inputFyPeriod )/getDivider(fyItem3))
    array.unshift(fyData4, getFinancialData(fyItem4, inputFyPeriod )/getDivider(fyItem4))

if barstate.islast
    len = array.size(fqTimestamp)
    string newYear = na
    for i = 0 to len-1
        yearText = str.format_time(array.get(fqTimestamp, i), 'yyyy')
        if yearText != newYear
            array.unshift(yearList, yearText)
            newYear := yearText

int fqArraySize = na
int fyArraySize = na 
int yearArraySize = na
if barstate.islast
    fqArraySize := array.size(fqTimestamp)
    fyArraySize := array.size(fyTimestamp)
    yearArraySize := array.size(yearList)

//} 
//
//【 ◉ Dispaly      】――――――――――――――――――――――――――――――――――――――――――――――――――――――{

var table result = na
if barstate.islast

    // Annual Data
    fyDataSize =     array.size(fyTimestamp)

    if numOfYear-1 >= yearArraySize
        runtime.error("ERROR-01-FY : Pls set numOfYear to "+ str.tostring(yearArraySize))
    
    if fyArraySize == 0
        runtime.error('ERROR-02-FY : No data')

    if fqArraySize == 0 
        runtime.error('ERROR-03-FQ : No data')
    
    if yearArraySize == 0
        runtime.error('ERROR-04-Year List : No data')

    result := table.new(position = tablePosY+'_'+ tablePosX, columns =numOfYear+1, rows = 15, bgcolor = color.white, border_color = color.rgb(147, 149, 153), frame_color = color.rgb(179, 173, 173), border_width = 1 ,frame_width = 2)
    table.cell(result,0, 0, 'Period', text_size = txtSize, text_color = color.rgb(10, 250, 18), bgcolor = color.rgb(55, 55, 58))
    table.cell(result,0, 2, fyItem1+getSuffix(fyItem1),  text_size = txtSize)
    table.cell(result,0, 3, fyItem2+getSuffix(fyItem2),  text_size = txtSize)
    table.cell(result,0, 4, fyItem3+getSuffix(fyItem3),  text_size = txtSize)
    table.cell(result,0, 5, fyItem4+getSuffix(fyItem4),  text_size = txtSize)

    dataSize = array.size(yearList)

    for i = 0 to numOfYear-1 
        // For loop for to create column header  
        // from left to right
        table.cell(result, i+1, 0, array.get(yearList, dataSize-i-1), text_size = txtSize, text_color = color.rgb(10, 250, 18), bgcolor = color.rgb(55, 55, 58))

        xYear = array.get(yearList, dataSize-i-1)
        for y=0 to array.size(fyData1)-1
            if str.format_time(array.get(fyTimestamp, y), format = 'yyyy') == xYear

                table.cell(result,i+1, 2, str.tostring(array.get(fyData1, y),'#,##0.00'), text_size = txtSize, bgcolor = array.get(fyData1, y) < 0 ? color.rgb(255, 82, 82, 50) : na ,text_halign = text.align_right)
                table.cell(result,i+1, 3, str.tostring(array.get(fyData2, y),'#,##0.00'), text_size = txtSize, bgcolor = array.get(fyData2, y) < 0 ? color.rgb(255, 82, 82, 50) : na ,text_halign = text.align_right)
                table.cell(result,i+1, 4, str.tostring(array.get(fyData3, y),'#,##0.00'), text_size = txtSize, bgcolor = array.get(fyData3, y) < 0 ? color.rgb(255, 82, 82, 50) : na ,text_halign = text.align_right)
                table.cell(result,i+1, 5, str.tostring(array.get(fyData4, y),'#,##0.00'), text_size = txtSize, bgcolor = array.get(fyData4, y) < 0 ? color.rgb(255, 82, 82, 50) : na ,text_halign = text.align_right)
    
    // // Quarter Data
    for i = 0 to numOfYear
        // Create blank row.
        table.cell(result, i, 1, '', height = 0.1, text_size = txtSize, bgcolor = color.rgb(2, 2, 2)) // create blank row.
        table.cell(result,i, 6,  '', height = 0.1, text_size = txtSize, bgcolor = color.rgb(2, 2, 2))
        table.cell(result,i, 11, '', height = 0.1, text_size = txtSize, bgcolor = color.rgb(0, 0, 0))

    for i = 1 to 4 
        // Create Quarter Label 
        // Top to bottom
        table.cell(result,0, 6+i, 'Q'+str.tostring(5-i), text_size = txtSize)

    string yearInFq = na
    string monthInFq = na
    for z = 0 to numOfYear-1
        refYear = array.get(yearList, dataSize-z-1)

        for i = 0 to array.size(fqTimestamp)-1
            yearInFq := str.format_time(array.get(fqTimestamp, i), 'yyyy')
            monthInFq := str.format_time(array.get(fqTimestamp, i), 'MMM')

            if yearInFq == refYear
                if monthInFq == 'Dec'
                    table.cell(result, z+1, 7, str.tostring(array.get(fqData1, i), '#,##0.00'), bgcolor = array.get(fqData1, i) < 0 ? color.rgb(255, 82, 82, 50) : na, text_size = txtSize)
                if monthInFq == 'Sep'
                    table.cell(result, z+1, 8, str.tostring(array.get(fqData1, i), '#,##0.00'), bgcolor = array.get(fqData1, i) < 0 ? color.rgb(255, 82, 82, 50) : na,text_size = txtSize)
                if monthInFq == 'Jun'
                    table.cell(result, z+1, 9, str.tostring(array.get(fqData1, i), '#,##0.00'), bgcolor = array.get(fqData1, i) < 0 ? color.rgb(255, 82, 82, 50) : na,text_size = txtSize)
                if monthInFq == 'Mar'
                    table.cell(result, z+1, 10, str.tostring(array.get(fqData1, i), '#,##0.00'), bgcolor = array.get(fqData1, i) < 0 ? color.rgb(255, 82, 82, 50) : na,text_size = txtSize)
    
//}

  

