FrostyBot
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//@version=5

o__='  © kaigouthro                                        '

// @description       rostyBot-js JSON Alert Builder for Binance Futures and FTX orders
// https://github.com/CryptoMF/frostybot-js/wiki/Frostybot-Command-Reference#structure
// TODO: Comment Functions and anotations from command reference ^^
// TODO: Add additonal whitelist/account Functions.
// ===================================================
library("FrostyBot")

// ========================================================
o__:='         ┌─┐┌─┐┌┐┌┌─┐┌┬┐┌─┐┌┐┌┌┬┐┌─┐   		       ' 
o__:='         │  │ ││││└─┐ │ ├─┤│││ │ └─┐   		       ' 
o__:='         └─┘└─┘┘└┘└─┘ ┴ ┴ ┴┘└┘ ┴ └─┘   		       ' 

  
varip string _BUY         = "buy"
varip string _CANCELALL   = "cancelall"
varip string _CLOSE       = "close"
varip string _CLOSEL      = "closel"
varip string _CLOSES      = "closes"
varip string _COMMAND     = "command"
varip string _LEVERAGE    = "leverage"
varip string _LONG        = "long"
varip string _MAX         = "max"
varip string _MAXSIZE     = "maxsize"
varip string _PFTPRICE    = "profitprice"
varip string _PFTSIZE     = "profitsize"
varip string _PFTTRIGGER  = "profittrigger"
varip string _POST        = "post"
varip string _PRICE       = "price"
varip string _REDUCE      = "reduce"
varip string _SELL        = "sell"
varip string _SHORT       = "short"
varip string _SIZE        = "size"
varip string _STOPLOSS    = "stoploss"
varip string _STOPPRICE   = "stopprice"
varip string _STOPSIZE    = "stopsize"
varip string _STOPTRIGGER = "stoptrigger"
varip string _STUB        = "stub"
varip string _SYMBOL      = "symbol"
varip string _TAKEPRF     = "takeprofit"
varip string _TAKEPROFIT  = "takeprofit"
varip string _TRADE       = "trade"
varip string _TRAILLONG   = "traillong"
varip string _TRAILSHORT  = "trailshort"
varip string _TRAILSTOP   = "trailstop"
varip string _TYPE        = "type"

// ========================================================
o__:='   ┬ ┬┌─┐┬  ┌─┐┌─┐┬─┐  ┌─┐┬ ┬┌┐┌┌─┐┌┬┐┬┌─┐┌┐┌┌─┐   ' 
o__:='   ├─┤├┤ │  ├─┘├┤ ├┬┘  ├┤ │ │││││   │ ││ ││││└─┐   ' 
o__:='   ┴ ┴└─┘┴─┘┴  └─┘┴└─  └  └─┘┘└┘└─┘ ┴ ┴└─┘┘└┘└─┘   '                              

// semi-minified..
RP    ( _val, _o, _i ) => str.replace_all( _val, _o, _i  )
S     ( _val         ) => str.tostring   ( _val          )
item  ( _st , _key , _value , nest)     => str.format((nest?",":"")+'"{0}":"{1}"|', _key, _value)
json  ( _st , _key , _value , nest = 0) => s = _st, _st + item(_st, _key, _value, str.endswith(s, "|"))
brace ( x )   => s = RP(RP(RP(x," ",""),"|",""),'""','"'), s := string("{"+s+"}")
base (string cmd, stub, ticker)=>  s  = '"command":"trade:'+stub+':'+ cmd + '",', s := json (s, _SYMBOL ,'"'+ ticker  + '"'  )

szType (_sizeType, _qty      ) =>
    _size = switch _sizeType
        "absolute" => S(_qty)
        "factor"   => S(_qty) + "x"
        "percent"  => S(_qty) + "%"
prcType(_priceType,_price,_side) =>
    switch _priceType
        "absolute" =>         S(_price)
        "relative" => _side + S(_price)
        "percent"  => _side + S(_price) + "%" 
        => _side+"0.01%"



// ========================================================
o__:='         ┌┬┐┬─┐┌─┐┌┬┐┌─┐  ┌─┐┬─┐┌┬┐┌─┐┬─┐┌─┐       ' 
o__:='          │ ├┬┘├─┤ ││├┤   │ │├┬┘ ││├┤ ├┬┘└─┐       ' 
o__:='          ┴ ┴└─┴ ┴─┴┘└─┘  └─┘┴└──┴┘└─┘┴└─└─┘       ' 



// @function             leverage set         alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _lev          (int   ) Leverage Multiplier (set whenever desired change)                     
// @param _orders       (int   ) Order count (1 - N orders, make sure 1/N * Qty > minimum!!)  
// @returns             (string) Alert Message in JSON format 
export leverage   ( string stub ,
             string ticker                     ,   
             int    _lev       =   20          ,  
             string _levtype   = "cross"       )  =>
    s     = base   (     _LEVERAGE , stub      ,  ticker                )
    s    := json   ( s , _LEVERAGE             ,  _lev                  )
    s    := json   ( s , _TYPE                 , '"'+_levtype+'"'       )
    brace(s)                                                                                                                                    


// @function             buy   order          alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _qty          (float ) Quantity (Changes depending on price type)                     
// @param _sizeType     (string) "absolute" => Exact Size (Position or Qty.. SEE DOCUMENTATION!)
//                          "factor"   => Factor of current Position (1.5 = 150%, .5 = 50%)
//                          "percent"  => Percent of Equity                        
// @param _priceType    (string) "absolute" => exactly the price input is dollars  
//                          "relative" => market +    price input is dollars
//                          "percent"  => market +    price input is percent                     
// @param _price        (float ) Price Input (SEE DOCUMENTATION!!)                    
// @param _max          (float ) Maximum Position Size Limiter                       
// @param postonly      (bool  ) Place as "post only"                                 
// @returns             (string) Alert Message in JSON format 
export buy        ( string stub ,
             string ticker                     ,   
             float  _qty                       ,  
             string _sizeType  = "percent"     ,   
             string _priceType = "percent"     ,   
             float  _price     =   0.01        ,  
             float  _max       =   10000       ,  
             bool   postonly   =   false       )  =>
    _size = szType(_sizeType   , _qty          )
    _prc  = prcType(_priceType , _price, "-"   )
    s     = base   (     _BUY      , stub      ,  ticker                )
    s    := json   ( s , _POST                 ,  postonly              )
    s    := json   ( s , _MAXSIZE              ,  _max                  )
    s    := json   ( s , _SIZE                 ,  _size                 )
    s    := json   ( s , _PRICE                ,  _prc                  )
    brace(s)                                                                                                                                    


// @function             sell  order          alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _qty          (float ) Quantity (Changes depending on price type)                     
// @param _sizeType     (string) "absolute" => Exact Size (Position or Qty.. SEE DOCUMENTATION!)
//                          "factor"   => Factor of current Position (1.5 = 150%, .5 = 50%)
//                          "percent"  => Percent of Equity                        
// @param _priceType    (string) "absolute" => exactly the price input is dollars  
//                          "relative" => market +    price input is dollars
//                          "percent"  => market +    price input is percent                     
// @param _price        (float ) Price Input (SEE DOCUMENTATION!!)                    
// @param _max          (float ) Maximum Position Size Limiter                       
// @param postonly      (bool  ) Place as "post only"                                 
// @returns             (string) Alert Message in JSON format 
export sell       ( string stub ,
             string ticker                     ,   
             float  _qty                       ,  
             string _sizeType  = "percent"     ,   
             string _priceType = "percent"     ,   
             float  _price     =   0.01        ,  
             float  _max       =   10000       ,  
             bool   postonly   =   false       )  =>
    _size = szType(_sizeType  , _qty           )
    _prc  = prcType(_priceType , _price, "+"   )
    s     = base   (     _SELL     , stub      ,  ticker                )
    s    := json   ( s , _POST                 ,  postonly              )
    s    := json   ( s , _MAXSIZE              ,  _max                  )
    s    := json   ( s , _SIZE                 ,  _size                 )
    s    := json   ( s , _PRICE                ,  _prc                  )
    brace(s)                                                                                                                                    


// @function             cancel all open orders (no cancel by id... yet)
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)    
export cancelall  ( string stub ,
             string ticker                     )  =>
    brace(base(_CANCELALL, stub, ticker))


// @function            close long           alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _qty          (float ) Quantity (Changes depending on price type)                     
// @param _priceType    (string) "absolute" => exactly the price input is dollars  
//                          "relative" => market +    price input is dollars
//                          "percent"  => market +    price input is percent                     
// @param _price        (float ) Price Input (SEE DOCUMENTATION!!)                     
// @param _reduce       (bool  ) Reduce only order                                
// @returns             (string) Alert Message in JSON format 
export closelong  ( string stub ,
             string ticker                     ,   
             float  _qty                       ,  
             string _priceType = "percent"     ,   
             float  _price     =   na          ,  
             bool   _reduce    =   true        )  =>
    _prc  = prcType(_priceType , _price, "+"   )
    s     = base   (     _CLOSE    , stub      ,  ticker                )
    s    := json   ( s , _SIZE                 ,  _qty                  )
    s    := json   ( s , _REDUCE               ,  _reduce               )
    s    := json   ( s , _PRICE                ,  _prc                  )
    brace(s)                                                                                                                                    


// @function             close short          alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _qty          (float ) Quantity (Changes depending on price type)                     
// @param _priceType     (string) "absolute" => exactly the price input is dollars  
//                          "relative" => market +    price input is dollars
//                          "percent"  => market +    price input is percent                     
// @param _price        (float ) Price Input (SEE DOCUMENTATION!!)                     
// @param _reduce       (bool  ) Reduce only order                                
// @returns             (string) Alert Message in JSON format 
export closeshort ( string stub ,
             string ticker                     ,   
             float  _qty                       ,  
             string _priceType = "percent"     ,   
             float  _price     =   na          ,  
             bool   _reduce    =   true        )  =>
    _prc  = prcType(_priceType , _price, "-"   )
    s     = base   (     _CLOSE    , stub      , ticker                 )
    s    := json   ( s , _SIZE                 ,  _qty                  )
    s    := json   ( s , _REDUCE               ,  _reduce               )
    s    := json   ( s , _PRICE                ,  _prc                  )
    brace(s)                                                                                                                                    


// @function             trail sl long        alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _offset       (float ) Offset to trail by                        
// @param _reduce       (bool  ) Reduce only order                                  
// @returns             (string) Alert Message in JSON format 
export traillong  ( string stub ,
             string ticker                     ,   
             float  _offset                    ,  
             bool   _reduce    =   true        )  =>
    s     = base   (     _TRAILSTOP, stub      ,   ticker               )
    s    := json   ( s , _REDUCE               ,  _reduce               )
    s    := json   ( s , _TRAILSTOP            ,  "-" + S(_offset) + "%")
    brace(s)                                                                                                                                    


// @function             trail sl short       alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _offset       (float ) Offset to trail by                        
// @param _reduce       (bool  ) Reduce only order                                  
// @returns             (string) Alert Message in JSON format 
export trailshort ( string stub ,
             string ticker                     ,   
             float  _offset                    ,  
             bool   _reduce    =   true        )  =>
    s     = base   (     _TRAILSTOP, stub      ,   ticker               )
    s    := json   ( s , _REDUCE               ,  _reduce               )
    s    := json   ( s , _TRAILSTOP            ,  "+" + S(_offset) + "%")
    brace(s)                                                                                                                                    


// @function             set long  position   alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _qty          (float ) Quantity (Changes depending on price type)                     
// @param _start        (float ) Entry price / Offset from Market (Below mark Long, Above for Short)                     
// @param _dist         (float ) Distance from First Price (for spread orders)                        
// @param _orders       (int   ) Order count (1 - N orders, make sure 1/N * Qty > minimum!!)                    
// @param _max          (float ) Maximum Position Size Limiter                       
// @param postonly      (bool  ) Place as "post only"                                 
// @returns             (string) Alert Message in JSON format 
export long       ( string stub ,
             string ticker                     ,   
             float  _qty                       ,  
             float  _start     =   0.0001      ,  
             float  _dist      =   .0025       ,  
             int    _orders    =   1           ,  
             float  _max       =   10000       ,  
             bool   postonly   =   false       )  =>
    _last = _start + _dist
    s     = base   (     _LONG     , stub      ,   ticker               )
    s    := json   ( s , _POST                 ,    postonly            )
    s    := json   ( s , _MAX                  ,    _max                )
    s    := json   ( s , _SIZE                 ,  "%" + S(_qty    )     )
    s    := json   ( s , _PRICE                ,  "-" + S(_start * 100) +
     (_orders > 1 ? str.format(",{0},{1}", _last * 100, _orders) : '')  )
    brace(s)                                                                                                                                    


// @function             set short position   alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _qty          (float ) Quantity (Changes depending on price type)                     
// @param _start        (float ) Entry price / Offset from Market (Below mark Long, Above for Short)                     
// @param _dist         (float ) Distance from First Price (for spread orders)                        
// @param _orders       (int   ) Order count (1 - N orders, make sure 1/N * Qty > minimum!!)                    
// @param _max          (float ) Maximum Position Size Limiter                       
// @param postonly      (bool  ) Place as "post only"                                 
// @returns             (string) Alert Message in JSON format 
export short      ( string stub ,
             string ticker                     ,   
             float  _qty                       ,  
             float  _start     =   0.0001      ,  
             float  _dist      =   .0025       ,  
             int    _orders    =   1           ,  
             float  _max       =   10000       ,  
             bool   postonly   =   false       )  =>
    _last = _start + _dist
    s     = base   (     _SHORT    , stub      ,   ticker               )
    s    := json   ( s , _POST                 ,   postonly             )
    s    := json   ( s , _MAX                  ,   _max                 )
    s    := json   ( s , _SIZE                 ,  "%" + S(_qty    )     )
    s    := json   ( s , _PRICE                ,  "+" + S(_start * 100) +
     (_orders > 1 ? str.format(",{0},{1}", _last * 100, _orders) : '')  )
    brace(s)                                                                                                                                    


// @function             take profit          alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _size         (float ) Position Size (DOCUMENTATION!!)                        
// @param _trig         (float ) Trigger Price                     
// @param _exec         (float ) Execution Trigger Price                        
// @param _reduce       (bool  ) Reduce only order                                
// @returns             (string) Alert Message in JSON format 
export takeprofit ( string stub ,
             string ticker                     ,   
             float  _size                      ,  
             float  _trig                      ,  
             float  _exec                      ,  
             bool   _reduce    =   true        )  =>
    s     = base   (     _TAKEPRF  , stub      ,   ticker               )
    s    := json   ( s , _PFTSIZE              ,  S(_size   ) + "%"     )
    s    := json   ( s , _PFTTRIGGER           ,  S(_trig   )           )
    s    := json   ( s , _PFTPRICE             ,  S(_exec   )           )
    s    := json   ( s , _REDUCE               ,  S(_reduce )           )
    brace(s)                                                                                                                                    


// @function             stop loss            alert constructor for frostybot
// @param stub          (string) Stub value for your Frostybot server                     
// @param ticker        (string) Syminfo.tickerid Value (BINANCE:BTCUSDTPERP)                     
// @param _size         (float ) Position Size (DOCUMENTATION!!)                        
// @param _trig         (float ) Trigger Price                     
// @param _exec         (float ) Execution Trigger Price                        
// @param _reduce       (bool  ) Reduce only order                                
// @returns             (string) Alert Message in JSON format 
export stoploss   ( string stub ,
             string ticker                     ,   
             float  _size                      ,  
             float  _trig                      ,  
             float  _exec                      ,  
             bool   _reduce    =   true        )  =>
    s     = base   (     _STOPLOSS , stub      ,   ticker               )
    s    := json   ( s , _REDUCE               ,  _reduce               )
    s    := json   ( s ,_STOPSIZE              ,  S(_size  ) + "%"      )
    s    := json   ( s ,_STOPTRIGGER           ,  S(_trig  )            )
    s    := json   ( s ,_STOPPRICE             ,  S(_exec  )            )
    brace(s)                                                                                                                                    

// ========================================================
o__:='         ┌┬┐┌─┐┌─┐     ┌─┐┬ ┬┌┬┐┌┐ ┌─┐┬            ' 
o__:='         │││├─┤├─┘     └─┐└┬┘│││├┴┐│ ││            ' 
o__:='         ┴ ┴┴ ┴┴       └─┘ ┴ ┴ ┴└─┘└─┘┴─┘          ' 

// @function  Map a Symbol from Tradingview to your Frostybot (REQUIRED ONCE EVER / SYMBOL)
//            No automatic way of this yet (to keep function available for MULTI TICKER scripts.)
// @param     _coin      (string) the left  side of a pair " > ______ < /   .....    "  ( BTCUSDT => BTC)
// @param     _base      (string) the right side of a pair "   ......   / > ______ < "  ( BTCUSDT => USDT) 
// @param     ticker     (string) the built in tickerid for the coin (syminfo.tickerid / input.symbol)
export map         (string _coin,
             string _base                     ,
             string ticker                    ) =>
    _prefix = syminfo.prefix(ticker)
    _ticker = syminfo.ticker(ticker)
    s     = ''
    s    := json   ( s ,'symbolmap'  , 'add'                            )
    s    := json   ( s , 'exchange'  , S(str.upper(_prefix))            )   
    s    := json   ( s , 'symbol'    , S(str.upper(_prefix+':'+_ticker)))                       
    s    := json   ( s , 'mapping'   , S(_coin +'/'+_base)              )   


// ========================================================
o__:='         ┌┬┐┌─┐┌─┐┌┬┐                              ' 
o__:='          │ ├┤ └─┐ │                               ' 
o__:='          ┴ └─┘└─┘ ┴                               ' 



// for testing your scripts (default signals are all safe, NO TRANSACTIONS MADE)
// @param  _testchoice  (int)     Alert to test
// @param  _ticker      (string)  Ticker input
// @param  _stub        (string)  Stub input
// @param  _checkalert  (bool)    Send alerts (3 / minute  if activer)
// @param  _label       (bool)    Show Alerts on Label
// @returs void
export test(int _testchoice, string _ticker , string _stub ="stup" , bool _checkalert = true, bool _label = true) =>
    var label lb = na
    label.delete(lb[1])

    varip init = true
    if second % 20 == 0
        init := true

    if barstate.islast    
        string _jleverage    = leverage     (_stub,_ticker,  5,            "cross"              )
        string _jbuy         = buy          (_stub,_ticker,  0, "percent" ,"percent", 0.01, 500 )
        string _jsell        = sell         (_stub,_ticker,  0, "percent" ,"percent", 0.01, 500 )
        string _jlong        = long         (_stub,_ticker,  0     )
        string _jshort       = short        (_stub,_ticker,  0        , .1, .3, 10, 500, false  )
        string _jclosel      = closelong    (_stub,_ticker,  0        ,    "percent", 10        )
        string _jcloses      = closeshort   (_stub,_ticker,  0        ,    "percent", 5         )
        string _jstoploss    = stoploss     (_stub,_ticker,  50       , close[10] , close       )
        string _jtakeprofit  = takeprofit   (_stub,_ticker,  50       , close[10] , close       )
        string _jtraillong   = traillong    (_stub,_ticker,  5     )
        string _jtrailshort  = trailshort   (_stub,_ticker,  5     )
        string _jcancelall   = cancelall    (_stub,_ticker         )
        
        [_alrt,_alrtlbl] = switch _testchoice
            1  => [_jleverage   , 'leverage'   ]   
            2  => [_jbuy        , 'buy'        ]    
            3  => [_jsell       , 'sell'       ]   
            4  => [_jlong       , 'long'       ]   
            5  => [_jshort      , 'short'      ]  
            6  => [_jclosel     , 'closel'     ]     
            7  => [_jcloses     , 'closes'     ]     
            8  => [_jstoploss   , 'stoploss'   ]   
            9  => [_jtakeprofit , 'takeprofit' ]     
            10 => [_jtraillong  , 'traillong'  ]  
            11 => [_jtrailshort , 'trailshort' ]     
            12 => [_jcancelall  , 'cancelall'  ] 
        
        if _checkalert and init 
            alert(_alrt ,alert.freq_once_per_bar)
            init := false

        if _label
            lb:= label.new(last_bar_index, close, '⛄  ' + _alrtlbl + '\n\n' + _alrt ,textalign=text.align_left,color=color.white,size=size.normal)
                



// ===================================================
// testing strings with label.
_chkalrt        = input.bool  ( true       , " Test Webhook W/alert", inline="bools",group="test") 
_label          = input.bool  ( true       , " Test Label          ", inline="bools",group="test") 
_testchoice     = input.int   ( 1          , " Test Alert #"   ,1,12, inline="str  ",group="test")   
_stub           = input.string("StubToTest", " Stub "               , inline="str  ",group="test")     
_tiicker        = input.symbol('BTCUSDTPERP') 

test(_testchoice, _tiicker, _stub,_chkalrt,_label)            


    //////////////////////////////////

