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

//@version=5

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

// @function Round a floating point value to a specified number of decimal places.
// @description This function takes a floating point value and rounds it to a specified number of decimal places.
// @param _value The floating point value to be rounded.
// @param _decimals The number of decimal places to round to. Must be a non-negative integer. OVERLOAD bring in a preselector format as string
// @returns The rounded value, as a string.
export round_to_Str(float _value, int _decimals) => 
    result = str.tostring(math.round(_value * math.pow(10, _decimals)) / math.pow(10, _decimals))
export round_to_Str(float _value, string _decimals_s) => 
    result = str.tostring(_value, _decimals_s)


export method removeDeleteAll(array<line> ln) => 
    int size = ln.size() 
    for i = size > 0 ? size - 1 : na to 0 
        ln.pop().delete()
export method removeDeleteAll(array<linefill> ln) => 
    int size = ln.size() 
    for i = size > 0 ? size - 1 : na to 0 
        ln.pop().delete()
export method removeDeleteAll(array<label> ln) => 
    int size = ln.size() 
    for i = size > 0 ? size - 1 : na to 0 
        ln.pop().delete()
export method removeDeleteAll(array<box> ln) => 
    int size = ln.size() 
    for i = size > 0 ? size - 1 : na to 0 
        ln.pop().delete()

// @function Delete all drawings on the chart.
// @description This function deletes all drawings on the chart, including lines, boxes, fills and labels.
// @returns None.
export clear_all() =>
    line.all.removeDeleteAll()
    label.all.removeDeleteAll()
    linefill.all.removeDeleteAll()
    box.all.removeDeleteAll()

// @function Create a string of spaces to shift text over by a specified amount.
// @description This function takes an integer value and returns a string consisting of that many spaces, which can be used to shift text over in a PineScript chart.
// @param _value The number of spaces to create in the output string.
// @returns A string consisting of the specified number of spaces.
export shifting(int _value) =>
    var string output = ""
    output := ""
    for i = 0 to _value by 1
        output := output + " "


// @function Convert a linear value to a logarithmic value.
// @description This function takes a linear value and converts it to a logarithmic value, using the formula specified in the code.
// @param value The linear value to be converted to a logarithmic value.
// @returns The corresponding logarithmic value, as a floating point number.
export fromLog(float _value) =>
	float       logical_offset          =   4
	float       coord_offset            =   0.0001
    float exponent = math.abs(_value)
    float product  = math.pow(10, exponent - logical_offset) - coord_offset
    float result   = exponent < 1e-8 ? 0 : _value < 0 ? - product : product


// @function Convert a logarithmic value to a linear value.
// @description This function takes a logarithmic value and converts it to a linear value, using the formula specified in the code.
// @param value The logarithmic value to be converted to a linear value.
// @returns The corresponding linear value, as a floating point number.
export toLog(float _value) =>
    float       logical_offset          =   4
	float       coord_offset            =   0.0001
    float exponent = math.abs(_value)
    float product  = math.log10(exponent + coord_offset) + logical_offset
    float result   = exponent < 1e-8 ? 0 : _value < 0 ? - product : product


// @function Calculate the time per bar on the chart.
// @description This function calculates the time per bar on the chart based on the first 100 bars. Usefull when we have exchanges with timegaps
// @returns The time per bar, as an integer value.
export f_getbartime() =>
    var int firstbartime = 0, var int lastbartime = 0
    var bool locked1 = false 
    var bool locked2 = false 
    var bool locked3 = false
    var int counter = 0
    var int difftime = 0
    var int timeperbar = na

    if locked1 == false
        firstbartime := time
        counter := 0
        locked1 := true
    if counter == 100 and locked2 == false
        lastbartime := time
        locked2 := true
    if locked3 == false and locked2 == true
        difftime := lastbartime - firstbartime
        timeperbar := difftime / counter 
        locked3 := true
    if locked3 != true
        counter += 1
    timeperbar


///// Fibonacci Box {
// @types for defining a fill group with corresponding color
export type type_fill
    int                                     partner_A       =       na
    int                                     partner_B       =       na
    color                                   fill_color      =       na

// @types for defining a fibonacci box
export type type_Fibonacci_box
    float                                   bottom_price    =       na
    float                                   top_price       =       na
    int                                     StartBar        =       na
    int                                     StopBar         =       na
    array<float>                            levels          =       na
    array<float>                            prices          =       na
    array<type_fill>                        fills           =       na    
    bool                                    ChartisLog      =       na
    bool                                    fibreverse      =       na
    color                                   fiblinecolor    =       na
    color                                   fibtextcolor    =       na
    string                                  fibtextsize     =       size.auto
    int                                     decimals_price  =       2
    int                                     decimals_percent =      2
    int                                     transp          =       na
    bool                                    drawlines       =       true
    bool                                    drawlabels      =       true
    bool                                    drawfills       =       true

// @function fibonacci calc.
// @description This function block uses the levels and paramters set into the type_fibonacci_box(levels) and fills the corresponding array of prices.
// @returns returns a type_Fibonacci_box with the filled data
export f_fib_calc(type_Fibonacci_box _Fibonacci_box) =>
    var type_Fibonacci_box _get_RW = _Fibonacci_box

    float Diff = _get_RW.top_price-_get_RW.bottom_price

    if not _get_RW.fibreverse
        for i = 0 to array.size(_get_RW.levels) -1
            if _get_RW.ChartisLog
                array.set(_get_RW.prices, i,  fromLog( toLog(_get_RW.bottom_price) + (toLog(_get_RW.top_price)-toLog(_get_RW.bottom_price)) * array.get(_get_RW.levels,i) ))
            else
                array.set(_get_RW.prices, i,  (_get_RW.bottom_price) + (Diff) * (array.get(_get_RW.levels,i)))
        _get_RW
    else
        for i =-(array.size(_get_RW.levels) -1) to 0
            if _get_RW.ChartisLog
                array.set(_get_RW.prices, -i,   fromLog( toLog(_get_RW.bottom_price) + (toLog(_get_RW.top_price)-toLog(_get_RW.bottom_price)) * array.get(_get_RW.levels,-i) ))
            else
                array.set(_get_RW.prices, -i,  (_get_RW.bottom_price) + (Diff) * (array.get(_get_RW.levels,-i)))
        _get_RW
    _get_RW


// @function fibonacci draw.
// @description This function block uses the levels, prices and paramters set into the type_fibonacci_box(levels) and draws the fib on the chart
// @returns returns lines labels and fills on the chart, no data returns
export f_fib_draw(type_Fibonacci_box _Fibonacci_box) =>
    var line [] line_array  = array.new_line(array.size(_Fibonacci_box.levels), line.new(x1 = na, y1 = na, x2 = na, y2 = na))
    var linefill [] linefill_array  = array.new_linefill(0, linefill.new(line1 = na, line2 = na, color = na))
    var label [] label_array  = array.new_label(0, label.new(x = na, y = na))

    line line_1 = na
    linefill fill_1 = na
    label label1 = na
    label label2 = na

    line_array.removeDeleteAll()
    label_array.removeDeleteAll()
    linefill_array.removeDeleteAll()

    for i = 0 to array.size(_Fibonacci_box.levels)-1

        line_array.push(line.new(x1= _Fibonacci_box.StartBar, y1= array.get(_Fibonacci_box.prices,i),
         x2 = _Fibonacci_box.StopBar, 
         y2 = array.get(_Fibonacci_box.prices,i), 
         xloc=xloc.bar_index, 
         style=line.style_solid, 
         extend=extend.none,
         color=_Fibonacci_box.drawlines?_Fibonacci_box.fiblinecolor:color.new(color.black,100)))

        if _Fibonacci_box.drawlabels
            label_array.push(label.new(x=_Fibonacci_box.StopBar+20, y= array.get(_Fibonacci_box.prices,i), 
             text = '       ' + str.tostring(_Fibonacci_box.fibreverse?(1-array.get(_Fibonacci_box.levels,i)):array.get(_Fibonacci_box.levels,i)) + ' (' + str.tostring(round_to_Str(array.get(_Fibonacci_box.prices,i),_Fibonacci_box.decimals_price)) + ')  ' + 
             str.tostring(round_to_Str(array.get(_Fibonacci_box.prices,i)/close*100-100,_Fibonacci_box.decimals_percent)) + '% = ' + str.tostring(round_to_Str(array.get(_Fibonacci_box.prices,i)-close,_Fibonacci_box.decimals_price)), 
             xloc=xloc.bar_index, 
             yloc=yloc.price, 
             color=_Fibonacci_box.fibtextcolor, 
             style=label.style_none, 
             textcolor=_Fibonacci_box.fibtextcolor, 
             size=_Fibonacci_box.fibtextsize, 
             textalign=text.align_left))

        if i == 8 and _Fibonacci_box.drawlabels
            label_array.push(label.new(x=_Fibonacci_box.StopBar+20, y= array.get(_Fibonacci_box.prices,i), 
             text = 'Range-Up     =   ' + str.tostring(round_to_Str(array.get(_Fibonacci_box.prices,0)/array.get(_Fibonacci_box.prices,8)*100-100,_Fibonacci_box.decimals_percent)) + '% / ' +
             str.tostring(round_to_Str(array.get(_Fibonacci_box.prices,0)-array.get(_Fibonacci_box.prices,8),_Fibonacci_box.decimals_price)) + 
             '\nRange-Down = ' + str.tostring(round_to_Str(array.get(_Fibonacci_box.prices,8)/array.get(_Fibonacci_box.prices,0)*100-100,_Fibonacci_box.decimals_percent)) + '%\n',
             xloc=xloc.bar_index, 
             yloc=yloc.price, 
             color=_Fibonacci_box.fibtextcolor, 
             style=label.style_none, 
             textcolor=_Fibonacci_box.fibtextcolor, 
             size=_Fibonacci_box.fibtextsize, 
             textalign=text.align_left))

    if _Fibonacci_box.drawfills
        for i_F = 0 to array.size(_Fibonacci_box.fills) - 1
            var type_fill _get_RO = na
            _get_RO := array.get(_Fibonacci_box.fills,i_F)
            for i_LA = 0 to (array.size(_Fibonacci_box.prices) - 1)
                if _get_RO.partner_A == i_LA and _get_RO.partner_B <= array.size(_Fibonacci_box.prices) and _get_RO.partner_B >= 0

                    linefill_array.push(linefill.new(line1 = array.get(line_array,i_LA), line2 = array.get(line_array,_get_RO.partner_B),color = _get_RO.fill_color))

///// Fibonacci Box }

//demo implementation {

var type_Fibonacci_box Fibonacci_box = type_Fibonacci_box.new()
Fibonacci_box.levels            :=          array.new<float>(0,na)
// 
Fibonacci_box.bottom_price      :=   ta.lowest(500)
Fibonacci_box.top_price         :=   ta.highest(500)
Fibonacci_box.StartBar          :=   bar_index -500
Fibonacci_box.StopBar           :=   bar_index

// 
Fibonacci_box.fibreverse        :=   input.bool(false,'Reverse Fib',group='Global', inline='11a')
Fibonacci_box.ChartisLog        :=   input.bool(false,'Logarithmic',group='Global',inline='11a')
Fibonacci_box.decimals_price    :=   input.int(2,'Price decimals',group='decimals trimming',inline='1')
Fibonacci_box.decimals_percent  :=   input.int(2,'Percent decimals',group='decimals trimming',inline='1')
// 
Fibonacci_box.fibtextcolor      :=   input.color(color.rgb(128, 128, 128),'Text color',group='Global',inline='1a')
Fibonacci_box.fibtextsize       :=   input.string(size.small,'size', options = [size.tiny,size.small,size.normal,size.large],group='Global',inline='1a')
Fibonacci_box.fiblinecolor      :=   input.color(color.rgb(126, 126, 126),'Linecolor',inline='1a')
Fibonacci_box.transp            :=   input.int(60,'Transparency fillings',group='Fillings')
Fibonacci_box.drawlines         :=   true
Fibonacci_box.drawfills         :=   true
Fibonacci_box.drawlabels        :=   true
// 
array.push(Fibonacci_box.levels,     input.float(1.0     ,'Level 0',group='Fiblevels 01',step=0.001))
array.push(Fibonacci_box.levels,     input.float(0.786    ,'Level 1',group='Fiblevels 01',step=0.001,inline='1a')) 
array.push(Fibonacci_box.levels,     input.float(0.65    ,'Level 2',group='Fiblevels 23',step=0.001))
array.push(Fibonacci_box.levels,     input.float(0.618   ,'Level 3',group='Fiblevels 23',step=0.001,inline='1b'))   
array.push(Fibonacci_box.levels,     input.float(0.5    ,'Level 4',group='Fiblevels 4',step=0.001))
array.push(Fibonacci_box.levels,     input.float(0.382    ,'Level 5',group='Fiblevels 56',step=0.001 ))
array.push(Fibonacci_box.levels,     input.float(0.35    ,'Level 6',group='Fiblevels 56',step=0.001,inline='1c'))
array.push(Fibonacci_box.levels,     input.float(0.236  ,'Level 7',group='Fiblevels 78',step=0.001 ))
array.push(Fibonacci_box.levels,     input.float(0.0    ,'Level 8',group='Fiblevels 78',step=0.001,inline='1d'))
// 
Fibonacci_box.prices            :=          array.new<float>(array.size(Fibonacci_box.levels),0)
Fibonacci_box.fills             :=          array.new<type_fill>(0, type_fill.new(partner_A = na, partner_B = na, fill_color = na))
// 
_1A = input(0, "partner1_A", group="Fill 1")
_1B = input(1, "partner1_B", group="Fill 1")
_1C = color.new(input.color(color.rgb(175, 76, 76, 0),'Fill-Color 0-1',group="Fill 1",inline='1a'),Fibonacci_box.transp)
_2A = input(2, "partner2_A", group="Fill 2")
_2B = input(3, "partner2_B", group="Fill 2")
_2C = color.new(input.color(color.rgb(175, 172, 76, 0),'Fill-Color 2-3',group="Fill 2",inline='1b'),Fibonacci_box.transp)
_3A = input(5, "partner3_A", group="Fill 3")
_3B = input(6, "partner3_B", group="Fill 3")
_3C = color.new(input.color(color.rgb(175, 172, 76, 0),'Fill-Color 5-6',group="Fill 3",inline='1c'),Fibonacci_box.transp)
_4A = input(7, "partner4_A", group="Fill 4")
_4B = input(8, "partner4_B", group="Fill 4")
_4C = color.new(input.color(color.rgb(76, 175, 79, 0),'Fill-Color 7-8',group="Fill 4",inline='1d'),Fibonacci_box.transp)
// 
array.push(Fibonacci_box.fills,(type_fill.new(partner_A = _1A, partner_B = _1B, fill_color = _1C)))
array.push(Fibonacci_box.fills,(type_fill.new(partner_A = _2A, partner_B = _2B, fill_color = _2C)))
array.push(Fibonacci_box.fills,(type_fill.new(partner_A = _3A, partner_B = _3B, fill_color = _3C)))
array.push(Fibonacci_box.fills,(type_fill.new(partner_A = _4A, partner_B = _4B, fill_color = _4C)))


Fibonacci_box := f_fib_calc(Fibonacci_box)
f_fib_draw(Fibonacci_box)

// }