Percentile - Price vs Fundamentals
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HeWhoMustNotBeNamed

//   __    __            __       __  __                  __       __                        __      __    __              __      _______             __    __                                          __ 
//  /  |  /  |          /  |  _  /  |/  |                /  \     /  |                      /  |    /  \  /  |            /  |    /       \           /  \  /  |                                        /  |
//  $$ |  $$ |  ______  $$ | / \ $$ |$$ |____    ______  $$  \   /$$ | __    __   _______  _$$ |_   $$  \ $$ |  ______   _$$ |_   $$$$$$$  |  ______  $$  \ $$ |  ______   _____  ____    ______    ____$$ |
//  $$ |__$$ | /      \ $$ |/$  \$$ |$$      \  /      \ $$$  \ /$$$ |/  |  /  | /       |/ $$   |  $$$  \$$ | /      \ / $$   |  $$ |__$$ | /      \ $$$  \$$ | /      \ /     \/    \  /      \  /    $$ |
//  $$    $$ |/$$$$$$  |$$ /$$$  $$ |$$$$$$$  |/$$$$$$  |$$$$  /$$$$ |$$ |  $$ |/$$$$$$$/ $$$$$$/   $$$$  $$ |/$$$$$$  |$$$$$$/   $$    $$< /$$$$$$  |$$$$  $$ | $$$$$$  |$$$$$$ $$$$  |/$$$$$$  |/$$$$$$$ |
//  $$$$$$$$ |$$    $$ |$$ $$/$$ $$ |$$ |  $$ |$$ |  $$ |$$ $$ $$/$$ |$$ |  $$ |$$      \   $$ | __ $$ $$ $$ |$$ |  $$ |  $$ | __ $$$$$$$  |$$    $$ |$$ $$ $$ | /    $$ |$$ | $$ | $$ |$$    $$ |$$ |  $$ |
//  $$ |  $$ |$$$$$$$$/ $$$$/  $$$$ |$$ |  $$ |$$ \__$$ |$$ |$$$/ $$ |$$ \__$$ | $$$$$$  |  $$ |/  |$$ |$$$$ |$$ \__$$ |  $$ |/  |$$ |__$$ |$$$$$$$$/ $$ |$$$$ |/$$$$$$$ |$$ | $$ | $$ |$$$$$$$$/ $$ \__$$ |
//  $$ |  $$ |$$       |$$$/    $$$ |$$ |  $$ |$$    $$/ $$ | $/  $$ |$$    $$/ /     $$/   $$  $$/ $$ | $$$ |$$    $$/   $$  $$/ $$    $$/ $$       |$$ | $$$ |$$    $$ |$$ | $$ | $$ |$$       |$$    $$ |
//  $$/   $$/  $$$$$$$/ $$/      $$/ $$/   $$/  $$$$$$/  $$/      $$/  $$$$$$/  $$$$$$$/     $$$$/  $$/   $$/  $$$$$$/     $$$$/  $$$$$$$/   $$$$$$$/ $$/   $$/  $$$$$$$/ $$/  $$/  $$/  $$$$$$$/  $$$$$$$/ 
//                                                                                                                                                                                                          
//                                                                                                                                                                                                          
//
//@version=5
indicator('Percentile - Price vs Fundamentals')
//################################### INPUT ################################### //

i_numberOfRanges = input.int(100, title='Number of ranges', options=[5, 10, 20, 25, 50, 100, 200, 500, 1000], group='Main')
i_percentile = true

i_reference = input.string('ath', title='Reference', options=['ath', 'window'], group='Reference')
i_startTime = input.time(defval=timestamp('01 Jan 2010 00:00 +0000'), title='Start Time', group='Reference')
i_endTime = input.time(defval=timestamp('01 Jan 2099 00:00 +0000'), title='End Time', group='Reference')
inDateRange = time >= i_startTime and time <= i_endTime

i_displayHeader = input.bool(true, 'Display Header', group='Display', inline='text')
i_text_size = input.string(size.normal, title='', options=[size.tiny, size.small, size.normal, size.large, size.huge], group='Display', inline='text')

//################################### FUNDAMENTALS ################################### //
f_getFinancials(financial_id, period) =>
    request.financial(syminfo.tickerid, financial_id, period)
f_getOptimalFinancialBasic(financial_id) =>
    f_getFinancials(financial_id, syminfo.currency == 'USD' ? 'FQ' : 'FY')

tso = request.financial(syminfo.tickerid, 'TOTAL_SHARES_OUTSTANDING', 'FQ')
bvps = request.financial(syminfo.tickerid, 'BOOK_VALUE_PER_SHARE', 'FQ')
eps = request.financial(syminfo.tickerid, 'EARNINGS_PER_SHARE_BASIC', 'TTM')
rps = request.financial(syminfo.tickerid, 'TOTAL_REVENUE', 'TTM') / tso

ebitda = request.financial(syminfo.tickerid, 'EBITDA', 'TTM') / tso
ebit = f_getOptimalFinancialBasic('EBIT') / tso
fcf = f_getOptimalFinancialBasic('FREE_CASH_FLOW') / tso
//currentassets = f_getOptimalFinancialBasic("NCAVPS_RATIO")

nmargin = f_getOptimalFinancialBasic('NET_MARGIN')
gmargin = f_getOptimalFinancialBasic('GROSS_MARGIN')
omargin = f_getOptimalFinancialBasic('OPERATING_MARGIN')
emargin = f_getOptimalFinancialBasic('EBITDA_MARGIN')
fmargin = f_getOptimalFinancialBasic('FREE_CASH_FLOW_MARGIN')

roa = f_getOptimalFinancialBasic('RETURN_ON_ASSETS')
roe = f_getOptimalFinancialBasic('RETURN_ON_EQUITY')
roic = f_getOptimalFinancialBasic('RETURN_ON_INVESTED_CAPITAL')

ldta = 1 / f_getOptimalFinancialBasic('LONG_TERM_DEBT_TO_ASSETS')
dta = 1 / f_getOptimalFinancialBasic('DEBT_TO_ASSET')
dte = 1 / f_getOptimalFinancialBasic('DEBT_TO_EQUITY')
dtr = 1 / f_getOptimalFinancialBasic('DEBT_TO_REVENUE')
dteb = 1 / f_getOptimalFinancialBasic('DEBT_TO_EBITDA')

//################################### Random color generator ################################### //
// ************** This part of the code is taken from // © RicardoSantos
// https://www.tradingview.com/script/4jdGt6Xo-Function-HSL-color/
f_color_hsl(_hue, _saturation, _lightness, _transparency) =>  //{
//  @function: returns HSL color.
//  @reference: https://stackoverflow.com/questions/36721830/convert-hsl-to-rgb-and-hex
    float _l1 = _lightness / 100.0
    float _a = _saturation * math.min(_l1, 1.0 - _l1) / 100.0
    float _rk = (0.0 + _hue / 30.0) % 12.0
    float _r = 255.0 * (_l1 - _a * math.max(math.min(_rk - 3.0, 9.0 - _rk, 1.0), -1.0))
    float _gk = (8.0 + _hue / 30.0) % 12.0
    float _g = 255.0 * (_l1 - _a * math.max(math.min(_gk - 3.0, 9.0 - _gk, 1.0), -1.0))
    float _bk = (4.0 + _hue / 30.0) % 12.0
    float _b = 255.0 * (_l1 - _a * math.max(math.min(_bk - 3.0, 9.0 - _bk, 1.0), -1.0))
    color.rgb(_r, _g, _b, _transparency)
//}
// ************** End of borrowed code

f_random_color() =>
    f_color_hsl(math.random(0, 255), math.random(70, 100), math.random(30, 70), 0)

//################################### CONSTANTS AND ARRAYS ################################### //
var CELLRANGE = 100 / i_numberOfRanges
var NUMBER_OF_ELEMENTS = 20
var values = array.new_float(NUMBER_OF_ELEMENTS, 0)
array.set(values, 0, high)
array.set(values, 1, bvps)
array.set(values, 2, eps)
array.set(values, 3, rps)
array.set(values, 4, fcf)
array.set(values, 5, ebit)
array.set(values, 6, ebitda)
array.set(values, 7, nmargin)
array.set(values, 8, gmargin)
array.set(values, 9, omargin)
array.set(values, 10, emargin)
array.set(values, 11, fmargin)
array.set(values, 12, roa)
array.set(values, 13, roe)
array.set(values, 14, roic)
array.set(values, 15, ldta)
array.set(values, 16, dta)
array.set(values, 17, dte)
array.set(values, 18, dtr)
array.set(values, 19, dteb)

var colors = array.new_color(NUMBER_OF_ELEMENTS)
if barstate.isfirst
    for _i = 0 to NUMBER_OF_ELEMENTS - 1 by 1
        array.set(colors, _i, f_random_color())
var drawdownRange = array.new_int(i_numberOfRanges * NUMBER_OF_ELEMENTS, 0)
var athArray = array.new_float(NUMBER_OF_ELEMENTS, 0.0)
var drawdownArray = array.new_float(NUMBER_OF_ELEMENTS, 0.0)
var percentileArray = array.new_float(NUMBER_OF_ELEMENTS, 0.0)

//################################### LOGIC AND CALCULATION ################################### //
f_calculate_percentile_drawdown(values, drawdownRange, athArray, drawdownArray, percentileArray, i) =>
    _ath = array.get(athArray, i)
    _value = array.get(values, i)
    _ath := _value > _ath ? _value : _ath
    _drawdown = math.round(math.abs(_ath - _value) / math.abs(_ath) * 100)
    _startIndex = i * i_numberOfRanges
    _endIndex = (i + 1) * i_numberOfRanges
    _index = _startIndex + math.min(math.max(math.round(_drawdown / CELLRANGE), 0), i_numberOfRanges - 1)
    array.set(drawdownRange, _index, array.get(drawdownRange, _index) + 1)
    _before = array.slice(drawdownRange, _index, _endIndex)
    _full = array.slice(drawdownRange, _startIndex, _endIndex)
    _percentile = math.round(array.sum(_before) * 100 / array.sum(_full), 2)

    array.set(athArray, i, _ath)
    array.set(drawdownArray, i, _drawdown)
    array.set(percentileArray, i, _percentile)

if i_reference == 'ath' or inDateRange
    if i_displayHeader
        var dateStart = str.tostring(year) + '/' + str.tostring(month) + '/' + str.tostring(dayofmonth)
        dateEnd = str.tostring(year) + '/' + str.tostring(month) + '/' + str.tostring(dayofmonth)
        DISPLAY = table.new(position=position.top_center, columns=2, rows=1, border_width=1)
        table.cell(table_id=DISPLAY, column=0, row=0, text=syminfo.ticker, bgcolor=color.teal, text_color=color.white, text_size=i_text_size)
        table.cell(table_id=DISPLAY, column=1, row=0, text=dateStart + ' to ' + dateEnd, bgcolor=color.teal, text_color=color.white, text_size=i_text_size)
    for i = 0 to NUMBER_OF_ELEMENTS - 1 by 1
        f_calculate_percentile_drawdown(values, drawdownRange, athArray, drawdownArray, percentileArray, i)

//################################### PLOT ################################### //
plot(i_percentile ? array.get(percentileArray, 0) : -array.get(drawdownArray, 0), color=array.get(colors, 0), title='Price', linewidth=2)
plot(i_percentile ? array.get(percentileArray, 1) : -array.get(drawdownArray, 1), color=array.get(colors, 1), title='Book Value', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 2) : -array.get(drawdownArray, 2), color=array.get(colors, 2), title='Earnings', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 3) : -array.get(drawdownArray, 3), color=array.get(colors, 3), title='Revenue', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 4) : -array.get(drawdownArray, 4), color=array.get(colors, 4), title='Free Cashflow', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 5) : -array.get(drawdownArray, 5), color=array.get(colors, 5), title='EBIT', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 6) : -array.get(drawdownArray, 6), color=array.get(colors, 6), title='EBITDA', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 7) : -array.get(drawdownArray, 7), color=array.get(colors, 7), title='Net Margin', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 8) : -array.get(drawdownArray, 8), color=array.get(colors, 8), title='Gross Margin', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 9) : -array.get(drawdownArray, 9), color=array.get(colors, 9), title='Operating Margin', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 10) : -array.get(drawdownArray, 10), color=array.get(colors, 10), title='EBITDA Margin', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 11) : -array.get(drawdownArray, 11), color=array.get(colors, 11), title='Freecashflow Margin', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 12) : -array.get(drawdownArray, 12), color=array.get(colors, 12), title='ROA', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 13) : -array.get(drawdownArray, 13), color=array.get(colors, 13), title='ROE', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 14) : -array.get(drawdownArray, 14), color=array.get(colors, 14), title='ROIC', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 15) : -array.get(drawdownArray, 15), color=array.get(colors, 15), title='Long Debt/Assets', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 16) : -array.get(drawdownArray, 16), color=array.get(colors, 16), title='Debt/Assets', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 17) : -array.get(drawdownArray, 17), color=array.get(colors, 17), title='Debt/Equity', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 18) : -array.get(drawdownArray, 18), color=array.get(colors, 18), title='Debt/Revenue', linewidth=0)
plot(i_percentile ? array.get(percentileArray, 19) : -array.get(drawdownArray, 19), color=array.get(colors, 19), title='Debt/EBITDA', linewidth=0)

