[pp] Fib Alerts
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © pAulseperformance

// import pAulseperformance/webhook3commas/1 as w3c

// Added Realtime Lock to Avoid label repainting.
// Cleaned some code.
// Added level offset feature
//@version=5
indicator("[pp] Fib Alerts", overlay=true)

C_15 = color.maroon
C_1 = color.blue
C_4 = color.aqua
C_D = color.green
C_W = color.yellow
C_M = color.red

GROUP1          = "Levels"
i_useLog        = input.bool(true, "Use Log", group=GROUP1, tooltip="Compute Fib levels on Logarithmic Scale. TODO")
i_offset        = input.int(3, "Levels Offset", group=GROUP1)
i_fibBottom     = input.price(1, "Bottom", confirm=true, inline="i_fibBottom", group=GROUP1)
i_C_Bottom      = input.color(C_1, "", inline="i_fibBottom", group=GROUP1)
i_fibTop        = input.price(0., "Top", confirm=true, inline="i_fibTop", group=GROUP1)
i_C_Top         = input.color(C_1, "", inline="i_fibTop", group=GROUP1)


i_270On         = input.bool(true, "", inline="i_270", group=GROUP1)
i_270           = input.float(-0.270, "", inline="i_270", group=GROUP1)
i_C_270         = input.color(C_15, "", inline="i_270", group=GROUP1)
i_270Dir        = input.string("Sell", "", options=["Buy", "Sell", "None"], inline="i_270", group=GROUP1)

i_113On         = input.bool(true, "", inline="i_113", group=GROUP1)
i_113           = input.float(-0.113, "", inline="i_113", group=GROUP1)
i_C_113         = input.color(C_15, "", inline="i_113", group=GROUP1)
i_113Dir        = input.string("Sell", "", options=["Buy", "Sell", "None"], inline="i_113", group=GROUP1)

i_382On         = input.bool(true, "", inline="i_382", group=GROUP1)
i_382           = input.float(0.382, "", inline="i_382", group=GROUP1)
i_C_382         = input.color(C_15, "", inline="i_382", group=GROUP1)
i_382Dir        = input.string("Buy", "", options=["Buy", "Sell", "None"], inline="i_382", group=GROUP1)

i_50On         = input.bool(true, "", inline="i_50", group=GROUP1)
i_50           = input.float(0.5, "", inline="i_50", group=GROUP1)
i_C_50         = input.color(C_15, "", inline="i_50", group=GROUP1)
i_50Dir        = input.string("None", "", options=["Buy", "Sell", "None"], inline="i_50", group=GROUP1)

i_618On         = input.bool(true, "", inline="i_618", group=GROUP1)
i_618           = input.float(0.618, "", inline="i_618", group=GROUP1)
i_C_618         = input.color(C_15, "", inline="i_618", group=GROUP1)
i_618Dir        = input.string("Buy", "", options=["Buy", "Sell", "None"], inline="i_618", group=GROUP1)

i_707On         = input.bool(true, "", inline="i_707", group=GROUP1)
i_707           = input.float(0.707, "", inline="i_707", group=GROUP1)
i_C_707         = input.color(C_15, "", inline="i_707", group=GROUP1)
i_707Dir        = input.string("Buy", "", options=["Buy", "Sell", "None"], inline="i_707", group=GROUP1)

i_886On         = input.bool(true, "", inline="i_886", group=GROUP1)
i_886           = input.float(0.886, "", inline="i_886", group=GROUP1)
i_C_886         = input.color(C_15, "", inline="i_886", group=GROUP1)
i_886Dir        = input.string("None", "", options=["Buy", "Sell", "None"], inline="i_886", group=GROUP1)

GROUP2          = "Custom Alerts"
i_useCustomAlrt = input.bool(true, "Use Custom Alerts", group=GROUP2)
i_alertBuyTxt   = input.string("Buy Alert Message", "Buy Alert Message", group=GROUP2)
i_alertSellTxt  = input.string("Sell Alert Message", "Sell Alert Message", group=GROUP2)

GROUP3          = "3 Commas Integration"
i_usew3cAlrt    = input.bool(false, "Use 3 Commas Alerts", group=GROUP3)
i_3commasBotid  = input.int(6467052, "Bot Id", group=GROUP3)
i_3commasToken  = input.string("5ceb-asdfsadfsdf-f4fsdfsdfdsf-44fsdf", "Email Token", group=GROUP3)

f_lockRealtime(_data) =>
    // Locks reatime triggers in place so no repainting.
    varip bool _newBool = false
    if barstate.isnew
        _newBool := _data
    if _data
        _newBool := _data
    _newBool
    
f_line(_x1, _y1, _x2, _y2, _clr=color.blue) =>
    var _l = line.new(_x1,_y1,_x2,_y2,extend=extend.none, color=_clr)
    line.set_xy2(_l, _x2, _y2)
    
f_label(_x, _y, _txt, _clr)=>
    var _l = label.new(_x, _y, _txt, color=_clr, style=label.style_label_left, textcolor=#FFFFFF)
    label.set_x(_l, _x)

f_drawLevel(_bottom, _top, _level, bool _show=true, _clr=color.blue, _dir="None") =>
    _sign  = math.sign(_top - _bottom)
    _diff  = math.abs(_top - _bottom)
    // _price =     i_useLog ? _top * math.pow(_bottom/_top, _level) : _top  - (_diff * _level*_sign)  
    _price = _top  - (_diff * _level*_sign)
    
    // Track price crosses, so we don't send the same alerts when price sits around fib level.
    var _a_violations = array.new_float(na)

    if _show
        f_line(bar_index[1], _price, bar_index+i_offset, _price, _clr)
        _text   = str.tostring(_level) + (_dir == "Buy" ?  ' ⬆':_dir == "Sell"? ' ⬇':"")
        f_label(bar_index+i_offset, _price, _text, _clr)

        _buyTrigger = ta.cross(close, _price) and not array.includes(_a_violations,_price) and _dir == "Buy"
        if f_lockRealtime(_buyTrigger)
            // Push to array so we don't trigger this alert again.
            array.push(_a_violations,_price)
            
            label.new(bar_index, _price, str.format("{0} @ {1}", _dir, _text), color=color.green,style=label.style_label_up, textcolor=color.white)
            if i_useCustomAlrt
                alert(i_alertBuyTxt)
            // if i_usew3cAlrt
            //     alert(w3c.f_dealStart(i_3commasBotid, i_3commasToken))

        _sellTrigger = ta.cross(close, _price) and not array.includes(_a_violations,_price) and _dir == "Sell"
        if f_lockRealtime(_sellTrigger)
            // Push to array so we don't trigger this alert again.
            array.push(_a_violations,_price)

            label.new(bar_index, _price, str.format("{0} @ {1}", _dir, _text), color=color.red, textcolor=color.white)
            if i_useCustomAlrt
                alert(i_alertSellTxt)
            // if i_usew3cAlrt
            //     alert(w3c.f_closeMarket(i_3commasBotid, i_3commasToken))

 
f_drawFib(float _bottom, float _top) =>
    if barstate.isrealtime or barstate.islastconfirmedhistory
        f_drawLevel(_bottom, _top, i_270, i_270On, i_C_270, i_270Dir)
        f_drawLevel(_bottom, _top, i_113, i_113On, i_C_113, i_113Dir)
        f_drawLevel(_bottom, _top, 1, true, i_C_Bottom)
        f_drawLevel(_bottom, _top, i_382, i_382On, i_C_382, i_382Dir)
        f_drawLevel(_bottom, _top, i_50, i_50On, i_C_50, i_50Dir)
        f_drawLevel(_bottom, _top, i_618, i_618On, i_C_618, i_618Dir)
        f_drawLevel(_bottom, _top, i_707, i_707On, i_C_707, i_707Dir)
        f_drawLevel(_bottom, _top, i_886, i_886On, i_C_886, i_886Dir)
        f_drawLevel(_bottom, _top, 0, true, i_C_Top)

    
    
    
f_drawFib(i_fibBottom, i_fibTop)
