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

//New
// @description A handful collection of regular functions, Custom Tools & Utility Functions could be used in regular Scripts. hope these functions can be understood by a non programmer like me too.
library("BE_CustomFx_Library",overlay=true)
//Test1
import PineCoders/Time/3 as pcTimeLib

export type Info_ScriptExpiry
    float   IAO_Value
    string  IAO_ValueText
    string  API_Bridge_Format
    string  FyersFormat
    string  FinvasiaFormat
    string  DhanStrikeFormat

export type Trade_Info
    int     AllTrades                   = 0
    int     LongTrades                  = 0
    int     ShortTrades                 = 0
    int     AllProfitableTrade          = 0
    float   TotalPNL                    = 0
    int     TodayTrades                 = 0
    int     TodayProfitableTrade        = 0
    float   TodayPNL                    = 0
    bool    IsFirstTradeForTheDay       = true
    int     NextTradeAfter              = 0
    string  HedgeCEOTM                  = ""
    string  HedgePEOTM                  = ""
    string  T_Symbol                    = ""    
    int     T_Direction                 = 0
    float   T_PNL                       = 0
    float   T_EnPrice                   = 0
    int     T_EnCandle                  = 0
    float   T_TGTPrice                  = 0
    float   T_SLPrice                   = 0
    float   T_ExPrice                   = 0
    int     T_ExCandle                  = 0
    int     T_Qty                       = 0
    string  T_Type                      = ""
    string  T_QtyString                 = ""
    string  T_Account_Name              = ""
    string  T_En_Date_Time              = ""
    string  T_Ex_Date_Time              = ""
    int     T_HasEntered                = 0
    int     T_HasExited                 = 0
    int     ExitCandleCheck             = 0
    float   Price2MoveForTrailing       = 0    
    float   TrailSL_Val                 = 0
    float   MaxTradeROI                 = 0
    int     SL_Lock_Level               = 0
    string  Gen_EnMsg                   = ""
    string  Gen_ExMsg                   = ""
    string  LastLegAlert                = ""

export type Info_Account_Expiry
    bool    IsExpired
    string  Expirydate
    int     ExpiryDateInt
    float   DaysDiff

export type Info_CalculatedSLTP_Level
    string  SL_Text
    string  TP_Text
    float   SL_Value
    float   TP_Value

//#region RegularUsuage

//#region EntryAlertInfoText
// @function to combine couple of basic Entry alert text
export EntryAlertInfoText(string TradeTypeOrScriptName, int TradeDirection, float EnPrice , string EntryDateTime, int TQTY, float LTP = close, string OtherInfo = "")=>
    str.upper(str.format('{0} {1} Trade Entered at:{2, number, #.##}, Delta:{3, number, #.##}, on {4} with Qty:{5, number, integer} {6}', 
     TradeTypeOrScriptName, 
     (TradeDirection == 1 ? "Long" : "Short"),
     EnPrice, 
     EnPrice - LTP,
     EntryDateTime, 
     TQTY, 
     OtherInfo))
//#endregion

//#region ExitAlertInfoText
// @function to combine couple of basic Exit alert text
export ExitAlertInfoText(string TradeTypeOrScriptName, int TradeDirection, float ExPrice , string ExitDateTime, float ProfitOrLossValue, int TQTY, float LTP = close, string OtherInfo = "")=>
    str.upper(str.format('{0} {1} Trade Exited at:{2, number, #.##}, Delta:{3, number, #.##}, on {4} with PNL:{5, number, #.##} | Qty:{6, number, integer} {7}', 
     TradeTypeOrScriptName, 
     (TradeDirection == 1 ? "Long" : "Short"),
     ExPrice, 
     ExPrice - LTP,
     ExitDateTime,
     ProfitOrLossValue,
     TQTY, 
     OtherInfo))
//#endregion

//#region OperatorChk
// @function to compare the value against and provide the boolean output
// @param flot val2chk = Number to check
// @param string OperatorTxt = Set to E for ==, NE for !=, G for >, GE for >=, L for <, LE for <= , BE for <= and >= (Between with Equal) and ather string for >< (Between with greater Lesser) 
// @param float CompareVal1 = single input to compare against Val2Chk 
// @param float CompareVal2 = incase of between scenarios thsi will be used 
// @returns Returns bool Output if compared value found to be true
export OperatorChk(float val2chk,string OperatorTxt, float CompareVal1,float CompareVal2 = 0) =>
    bool isAboveWithE           = val2chk >=    math.min(CompareVal1,CompareVal2)    ? true : false
    bool isBelowWithE           = val2chk <=    math.max(CompareVal1,CompareVal2)    ? true : false
    bool isAboveWithOutE        = val2chk >     math.min(CompareVal1,CompareVal2)    ? true : false
    bool isBelowWithOutE        = val2chk <     math.max(CompareVal1,CompareVal2)    ? true : false
    a = switch OperatorTxt
        "E"                     => val2chk == CompareVal1                               ? true : false
        "NE"                    => val2chk != CompareVal1                               ? true : false
        "NEOR"                  => val2chk != CompareVal1 or val2chk != CompareVal2     ? true : false
        "G"                     => val2chk > CompareVal1                                ? true : false
        "GE"                    => val2chk >= CompareVal1                               ? true : false
        "L"                     => val2chk < CompareVal1                                ? true : false
        "LE"                    => val2chk <= CompareVal1                               ? true : false
        "B"                     => isAboveWithOutE and isBelowWithOutE                  ? true : false
        "BE"                    => isAboveWithE and isBelowWithE                        ? true : false
        "OSE"                   => (val2chk < math.min(CompareVal1,CompareVal2)) or (val2chk > math.max(CompareVal1,CompareVal2))   ? true : false
        "OSI"                   => (val2chk <= math.min(CompareVal1,CompareVal2)) or (val2chk >= math.max(CompareVal1,CompareVal2)) ? true : false
        => false
//#endregion

//#region CustomCandleInfoFx
// Supporting tuple function to calculate the candle co-ordinates.
CustomCandleInfoFx(Repainting, Gaps) =>
    bool Rpnt               = barstate.ishistory or barstate.isconfirmed
    float C_Open            = na
    float C_Close           = na 
    float C_High            = na
    float C_Low             = na
    float R_Open            = Rpnt ? open[0]    : nz(open[1])
    float R_Close           = Rpnt ? close[0]   : nz(close[1])
    float R_High            = Rpnt ? high[0]    : nz(high[1])
    float R_Low             = Rpnt ? low[0]     : nz(low[1])  
    if barstate.isfirst and Gaps == false
        C_Open  := open
        C_Close := close
        C_High  := high
        C_Low   := low
    else if Repainting and Gaps == false
        C_Open  := open
        C_Close := close
        C_High  := high
        C_Low   := low    
    else if Repainting == false and Gaps == false
        C_Open  := nz(open[1])
        C_Close := nz(close[1])
        C_High  := nz(high[1])
        C_Low   := nz(low[1])
    else if Repainting and Gaps
        C_Open  := open[0] > high[1] or open[0] < low[1] ? close[1] : open[0]
        C_Close := close
        C_High  := math.max(high,C_Open)
        C_Low   := math.min(low,C_Open)
    else 
        C_Open  := R_Open > R_High[1] or R_Open < R_Low[1] ? R_Close[1] : R_Open
        C_Close := R_Close
        C_High  := math.max(R_High,C_Open)
        C_Low   := math.min(R_Low,C_Open)

    [C_Open, C_Close, C_High, C_Low]

// [Opn,cls,hgh,lw] = CustomCandleInfoFx(false,true)
// plotcandle(
//      Opn -400,
//      hgh -400,
//      lw -400,
//      cls -400,
//      "customOHLC",
//      color = Opn > cls ? color.red : color.green,
//      wickcolor = color.white,
//      bordercolor =  Opn > cls ? color.red : color.green,
//      show_last = 120)
//#endregion

//#region NumAsText
// @function Function to return the String Value of Number with decimal precision with the prefix and suffix characters provided
// @param float ValueToConvert = Number to Convert
// @param int RequiredDecimalPlaces = No of Decimal values Required. supports to a max of 5 decimals else defaults to 2 
// @param string BeginingChar = Prefix character which is needed. 
// @param string EndChar = Suffix character which is needed. 
// @returns Returns Out put with formated value of Given Number for the specified deicimal values with Prefix and suffix string
export NumAsText(
     float ValueToConvert,
     int RequiredDecimalPlaces = -1,
     string ConversionType = format.mintick,
     string EndChar = "",
     string BeginingChar = "") =>

    string FormatedVal = switch RequiredDecimalPlaces
        -1=>    str.tostring(ValueToConvert,        ConversionType)    
        0 =>    str.format("{0,number,integer}",    ValueToConvert)
        1 =>    str.format("{0, number,#.#}",       ValueToConvert)
        3 =>    str.format("{0,number,#.###}",      ValueToConvert)
        4 =>    str.format("{0,number,#.####}",     ValueToConvert)
        5 =>    str.format("{0,number,#.####}",     ValueToConvert)
        2 =>    str.format("{0,number,#.##}",       ValueToConvert)

    string Output = BeginingChar + FormatedVal + EndChar
//#endregion

//#region G_Pct_Chng
// @function Gets the percentage change between 2 float values over a given lookback period
// @param float value1 The first value to reference
// @param float value2 The second value to reference
// @param int lookback The lookback period to analyze
export G_Pct_Chng(float value1, float value2, int lookback) =>
    vChange = value1 - value2
    vDiff   = vChange - vChange[lookback]
    (vDiff / vChange) * 100
//#endregion 

//#region G_Indicator_Val
// @function Gets Output of the technical analyis indicator which has length Parameter. RSI, ATR, EMA, SMA, HMA, WMA, VWMA, 'CMO', 'MOM', 'ROC','VWAP', "Highest", "Lowest"
// @param string IndicatorName to be specified
// @param float SrcVal for the TA indicator default is close
// @param simple int Length for the TA indicator
// @param int DecimalValue optional to specify if required formatted output with decimal percision else default output comes in tradable value
// @returns Value with the given parameters
export G_Indicator_Val(string IndicatorName, simple int Length, float SrcVal = close) =>
    ROC_Val     = ta.roc(SrcVal, Length)
    MOM_Val     = ta.mom(SrcVal, Length)
    CMO_Val     = ta.cmo(SrcVal, Length)
    RSI_Val     = ta.rsi(SrcVal, Length)
    ATR_Val     = ta.atr(Length) // to Mention Simple Int
    EMA_Val     = ta.ema(SrcVal, Length)
    SMA_Val     = ta.sma(SrcVal, Length)
    HMA_Val     = ta.hma(SrcVal, Length)
    WMA_Val     = ta.wma(SrcVal, Length)
    VWMA_Val    = ta.vwma(SrcVal, Length)
    Highest_Val = ta.highest(SrcVal == close ? high : SrcVal, Length)
    Lowest_Val  = ta.lowest(SrcVal == close ? low : SrcVal, Length)

    float IndValue = switch IndicatorName
        "ROC"       => ROC_Val
        "MOM"       => MOM_Val
        "CMO"       => CMO_Val
        "RSI"       => RSI_Val
        "ATR"       => ATR_Val // to Mention Simple Int
        "EMA"       => EMA_Val
        "SMA"       => SMA_Val
        "HMA"       => HMA_Val
        "WMA"       => WMA_Val
        "VWMA"      => VWMA_Val
        "VWAP"      => ta.vwap
        "Highest"   => Highest_Val
        "Lowest"    => Lowest_Val
//#endregion  

//#region G_CandleInfo
// @function function to get Candle Informarion such as both wicksize, top wick size , bottom wick size, full candle size and body size in default points
// @param string WhatCandleInfo,  string input with either of these options  "Wick" , "TWick" , "BWick" , "Candle", "Body" , "BearfbVal", "BullfbVal" , "CandleOpen" ,"CandleClose", "CandleHigh" , "CandleLow", "BodyPct","ExtractVal"
// @param bool RepaintingVersion,  set to true if required data on the realtime bar else default is set to false 
// @param float FibValueOfCandle,  set the fibo value to extract fibvalue of the candle else default is set to 38.2%
// @param bool AccountforGaps,  set to true if required data on considering the gap between previous and current bar else default is set to false
// @param float MidVal, set the % value in order to extract the value for the candle
// @returns Returns Respective values for the candles
export G_CandleInfo(simple string WhatCandleInfo, simple float FibValueOfCandle = 0.382, bool RepaintingVersion = false, bool AccountforGaps = false, float MidVal = 0.5) =>
    var float fbv = 0.00
    var int cndltype = 0
    fbv := FibValueOfCandle == 0 ? 0.382 : FibValueOfCandle
    
    [Opn, Cls, Hgh, Lw]         = CustomCandleInfoFx(RepaintingVersion,AccountforGaps)
    cndltype                    := Opn > Cls ? -1 : 1
    float Output = switch WhatCandleInfo
        "Wick"          => cndltype == -1 ? (Hgh - Opn) + (Cls - Lw)   : (Hgh - Cls) + (Opn - Lw)
        "TWick"         => cndltype == -1 ? (Hgh - Opn)                : (Hgh - Cls)
        "BWick"         => cndltype == -1 ? (Cls - Lw)                 : (Opn - Lw)
        "Candle"        => math.abs(Hgh - Lw)
        "Body"          => math.abs(Opn - Cls)
        "BearfbVal"     => (Hgh - Lw) * fbv
        "BullfbVal"     => (Lw - Hgh) * fbv
        "CandleOpen"    => Opn
        "ExtractVal"    => Lw + ((Hgh - Lw) * MidVal)
        "CandleLow"     => Lw
        "CandleHigh"    => Hgh
        "CandleClose"   => Cls
        "BodyPct"       => math.abs(Opn - Cls) / math.abs(Hgh - Lw)
        "MCPct"         => (math.abs(Opn - Cls) / Cls) * 100
        "CandlePct"     => (math.abs(Hgh - Lw) / Cls) * 100
//#endregion 

//#region G_ITM
// @function to calculate the ITM stike for the defined strike change
// @param string source (default='c') specify either of the source value ['o','h','l','c','s'] Note: Be mindful as it may be repainted and calulation to be called on every bar
// @param bool NeedRepainting (default=true) Specify false if you need them to calculate on the completed bars
// @param int StrikeDiff (default=100) Specify the strike difference value.  eg = Nifty = 50 and BNF = 100
// @param bool ForCE (default=true) Specify false if you need them to calculate for PE option
// @param int HowDeep (default=1) Specify the level.
// @param float SVal (default=0) Specify the source value.
// @param float CalibaratePct (default=0, min = 0 and max = 1) Specify the percentage value. eg if calibarion is set for 50% on the banknifty close price with 30030.5 ITM CE 1 deep will show 29900 how ever if close price is 30070.5 ITM CE with 1 deep will show 30000 
export G_ITM(int HowDeep = 1, int StrikeDiff = 100, bool ForCE = true, string source = 'c', float SVal = 0, float CalibaratePct = 0.0, bool NeedRepainting = true, string Delimiter = "|") =>
    CanOpn = G_CandleInfo("CandleOpen",0,NeedRepainting)
    CanHgh = G_CandleInfo("CandleHigh",0,NeedRepainting)
    CanLow = G_CandleInfo("CandleLow",0,NeedRepainting)
    CanCls = G_CandleInfo("CandleClose",0,NeedRepainting)    
    Val = switch str.upper(source)
        'O' => CanOpn
        'H' => CanHgh
        'L' => CanLow
        'C' => CanCls
        =>  SVal
    PreOutput           =  ForCE ? Val - (Val % StrikeDiff) - (StrikeDiff * HowDeep) : Val - (Val % StrikeDiff) + (StrikeDiff * HowDeep)
    ITMVal              = if CalibaratePct    == 0.0
        PreOutput
    else
        if OperatorChk(CalibaratePct, "BE", 0.0 , 1.0)
            if Val % StrikeDiff >= StrikeDiff * CalibaratePct and ForCE 
                PreOutput + StrikeDiff 
            else if Val % StrikeDiff >= StrikeDiff * CalibaratePct and not ForCE 
                PreOutput + StrikeDiff
            else
                PreOutput
        else
            PreOutput

    OutPt               = Info_ScriptExpiry.new(ITMVal, 
     NumAsText(ITMVal), 
     str.tostring(ITMVal) + Delimiter + (ForCE ? "CE" : "PE"),
     str.tostring(ITMVal) + (ForCE ? "CE" : "PE"),
     (ForCE ? "C" : "P")  + str.tostring(ITMVal),
     NumAsText(ITMVal, 1) + ".0")
//#endregion

//#region G_OTM
// @function to calculate the OTM stike for the defined strike change
// @param string source (default='c') specify either of the source value ['o','h','l','c'] Note: Be mindful as it may be repainted and calulation to be called on every bar
// @param bool NeedRepainting (default=true) Specify false if you need them to calculate on the completed bars
// @param int StrikeDiff (default=100) Specify the strike difference value.  eg = Nifty = 50 and BNF = 100
// @param bool ForCE (default=true) Specify false if you need them to calculate for PE option
// @param int HowDeep (default=1) Specify the level.
// @param float SVal (default=0) Specify the source value.
// @param float CalibaratePct (default=0, min = 0 and max = 1) Specify the percentage value. eg if calibarion is set for 50% on the banknifty close price with 30030.5 ITM CE 1 deep will show 29900 how ever if close price is 30070.5 ITM CE with 1 deep will show 30000 
// @returns the strike price for the options speficied
export G_OTM(int HowDeep = 1, int StrikeDiff = 100, bool ForCE = true, string source = 'c', float SVal = 0, float CalibaratePct = 0.0, bool NeedRepainting = true, string Delimiter = "|") =>
    CanOpn = G_CandleInfo("CandleOpen",0,NeedRepainting)
    CanHgh = G_CandleInfo("CandleHigh",0,NeedRepainting)
    CanLow = G_CandleInfo("CandleLow",0,NeedRepainting)
    CanCls = G_CandleInfo("CandleClose",0,NeedRepainting)    
    Val = switch str.upper(source)
        'O' => CanOpn
        'H' => CanHgh
        'L' => CanLow
        'C' => CanCls
        =>  SVal
    PreOutput           = ForCE ? Val - (Val % StrikeDiff) + (StrikeDiff * HowDeep) : Val - (Val % StrikeDiff) - (StrikeDiff * HowDeep)
    OTMVal              = if CalibaratePct    == 0.0
        PreOutput
    else
        if OperatorChk(CalibaratePct, "BE", 0.0 , 1.0)
            if Val % StrikeDiff >= StrikeDiff * CalibaratePct and ForCE 
                PreOutput + StrikeDiff
            else if Val % StrikeDiff >= StrikeDiff * CalibaratePct and not ForCE 
                PreOutput + StrikeDiff        
            else
                PreOutput
        else
            PreOutput
    OutPt               = Info_ScriptExpiry.new(OTMVal, 
     NumAsText(OTMVal), 
     str.tostring(OTMVal) + Delimiter + (ForCE ? "CE" : "PE"),
     str.tostring(OTMVal) + (ForCE ? "CE" : "PE"),
     (ForCE ? "C" : "P")  + str.tostring(OTMVal),
     NumAsText(OTMVal, 1) + ".0")
//#endregion

//#region G_ATM
// @function to calculate the ATM stike for the defined strike change
// @param string source (default='c') specify either of the source value ['o','h','l','c'] Note: Be mindful as it may be repainted and calulation to be called on every bar
// @param bool NeedRepainting (default=true) Specify false if you need them to calculate on the completed bars
// @param int StrikeDiff (default=100) Specify the strike difference value.  eg = Nifty = 50 and BNF = 100
// @param float SVal (default=0) Specify the source value.
// @param float CalibaratePct (default=0, min = 0 and max = 1) Specify the percentage value. eg if calibarion is set for 50% on the banknifty close price with 30030.5 ITM CE 1 deep will show 29900 how ever if close price is 30070.5 ITM CE with 1 deep will show 30000 
// @returns the strike price for the options speficied
export G_ATM(int StrikeDiff = 100, bool ForCE = true, string source = 'c', float SVal = 0, float CalibaratePct = 0.0, bool NeedRepainting = true, string Delimiter = "|") =>
    CanOpn = G_CandleInfo("CandleOpen",0,NeedRepainting)
    CanHgh = G_CandleInfo("CandleHigh",0,NeedRepainting)
    CanLow = G_CandleInfo("CandleLow",0,NeedRepainting)
    CanCls = G_CandleInfo("CandleClose",0,NeedRepainting)    
    Val = switch str.upper(source)
        'O' => CanOpn
        'H' => CanHgh
        'L' => CanLow
        'C' => CanCls
        =>  SVal
    PreOutput = Val - (Val % StrikeDiff)
    ATMVal              = if CalibaratePct == 0.00
        PreOutput
    else
        if OperatorChk(CalibaratePct, "BE", 0.0 , 1.0)
            if Val % StrikeDiff >= StrikeDiff * CalibaratePct and ForCE 
                PreOutput + StrikeDiff 
            else if Val % StrikeDiff >= StrikeDiff * CalibaratePct and not ForCE 
                PreOutput + StrikeDiff 
            else
                PreOutput
        else
            PreOutput
    OutPt               = Info_ScriptExpiry.new(ATMVal, 
     NumAsText(ATMVal), 
     str.tostring(ATMVal) + Delimiter + (ForCE ? "CE" : "PE"),
     str.tostring(ATMVal) + (ForCE ? "CE" : "PE"),
     (ForCE ? "C" : "P")  + str.tostring(ATMVal),
     NumAsText(ATMVal, 1) + ".0")
//#endregion

//#region Chk_TradingTime
// @function Determines if the bar's time falls within time filter range
// @param string SessionPeriod (default = "GMT+5:30")Opening Market peak time
// @returns A boolean - true if the current bar falls within the given time
export Chk_TradingTime(string SessionPeriod = '0930-1500') =>
    not(na(time(timeframe.period, SessionPeriod + ':1234567')))
//#endregion

//#region DhanHQ_Webhook
// @function to retrieve the JSON Format for DhanHQ
// @returns to retrieve the JSON Format for DhanHQ
export DhanHQ_Webhook(bool Calculation = true, string Dir_, string OrderType_ = "M", string ProductType_ = "", string ScriptName_Expiry, string Qty, string LMTPrice = "0", string Exchange = "NSE", bool IsCall = false, string StrikePrice = "") =>
    string OrderType    = str.upper(OrderType_) == "M" ? "MKT" : "LMT"
    string Dir          = str.upper(Dir_) == "BUY" ? "B" : "S"
    string ProductType  = str.upper(ProductType_) == "CNC" or str.upper(ProductType_) == "NRML" ? "M" : "I"
    string SymbolInfo   = str.upper(ScriptName_Expiry)
    string ExpiryInfo   = str.upper(ScriptName_Expiry)
    string AlertMSg   = ""
    bool ContainsExpiry = str.contains(SymbolInfo, "_")
    if ContainsExpiry and Calculation
        ExtractValue    = str.split(SymbolInfo,"_")
        SymbolInfo      := ExtractValue.first()
        ExpiryInfo      := ExtractValue.last()
        AlertMSg        := '{"transactionType": "' + Dir +'","orderType": "' + OrderType + '","quantity": "' + Qty + '","exchange": "' + Exchange + '","symbol": "' + SymbolInfo + '","instrument": "OPT","productType": "' + ProductType +'","sort_order": "$","price": "' + LMTPrice +'","option_type": "' + (IsCall ? "CE" : "PE") + '","strike_price": "' + StrikePrice + '","expiry_date": "' + ExpiryInfo + '"}'
    else if ContainsExpiry and not Calculation
        ExtractValue    = str.split(SymbolInfo,"_")
        SymbolInfo      := ExtractValue.first()
        ExpiryInfo      := ExtractValue.get(1)
        StrikeInfo      = ExtractValue.get(2)
        CE_Or_PE        = ExtractValue.last()
        AlertMSg        := '{"transactionType": "' + Dir +'","orderType": "' + OrderType + '","quantity": "' + Qty + '","exchange": "' + Exchange + '","symbol": "' + SymbolInfo + '","instrument": "OPT","productType": "' + ProductType +'","sort_order": "$","price": "' + LMTPrice +'","option_type": "' + CE_Or_PE + '","strike_price": "' + StrikeInfo + '","expiry_date": "' + ExpiryInfo + '"}'
    else if not Calculation
        AlertMSg        := '{"transactionType": "' + Dir +'","orderType": "' + OrderType + '","quantity": "' + Qty + '","exchange": "' + Exchange + '","symbol": "' + SymbolInfo + '","instrument": "' + (str.contains(SymbolInfo,"!") ? "FUT" : "EQ") +'","productType": "' + ProductType +'","sort_order": "$","price": "' + LMTPrice +'"}'
    else
        AlertMSg        := '{Not In Specified Format}'
//#endregion

//#region ScriptConvertor
// @param ExpectedOutput AcceptableValue = "Name", "Type", "Exp"
export ScriptConvertor(string ExpectedOutput = "Name", string DefTker = "", string DefType ="")=>
    Script          = DefTker == "" ? syminfo.ticker    : DefTker
    TypeOfScript    = if DefType == "" 
        syminfo.type
    else
        if DefType == "F"
            "futures"
        else if DefType == "E"
            "stock"
        else if DefType == "I"
            "index"
        else
            "Others"
    Output          = ""
    if ExpectedOutput == "Type"
        Output := switch TypeOfScript
            "stock"     => "E"
            "futures"   => "F"
            "index"     => "I"
            =>             "U"
    else if ExpectedOutput == "Name"
        PreOutput = switch TypeOfScript
            "stock"     => "E"
            "futures"   => "F"
            "index"     => "I"
            =>             "U"

        if PreOutput == "E" or PreOutput == "I"
            Output                  := Script
        else 
            if PreOutput == "F" and DefTker != ""
                Output                  := str.replace(str.replace(Script, "1!", ""),"2", "")
            else
                if DefTker == ""
                    Output              := Script
                else
                    Output              := "U1"
    else 
        if ExpectedOutput == "Exp"
            PreOutput = switch TypeOfScript
                "stock"     => "E"
                "futures"   => "F"
                "index"     => "I"
                =>             "U"
            if PreOutput == "F"
                CheckExc            = str.contains(Script, "1!") or str.contains(Script, "2!")
                if CheckExc
                    if str.contains(Script, "1!")
                        Output      := "C"
                    else
                        if str.contains(Script, "2!")
                            Output  := "N"
                        else
                            Output  := "C"
                else
                    Output          := "C"
            else 
                Output              := "U2"
        else
            Output                  := "Something"
//#endregion

//#region AlgoJiIntraMKTAlert
// @param string TSym - specify the symbol name for which alert is required defualt is syminfo.tickerid
// @param string EnOrEx - specify "EN" for Entry trade or "EX" for Exit trade
// @param string LongOrShort - specify "L" for Long and "S" for short
// @param int Qty - specify qunatitly for the trade
// @param string Instrument - specify instruement type [EQ|FUTIDX|OPTIDX]
// @param string Tag - specify Strategy Tag
export AlgoJiIntraMKTAlert(string TSym = "", string EnOrEx = "EN", string LongOrShort = "L", int Qty = 1, string Instrument = "OPTIDX", string Tag = "STG1")=>
    Tker    = TSym == "" ? syminfo.ticker(syminfo.tickerid) : TSym
    Output  = "TYPE: " + (LongOrShort == "L" ? "L" : "S") + (EnOrEx == "EN" ? "E" : "X") + " :SYMBOL: " + Tker + " :QTY: " + str.tostring(Qty) + " :PRICE: 0 :OT: MARKET|MIS :INS: " + Instrument + " :STAG: " + Tag 
//#endregion

//#region ChckIndcatorExpiry
// @function TODO: to Get Necessary Information with relavnt information for Date & Time Calculation
// @param x TODO: Specify the Date String
// @returns TODO: Get Necessary Information with relavnt information
export ChckIndcatorExpiry(string SupplyInput = "General") =>
	var bool    IsHavingA_Valid_L 		= false
	var int     L_ExpiryDate       		= 31
	var int     L_ExpiryMon      		= 12
	var int     L_ExpiryYear       		= 9999
    var         L_Array                 = array.from("General|31-01-2023","221812|31-12-2099","HEMA*General|31-01-2023","HEMA*221812|31-12-2099")
	for [index, value] in L_Array
		if str.startswith(str.upper(value), str.upper(SupplyInput))
			IsHavingA_Valid_L     		:= true
			ExpiryDateInfo       		= str.split(str.replace(value, SupplyInput + "|", ""),"-")
			L_ExpiryDate           		:= int(str.tonumber(array.get(ExpiryDateInfo,0)))
			L_ExpiryMon          		:= int(str.tonumber(array.get(ExpiryDateInfo,1)))
			L_ExpiryYear           		:= int(str.tonumber(array.get(ExpiryDateInfo,2)))
    DateCheck                           = timestamp(L_ExpiryYear, L_ExpiryMon, L_ExpiryDate, 23, 59, 59)
	var bool    IsValid                 = if IsHavingA_Valid_L
		if timenow <= DateCheck
			true
		else
			false
	else
		false
    Output                              = Info_Account_Expiry.new(
     IsValid,
     pcTimeLib.formattedDate(DateCheck, "medium"),
     DateCheck,
     math.round(math.abs(DateCheck - timenow) / 86400000, 1))
//#endregion

//#region ConvertIndexSLTP2optionsSLTP
// @function to calculate Distance between Points of Entry to the points of Either SL or Target In absoulute values
// @param Specify the "IAO" Strike type acceptable values are {ITM, OTM, or ATM}, HowDeep (Strike Difference like BNF 100 and Nifty 50)
// @returns Returns Calculated points in the string format.
export ConvertIndexSLTP2optionsSLTP(string IAO = "ITM", int HowDeep = 0, float MainSL = 0, float MainTGT = 0) =>
	DOW = dayofweek(timenow)
    Delta = if IAO == "ATM" or HowDeep == 0
		DOW > 3 ? 0.2936 : 0.3578
	else if IAO == "ITM"
		switch HowDeep
			1  =>  DOW > 3 and DOW <= 5 ? 0.3578 : 0.4283
			2  =>  DOW > 3 and DOW <= 5 ? 0.4283 : 0.5023
			3  =>  DOW > 3 and DOW <= 5  ? 0.5023 : 0.5775
			4  =>  DOW > 3 and DOW <= 5 ? 0.5775 : 0.651
			5  =>  DOW > 3 and DOW <= 5 ? 0.651  : 0.7195
			6  =>  DOW > 3 and DOW <= 5 ? 0.7195 : 0.7792
			7  =>  DOW > 3 and DOW <= 5 ? 0.7792 : 0.8195
			8  =>  DOW > 3 and DOW <= 5 ? 0.8195 : 0.8478
			9  =>  DOW > 3 and DOW <= 5 ? 0.8478 : 1
			=>	   1
	else if IAO == "OTM"
		switch HowDeep
			1  =>  DOW > 3 and DOW <= 5 ? 0.2369 : 0.3578
			2  =>  DOW > 3 and DOW <= 5 ? 0.1888 : 0.2936
			3  =>  DOW > 3 and DOW <= 5 ? 0.1481 : 0.2369
			4  =>  DOW > 3 and DOW <= 5 ? 0.1163 : 0.1888
			5  =>  DOW > 3 and DOW <= 5 ? 0.0914 : 0.1481
			6  =>  DOW > 3 and DOW <= 5 ? 0.0747 : 0.1163
			7  =>  DOW > 3 and DOW <= 5 ? 0.0606 : 0.0914
			8  =>  DOW > 3 and DOW <= 5 ? 0.0498 : 0.0747
			=>     0.0426
	
	CalculatedSL		= str.tostring(MainSL  * Delta, format.mintick)
	CalculatedTGT		= str.tostring(MainTGT * Delta, format.mintick)
	Output              = Info_CalculatedSLTP_Level.new(CalculatedSL, CalculatedTGT, math.round_to_mintick(MainSL  * Delta), math.round_to_mintick(MainTGT  * Delta))
//#endregion

//#region NLB_TradingAlert
// @function to Concatenate Leg Strings
// @param TradingAccount Specify the string name of Accout you are trading with
// @param Dir refers to Diection of the Trade (Buy or Sell),  Qty refers to Qty, TradingSymbol refers to Trading Symbol, Exch refers to Exchange (NSE or NFO), Tkn refers to Instrument Token
// @param string OrderType - Acceptable strings "M", "L", "SLL", "SLM", "BO", "DBO"
// @param float LimitPriceByOrSLBO - whats the price difference form market price can be specified here in this param, can also be used for specifying SL for BO order
// @param float TriggerPriceByOrTGTBO - whats the price difference form market price can be specified here in this param, can also be used for specifying TGT for BO order
// @returns Returns Alert String which can be used for trigerring Alert
export NLB_TradingAlert(string TradingAccount, string Dir = "BUY", string Qty, string T_Exch = "NSE", string TradingSymbol, int Tkn = 0, string ProductType = "INTRADAY", int Delay = 0, string OrderType = "M", string LimitPriceByOrSLBO = "", string TriggerPriceByOrTGTBO = "", string TrailSL = "", int LotSize = 15, float Leverage = 1.0)=>
    AccountsWithOutSign = array.from('DHANHQ', 'UPSTOXV2', 'ALICEBLUEV2','BINANCE')
    SameSigns           = array.from('MASTERTRUST', 'ZERODHA','SAMCO')
    AccountName         = TradingAccount == "FYERS" ? "FYERSV2" : TradingAccount == "ALICEBLUE" ? "ALICEBLUEV2" : TradingAccount == "IIFL" ? "IIFLV2" : TradingAccount == "UPSTOX" ? "UPSTOXV2" : TradingAccount
    IsAccWO_Sign        = AccountsWithOutSign.includes(AccountName)
    DelayString         = Delay == 0 ? "" : '"DL":"' + str.tostring(Delay) + '",'
    SameSignAccounts    = SameSigns.includes(AccountName)
    DirectionString     = switch AccountName
        "ANGEL"         =>  OrderType != "BO" ? '{' + DelayString + '"V":"NORMAL","TT":"' + Dir : '{' + DelayString + '"FSL":"' + LimitPriceByOrSLBO + '","FTP":"' + TriggerPriceByOrTGTBO + (TrailSL == "" ? "" : '","FTSL":"' + TrailSL) +'","V":"NORMAL","TT":"' + Dir
        =>                  OrderType != "BO" ? '{' + DelayString + '"TT":"' + Dir : '","TT":"' + Dir
    Exch                =  if AccountName == "FYERSV2"
        T_Exch == "NSE" or T_Exch == "NFO" ? "NSE" : T_Exch == "BSE" or T_Exch == "BFO" ? "BSE" : T_Exch
    else
        T_Exch
    SymbolIdentity      =   switch AccountName
        "FYERSV2"       =>  '","TS":"'  + Exch + ':' + TradingSymbol
        "ANGEL"         =>  Exch == "NSE" ? '","E":"'+ Exch + '","IT":"' + str.tostring(Tkn) + '","TS":"' + TradingSymbol : '","E":"' + Exch + '","TS":"' + TradingSymbol
        "MASTERTRUST"   =>  Exch == "NSE" ? '","E":"'+ Exch + '","IT":"' + str.tostring(Tkn) : '","E":"' + Exch + '","TS":"' + TradingSymbol
        "5PAISA"        =>  Exch == "NSE" ? '","E":"'+ Exch + '","IT":"' + str.tostring(Tkn) + '","TS":"' + TradingSymbol + '","ETYPE":"C' : '","E":"' + Exch + '","TS":"' + TradingSymbol + '","ETYPE":"D'
        =>                  '","E":"'   + Exch + '","TS":"' + TradingSymbol
    QtyIdentity         =   if not str.contains(Qty,"%")
        '","Q":"'   + Qty
    else
        '","Q":"'   + Qty + '","FUND":"EQUITY","LEVERAGE":"' + str.tostring(Leverage) + 'X","PER_LOT_SIZE":"' + str.tostring(LotSize)
    OrderTypeString     =   switch OrderType
        "M"             =>  AccountName == "TRADINGVIEW" ? '","PRICE":"0","LIVE":"TRUE' : Exch != "MCX" ? '","OT":"MARKET' : '","OT":"LIMIT","LTP":"' + (IsAccWO_Sign ? "0.05" : Dir != "BUY" ? '+0.05' : '-0.05')
        "L"             =>  '","OT":"LIMIT","LTP":"'    +   (IsAccWO_Sign ? "" : Dir != "BUY" ? '+' : '-') + LimitPriceByOrSLBO
        "SLL"           =>  '","OT":"SLL","LTP":"'      +   (IsAccWO_Sign ? "" : Dir == "BUY" ? '+' : '-') + LimitPriceByOrSLBO    + '","TRP":"' + (IsAccWO_Sign ? "" : SameSignAccounts ? Dir == "BUY" ? '+' : '-' : Dir != "BUY" ? '+' : '-') + TriggerPriceByOrTGTBO
        "SLM"           =>  '","OT":"SLM","TRP":"'      +   (IsAccWO_Sign ? "" : Dir == "BUY" ? '+' : '-') + TriggerPriceByOrTGTBO
        "BO"            =>  '{' + DelayString + '"FSL":"' + LimitPriceByOrSLBO + '","FTP":"' + TriggerPriceByOrTGTBO + (TrailSL == "" ? "" : '","FTSL":"' + TrailSL)
        "DBO"           =>  '","OT":"LIMIT","LTP":-0","SL":"' + LimitPriceByOrSLBO + '","TP":"' + TriggerPriceByOrTGTBO
        =>                  '","PRICE":"0","LIVE":"TRUE'
    EndString           =   switch AccountName
        "TRADINGVIEW"   =>  '","AT":"' + AccountName +'"}'
        =>                  OrderType != "DBO" ? '","P":"' + ProductType + '","VL":"DAY","AT":"' + AccountName +'"}' : '","P":"BO","VL":"DAY","AT":"' + AccountName +'"}'

    Output              =   if AccountName == "TRADINGVIEW"
        DirectionString +   SymbolIdentity  +   OrderTypeString +   QtyIdentity     +   EndString
    else if OrderType == "BO"
        (AccountName != "ANGEL" ? OrderTypeString : "") +   DirectionString +   SymbolIdentity  +   QtyIdentity     +   '","OT":"MARKET' +  EndString
    else
        DirectionString +   SymbolIdentity  +   QtyIdentity     +   OrderTypeString +  EndString
    OutputCode          = str.upper(Output)
//#endregion

//#region NLB_CancelClose
// @function to Contstruct the Alert Syntax for closing and cancelling open positions.
// @param TradingAccount Specify the string name of Accout you are trading with
// @param SymbolExpiry Specify the string text which can include symbol name and expirt date of symbol
// @returns Returns Close & Cancellation Alert String
export NLB_CancelClose(string CloseType = "", string TradingAccount = "X", string Exch_ = "NSE", string SymbolNameAndOrExpiry = "X", string CancelType = "", string Product = "", int Delay = 0, bool DhanHQ_As = false)=>
    AsString            = DhanHQ_As ? '"AS":"' : '"CLOSE":"CLOSE","TS":"'
    DelayString         = Delay == 0 ? "" : '"DL":"' + str.tostring(Delay) + '",'
    AccountName         = TradingAccount == "FYERS" ? "FYERSV2" : TradingAccount == "ALICEBLUE" ? "ALICEBLUEV2" : TradingAccount == "IIFL" ? "IIFLV2" : TradingAccount == "UPSTOX" ? "UPSTOXV2" : TradingAccount
    Exch                =  if AccountName == "FYERSV2"
        Exch_ == "NSE" or Exch_ == "NFO" ? "NSE" : Exch_ == "BSE" or Exch_ == "BFO" ? "BSE" : Exch_
    else
        Exch_    
    SymbolName          = TradingAccount == "FYERS" ? Exch + ':' + SymbolNameAndOrExpiry : SymbolNameAndOrExpiry
    ProductCode         = Product == "" ? "" : '","P":"' + Product
    CancelClode         = '{' + DelayString + '"CANCEL":"CANCEL' + ProductCode + '","TS":"' + SymbolName + '","AT":"' + AccountName +'"}'
    OutputCode          = if (CancelType != "" and CloseType == "A" ) and AccountName != "TRADINGVIEW"
        if AccountName != "FYERSV2"
            '{' + DelayString + '"CANCEL":"CANCEL' + ProductCode + '","TS":"' + SymbolName + '","AT":"' + AccountName +'"}, {"CLOSE":"CLOSE' + ProductCode + '","AT":"' + AccountName +'"}'
        else
            '{' + DelayString + '"CANCEL":"CANCEL' + ProductCode + '","E":"' + Exch + '","TS":"' + SymbolName + '","AT":"' + AccountName +'"}, {"CLOSE":"CLOSE' + ProductCode + '","AT":"' + AccountName +'"}'
    else if (CancelType != "" and CloseType != "" ) and AccountName == "TRADINGVIEW"
        '{' + DelayString + '"CANCEL":"CANCEL' + ProductCode + '","TS":"' + SymbolName + '","AT":"' + AccountName +'"}, {"AS":"' + SymbolName + '","E":"' + Exch + '","PRICE":"0","LIVE":"TRUE' + ProductCode + '","AT":"' + AccountName +'"}'
    else if (CloseType == "" and CancelType != "")
        CancelClode
    else if CancelType == "" and CloseType == "A" and AccountName != "TRADINGVIEW"
        '{' + DelayString + '"CLOSE":"CLOSE' + ProductCode + '","AT":"' + AccountName +'"}'
    else if CancelType == "" and CloseType == "S" and AccountName != "TRADINGVIEW"
        Part2CancelCode = switch AccountName
            "FYERSV2"       =>  '{' + DelayString + '"AS":"' + SymbolName + ProductCode + '","AT":"' + AccountName +'"}'
            "DHANHQ"        =>  '{' + DelayString + AsString + SymbolName + ProductCode + '","E":"' + Exch + '","AT":"' + AccountName +'"}'
            =>                  '{' + DelayString + '"AS":"' + SymbolName + ProductCode + '","E":"' + Exch + '","AT":"' + AccountName +'"}'
    else if CancelType == "S" and CloseType == "S" and AccountName != "TRADINGVIEW"
        Part2CancelCode = switch AccountName
            "FYERSV2"       =>  CancelClode + ',' + '{' + DelayString + '"AS":"' + SymbolName + ProductCode + '","AT":"' + AccountName +'"}'
            "DHANHQ"        =>  CancelClode + ',' + '{' + DelayString + AsString + SymbolName + ProductCode + '","E":"' + Exch + '","AT":"' + AccountName +'"}'
            =>                  CancelClode + ',' + '{' + DelayString + '"AS":"' + SymbolName + ProductCode + '","E":"' + Exch + '","AT":"' + AccountName +'"}'
    else if CancelType == "" and CloseType != "" and AccountName == "TRADINGVIEW"
        '{' + DelayString + '"E":"' + Exch + '","AS":"' + SymbolName + '","PRICE":"0","LIVE":"TRUE","CLOSE":"CLOSE' + ProductCode + '","AT":"TRADINGVIEW"}'
//#endregion

//#region ArrayCombiner
// @function to suffix ", " to the list of string of Array values supplied.
export ArrayCombiner(string[] LegsToCombine, string BotType = "", string DhanHQ_Secret = "")=>
    OP_Array = array.new_string(0)
    for [index, value] in LegsToCombine
        if str.length(value) > 2 and not OP_Array.includes(value)
            OP_Array.push(value)
    Ouput = if str.startswith(str.upper(BotType), "ALGOJI") or BotType == ""
        array.join(OP_Array,"\n")
    else if str.startswith(str.upper(BotType), "N-") or str.startswith(str.upper(BotType), "B-") or str.upper(BotType) == "NLB" or str.upper(BotType) == "BONLB"
        "[" + array.join(OP_Array,", ") + "]"
    else if str.startswith(str.upper(BotType), "DHANHQ") or str.startswith(str.upper(BotType), "DHAN")
        BuyLegs             = array.new_string(0)
        SellLegs            = array.new_string(0)
        for [index, value] in OP_Array
            if str.contains(value, '"transactionType": "B"')
                BuyLegs.push(value)
            else
                SellLegs.push(value)
        CombinedLegs        = BuyLegs.concat(SellLegs)
        UpdateSortOrder     = array.new_string(0)
        for [stdIdx, ech] in CombinedLegs
            UpdateSortOrder.push(str.replace(ech, "$", str.tostring(stdIdx), 0))
        '{"secret": "' + DhanHQ_Secret + '","alertType": "multi_leg_order","order_legs": [\n' + array.join(UpdateSortOrder,",\n") + "\n]}"

//#endregion

//#region NLB_Cal_TradingAlert
// @function to create String concatination which can be used for trigerring Alerts.
// @param Calculate - true for calculation options strike string
// @param IAO - for ITM or OTM or ATM strike Price
// @param F_CE - true for for CALL and false for PUT
// @param int SDiff (default=100) Specify the strike difference value.  eg = Nifty = 50 and BNF = 100
// @param float SVal (default=0) Specify the source value.
// @param float C_Pct (default=0, min = 0 and max = 1) Specify the percentage value. eg if calibarion is set for 50% on the banknifty close price with 30030.5 ITM CE 1 deep will show 29900 how ever if close price is 30070.5 ITM CE with 1 deep will show 30000 
// @param BotType specify the BOT type, ALGOJI or NEXTLEVEL_BOT
// @param C_Symbol specify the Basic Trading Symbol or WithExpiry_Symbol for FUTURES or OPTIONS
// @param Exc specify the exchange symbol reference
// @param MOrder - if set to true then creates string for Market order for Limit Order String set the param to false
// @param MPricePlus - for limit orders setting this param to true will help for creating a string with sell high trade types
// @param LMTPriceBy - whats the price difference form market price can be specified here in this param
// @returns Returns Intraday Alert String which can be used for trigerring Alert
export NLB_Cal_TradingAlert( string BotType = "NLB", bool Calculate = true, string IAO = "ITM", int Deep = 1, int SDiff = 100, bool F_CE = true, string src = 'c', float S_Val = 0, float C_Pct = 0.0, string TAccount = "X", string Exc = "NSE", string C_Symbol = "X", int tkn = 0, string C_Dir, string C_Qty, string MOrder = "M", string LMTPriceBy = "0", string TrgPriceBy = "0", string Product = 'INTRADAY', int Delay = 0, string TrailSL ="", int LotSize = 15, float Leverage = 1.0, string Tg = "STG1", string Instr = "OPTIDX", string EnOEx = "EN", bool CalculateSL_TGT = false)=>
    string SL_For_BO        = ""
    string TGT_For_BO       = ""
    if not CalculateSL_TGT
        SL_For_BO           := LMTPriceBy
        TGT_For_BO          := TrgPriceBy
    else
        GetInputs            = ConvertIndexSLTP2optionsSLTP(IAO, Deep, str.tonumber(LMTPriceBy), str.tonumber(TrgPriceBy))
        SL_For_BO           := GetInputs.SL_Text
        TGT_For_BO          := GetInputs.TP_Text

    string  Algo            = ""
    string  Fyers           = ""
    string  Finvasia        = ""
    string  TradingView     = ""
    string  TradingSymbol   = ""
    float   IOM_value       = 0
    string  UBotType        = str.upper(BotType)
    if Calculate
        GetInputsForIAO = switch IAO
            "ATM"           => G_ATM(SDiff, F_CE, src, S_Val, C_Pct)
            "ITM"           => G_ITM(Deep, SDiff, F_CE, src, S_Val, C_Pct)
            "OTM"           => G_OTM(Deep, SDiff, F_CE, src, S_Val, C_Pct)
        IOM_value           := GetInputsForIAO.IAO_Value
        Algo                := GetInputsForIAO.API_Bridge_Format
        Fyers               := GetInputsForIAO.FyersFormat
        Finvasia            := GetInputsForIAO.FinvasiaFormat
        TradingView         := str.replace(Algo,"|","",0)
        TradingSymbol       := if UBotType == "ALGOJI_D"
            str.upper(C_Symbol) + str.replace(Algo,"|","",0) + "|" + Algo
        else if UBotType == "ALGOJI"
            str.upper(C_Symbol) + "|" + Algo 
        else if UBotType == "DHANHQ"
            str.upper(C_Symbol) + "_" + GetInputsForIAO.DhanStrikeFormat + "_" + (F_CE ? "CE" : "PE") 
        else if str.contains(str.upper(TAccount),"FYERS")
            str.upper(C_Symbol) + Fyers 
        else if str.contains(str.upper(TAccount),"FINVASIA")    
            str.upper(C_Symbol) + Finvasia
        else
            str.upper(C_Symbol) + TradingView
    else
        TradingSymbol   := str.upper(C_Symbol)
    Output              = if str.startswith(UBotType,"ALGOJI")
        AlgoJiIntraMKTAlert(TradingSymbol, EnOEx, C_Dir, int(str.tonumber(C_Qty)), (Calculate ? "OPTIDX" : Instr), Tg)
    else if UBotType == "NLB" or str.startswith(UBotType, "N-") or str.startswith(UBotType, "B-") or UBotType == "BONLB"
        NLB_TradingAlert(TAccount, C_Dir, C_Qty, Exc, TradingSymbol, tkn, Product, Delay, MOrder, SL_For_BO, TGT_For_BO, TrailSL, LotSize, Leverage)
    else if UBotType == "DHANHQ" or str.startswith(UBotType, "DHAN")
        DhanHQ_Webhook(Calculate, C_Dir, MOrder, Product, str.upper(C_Symbol), C_Qty, SL_For_BO, Exc, F_CE, NumAsText(IOM_value, 1) + ".0")
    [IOM_value, TradingSymbol, Output]
//#endregion

//#endregion

//=====================================================================================================
//#region OccationalUsuage

//#region GetTicker
// @function to extract Symbol Name
// @param _str SymbolName with Exchange prefix
// @param _of Separator default is ":"
export GetTicker(string _str, string _of = ":") =>
    // string _str: string to separate.
    // string _op : separator character.
    string[] _chars     = str.split(_str, "")
    int _len            = array.size(_chars)
    int _ofPos          = array.indexof(_chars, _of)
    string[] _substr    = array.new_string(0)
    if _ofPos >= 0 and _ofPos < _len - 1
        _substr         := array.slice(_chars, _ofPos + 1, _len)
    string _return      = array.join(_substr, "")
//#endregion 

//#region GetTickerPrfx
// @function to extract Symbol Prefix
// @param _str SymbolName with Exchange prefix
// @param _of Separator default is ":"
export GetTickerPrfx(string _str, string _of = ":") =>
    // string _str: string to separate.
    // string _op : separator character.
    string[] _chars     = str.split(_str, "")
    int _len            = array.size(_chars)
    int _ofPos          = array.indexof(_chars, _of)
    string[] _substr    = array.new_string(0)
    if _ofPos >= 0 and _ofPos < _len - 1
        _substr         := array.slice(_chars, 0, _ofPos)
    string _return      = array.join(_substr, "")
//#endregion

//#region TxtSize
var SampleInputForText = input.string("Normal","Text Size to Select",["Auto", "Tiny", "Small", "Normal", "Large", "Huge"])
// @function Function to Get size Value for text values used in Lables 
// @param string SizeValue = auto, tiny, small, normal, large, huge. specify either of these values or default value Normal will be displayed as output
// @returns Returns Respective Text size
export TxtSize(simple string SizeValue = "Normal") =>
    string Output = switch SizeValue
        "Auto"  =>  size.auto
        "Tiny"  =>  size.tiny
        "Small" =>  size.small
        "Large" =>  size.large
        "Huge"  =>  size.huge
        =>          size.normal
//#endregion

//#region LnStyle
var SampleInputForLineStyle = input.string(title='Line Style', options=['solid (─)', 'dotted (┈)', 'dashed (╌)', 'arrow left (←)', 'arrow right (→)', 'arrows both (↔)'], defval='dotted (┈)')
// @function Function to Get Line Style Value for text values used in Lines
// @param string LineType = 'solid (─)', 'dotted (┈)', 'dashed (╌)', 'arrow left (←)', 'arrow right (→)', 'arrows both (↔)' or default line style 'dotted (┈)' will be the output
// @returns Returns Respective Line style
export LnStyle(simple string LineType = "dotted (┈)") =>
    string Output = switch LineType
        "solid (─)"         =>  line.style_solid
        "dashed (╌)"        =>  line.style_dashed
        "arrow left (←)"    =>  line.style_arrow_left
        "arrow right (→)"   =>  line.style_arrow_right
        "arrows both (↔)"   =>  line.style_arrow_both
        =>                      line.style_dotted
//#endregion

//#region ShapeType
//shape.triangleup, shape.triangledown, shape.flag, shape.circle, shape.arrowup, shape.arrowdown, shape.labelup, shape.labeldown, 
// @function Function to Get Shape Style Value for text values used in plot shapes
// @param string ShapeType = 'XCross', 'Cross', 'Triangle Up', 'Triangle Down', 'Flag', 'Circle','Arrow Up', 'Arrow Down','Lable Up', 'Lable Down' or default shpae style Triangle Up will be the output
// @returns Returns Respective Shape style
export ShapeType(simple string ShapeType = 'Triangle Up') =>
    string Output = switch ShapeType
        'Triangle Down'     =>  shape.triangledown
        'Arrow Up'          =>  shape.arrowup
        'Arrow Down'        =>  shape.arrowdown
        'Lable Up'          =>  shape.labelup
        'Lable Down'        =>  shape.labeldown
        'Flag'              =>  shape.flag
        'Circle'            =>  shape.circle
        'Square'            =>  shape.square
        'Dimond'            =>  shape.diamond
        =>                      shape.triangleup
//#endregion

//#region G_TablePositon
var SampleInputForTablePosition = input.string( title = "Table Position", defval = "LeftCenter", options = ["TopLeft", "BottomLeft", "LeftCenter", "RightCenter", "TopRight", "BottomRight","TopCenter","BottomCenter"])
export G_TablePositon(simple string TablePosition) =>
    switch TablePosition
        "TopLeft"       => position.top_left
        "BottomLeft"    => position.bottom_left
        "LeftCenter"    => position.middle_left
        "RightCenter"   => position.middle_right
        "TopRight"      => position.top_right
        "TopCenter"     => position.top_center
        "BottomCenter"  => position.bottom_center
        => position.bottom_right
//#endregion        

//#region IsPoweredPushBuy
// @function To Check if the candle is a powered Push or Not
export IsPoweredPushBuy(int LookBackBars, bool RepaintingVersion = true) =>
    MCPct               = G_CandleInfo("MCPct",0,RepaintingVersion)
    HasMoreBodyH        = ta.sma(MCPct, LookBackBars) * 2.5
    candlesize          = G_CandleInfo("Candle",0,RepaintingVersion)
    wicksize            = G_CandleInfo("Wick",0,RepaintingVersion)    
    [Opn, Cls, Hgh, Lw] = CustomCandleInfoFx(RepaintingVersion,false)
    bool Output         = false
    if Opn < Cls and MCPct > HasMoreBodyH and ((Opn==Lw and Cls==Hgh) or ((Cls >= (Hgh - (candlesize * 0.05))) and (wicksize < math.abs(Opn - Cls))) or (Cls==Hgh and (wicksize < math.abs(Opn - Cls))))
        Output          := true
//#endregion 

//#region IsPoweredPushSell
// @function To Check if the candle is a powered Push or Not
export IsPoweredPushSell(int LookBackBars, bool RepaintingVersion = true) =>
    MCPct               = G_CandleInfo("MCPct",0,RepaintingVersion)
    HasMoreBodyH        = ta.sma(MCPct, LookBackBars) * 2.5
    candlesize          = G_CandleInfo("Candle",0,RepaintingVersion)
    wicksize            = G_CandleInfo("Wick",0,RepaintingVersion)
    [Opn, Cls, Hgh, Lw] = CustomCandleInfoFx(RepaintingVersion,false)
    bool Output         = false
    if Opn > Cls and MCPct > HasMoreBodyH and ((Opn==Lw and Cls==Hgh) or ((Cls <= (Lw + (candlesize * 0.05))) and (wicksize < math.abs(Opn - Cls))) or (Cls==Lw and (wicksize < math.abs(Opn - Cls))))
        Output          := true
//#endregion 

//#region G_Delta
// @function Gets Delta Output of the technical analyis indicator
// @param int ShortLength to be specified default is 3
// @param int LongLength to be specified default is 8
// @param simple string MAType for the TA indicator supported are  EMA, SMA, HMA
// @param bool RepaintingVersion calculate on realtime bar? default is true.
// @returns Value with the given parameters
export G_Delta(simple int ShortLength = 3,simple int LongLength = 8,simple string MAType = "EMA", bool RepaintingVersion = true)=>
    if MAType == "EMA" or MAType == "SMA" or MAType == "HMA"
        SlowEMA     = G_Indicator_Val(MAType,LongLength, G_CandleInfo("CandleClose",0,RepaintingVersion))
        FastEMA     = G_Indicator_Val(MAType,ShortLength, G_CandleInfo("CandleClose",0,RepaintingVersion))
        StTermDelta = ((FastEMA - SlowEMA) / hlc3 ) * 100
//#endregion 

//#region G_BullBearBarCount
// @function Counts how many green & red bars have printed recently (ie. pullback count)
// @param int HowManyCandlesToCheck The lookback period to look back over
// @param int BullBear The color of the bar to count (1 = Bull, -1 = Bear), Open = close candles are ignored
// @param float AboveOrBelow if in case you would need to count only green candles wich are close above 50% of previous candle then use 0.5
// @returns The bar count of how many candles have retraced over the given lookback with specific candles
export G_BullBearBarCount(int HowManyCandlesToCheck, int BullBear = 1, float AboveOrBelow = 0) =>
    CandlesCounter  = 0
    TestTheValue    = G_CandleInfo("ExtractVal",0,false,false,AboveOrBelow)
    for i = 1 to HowManyCandlesToCheck by 1
        if BullBear == 1 and close[i] > open[i] and close[i] >= TestTheValue // Count green bars
            CandlesCounter := CandlesCounter + 1
        if BullBear == -1 and close[i] < open[i] and close[i] <= TestTheValue  // Count red bars
            CandlesCounter := CandlesCounter + 1
    CandlesCounter
//#endregion 

//#region BarToStartYourCalculation
// @function function to get candle co-ordinate in order to use it further for calculating your analysis work . "Heart full Thanks to 3 Pine motivators (LonesomeTheBlue, Myank & Sriki) who helped me cracking this logic"
// @param Int SelectedCandleNumber (default=450) How many candles you would need to anlysie in your script from the right.
// @returns A boolean - output is returned to say the starting point and continue to diplay true for the future candles
export BarToStartYourCalculation(int SelectedCandleNumber = 450) =>
    UTC = 0.
    // get UTC time
    int timenow_    = timenow + math.round(UTC * 3600000)
    int time_       = time + math.round(UTC * 3600000)

    // calculate D/H/M/S for the bar which we are on and time now
    int daynow_     = math.floor((timenow_ + 86400000 * 3) / 86400000) % 7
    int day_        = math.floor((time_ + 86400000 * 3) / 86400000) % 7
    int hournow_    = math.floor(timenow_ / 3600000) % 24
    int hour_       = math.floor(time_ / 3600000) % 24
    int minutenow_  = math.floor(timenow_ / 60000) % 60
    int minute_     = math.floor(time_ / 60000) % 60
    int secondnow_  = math.floor(timenow_ / 60000) % 60
    int second_     = math.floor(time_ / 60000) % 60
    int hmstime_    = hour_ * 3600000 + minute_ * 60000 + second_ * 1000
    int hmstimenow_ = hournow_ * 3600000 + minutenow_ * 60000 + secondnow_ * 1000

    var int starttime = na
    if bar_index > SelectedCandleNumber and (na(starttime) or time_ < nz(starttime))
        if timeframe.isdaily
            if day_ == daynow_  // session day
                diff            = time_ - time_[SelectedCandleNumber]
                starttime       := timenow_ - diff
            if daynow_ > day_ and daynow_ > day_[1] and day_[1] > day_  // weekend
                diff            = time_[1] - time_[SelectedCandleNumber + 1] + (daynow_ - day_[1]) * 86400000
                starttime       := timenow_ - diff
        if timeframe.isintraday
            if day_ == daynow_ and day_[1] == daynow_ and hmstime_ > hmstimenow_ and hmstime_[1] <= hmstimenow_  // in session
                diff            = time_[1] - time_[SelectedCandleNumber + 1] + math.round(UTC * 3600000)
                starttime       := timenow_ - diff
            if day_ != daynow_ and day_[1] == daynow_ and hmstime_ < hmstimenow_ and hmstime_[1] <= hmstimenow_  // in same day and after session
                diff            = time_[1] - time_[SelectedCandleNumber + 1] + hmstimenow_ - hmstime_[1] + math.round(UTC * 3600000)
                starttime       := timenow_ - diff
            if day_ == daynow_ and day_[1] + 1 == daynow_ and hmstime_ > hmstimenow_ and hmstime_[1] >= hmstimenow_  // in week (not first day) and before session
                diff            = time_[1] - time_[SelectedCandleNumber + 1] + 24 * 3600000 - hmstime_[1] + hmstimenow_ + math.round(UTC * 3600000)
                starttime       := timenow_ - diff
            if day_ == daynow_ and day_[1] > daynow_ and hmstime_ > hmstimenow_ and hmstime_[1] >= hmstimenow_  // the day after weekend before session
                diff            = time_[1] - time_[SelectedCandleNumber + 1] + 2 * 86400000 + 24 * 3600000 - hmstime_[1] + hmstimenow_ + math.round(UTC * 3600000)
                starttime       := timenow_ - diff
            if daynow_ > day_ and daynow_ > day_[1] and day_[1] > day_  // weekend
                diff            = time_[1] - time_[SelectedCandleNumber + 1] + (daynow_ - day_[1] - 1) * 86400000 + 24 * 3600000 - hmstime_[1] + hmstimenow_ + math.round(UTC * 3600000)
                starttime       := timenow_ - diff
        if timeframe.isweekly
            starttime           := timenow_ - timeframe.multiplier * SelectedCandleNumber * 604800000  // 1 week = 604800000ms
        if timeframe.ismonthly
            starttime           := timenow_ - timeframe.multiplier * SelectedCandleNumber * 2629743000  // 1 month = 2629743000ms

    GetBoolOutput               = not na(starttime) and time >= starttime
//#endregion 

//#region isHammer
// @function Checks if the previous bar is a hammer candle based on the given parameters
// @param float fib (default=0.382) The fib to base candle body on
// @param bool colorMatch (default=false) Does the candle need to be green? (true/false)
// @param bool NeedRepainting (default=false) Specify True if you need them to calculate on the realtime bars
// @returns A boolean - true if the previous bar matches the requirements of a hammer candle
export isHammer(float fib = 0.382, bool colorMatch = false, bool NeedRepainting = false) =>
    o           = G_CandleInfo("CandleOpen",0,NeedRepainting)
    h           = G_CandleInfo("CandleHigh",0,NeedRepainting)
    l           = G_CandleInfo("CandleLow",0,NeedRepainting)
    c           = G_CandleInfo("CandleClose",0,NeedRepainting)
    bullFib     = (l - h) * fib + h
    lowestBody  = c < o ? c : o
    lowestBody >= bullFib and (not colorMatch or c > o)
//#endregion 

//#region isStar
// @function Checks if the previous bar is a shooting star candle based on the given parameters
// @param float fib (default=0.382) The fib to base candle body on
// @param bool colorMatch (default=false) Does the candle need to be red? (true/false)
// @param bool NeedRepainting (default=false) Specify True if you need them to calculate on the realtime bars
// @returns A boolean - true if the previous bar matches the requirements of a shooting star candle
export isStar(float fib = 0.382, bool colorMatch = false, bool NeedRepainting = false) =>
    o           = G_CandleInfo("CandleOpen",0,NeedRepainting)
    h           = G_CandleInfo("CandleHigh",0,NeedRepainting)
    l           = G_CandleInfo("CandleLow",0,NeedRepainting)
    c           = G_CandleInfo("CandleClose",0,NeedRepainting)
    bearFib     = (h - l) * fib + l
    highestBody = c > o ? c : o
    highestBody <= bearFib and (not colorMatch or c < o)
//#endregion 

//#region isDoji
// @function Checks if the previous bar is a doji candle based on the given parameters
// @param float wickSize (default=1.5 times) The maximum allowed times can be top wick size compared to the bottom (and vice versa) 
// @param float bodySize (default= 5 percent to be mentioned as 0.05) The maximum body size as a percentage compared to the entire candle size
// @param bool NeedRepainting (default=false) Specify true if you need them to calculate on the realtime bars
// @returns A boolean - true if the previous bar matches the requirements of a doji candle
export isDoji(float wickSize = 1.5, float bodySize = 0.05, bool NeedRepainting = false) =>
    G_CandleInfo("TWick",0,NeedRepainting) <= G_CandleInfo("BWick",0,NeedRepainting) * wickSize and
     G_CandleInfo("BWick",0,NeedRepainting) <= G_CandleInfo("TWick",0,NeedRepainting) * wickSize and
     G_CandleInfo("BodyPct",0,NeedRepainting) <= bodySize
//#endregion 

//#region isBullishEC
// @function Checks if the previous bar is a bullish engulfing candle
// @param float allowance (default=0) How many POINTS to allow the open to be off by (useful for markets with micro gaps)
// @param float rejectionWickSize (default=disabled) The maximum rejection wick size compared to the body as a percentage
// @param bool engulfWick (default=false) Does the engulfing candle require the wick to be engulfed as well?
// @param bool NeedRepainting (default=false) Specify True if you need them to calculate on the realtime bars
// @returns A boolean - true if the previous bar matches the requirements of a bullish engulfing candle
export isBullishEC(float allowance = 0.0, float rejectionWickSize = 0.0, bool engulfWick = false, bool NeedRepainting = false) =>
    o           = G_CandleInfo("CandleOpen",0,NeedRepainting)
    h           = G_CandleInfo("CandleHigh",0,NeedRepainting)
    l           = G_CandleInfo("CandleLow",0,NeedRepainting)
    c           = G_CandleInfo("CandleClose",0,NeedRepainting)
    (c[1] <= o[1] and 
     c >= o[1] and
     o <= c[1] + allowance) and
     (not engulfWick or c >= h[1]) and 
     (rejectionWickSize == 0.0 or G_CandleInfo("TWick",0,NeedRepainting) / G_CandleInfo("Body",0,NeedRepainting) < rejectionWickSize)
//#endregion 

//#region isBearishEC
// @function Checks if the previous bar is a bearish engulfing candle
// @param float allowance (default=0) How many POINTS to allow the open to be off by (useful for markets with micro gaps)
// @param float rejectionWickSize (default=disabled) The maximum rejection wick size compared to the body as a percentage
// @param bool engulfWick (default=false) Does the engulfing candle require the wick to be engulfed as well?
// @param bool NeedRepainting (default=false) Specify True if you need them to calculate on the realtime bars
// @returns A boolean - true if the previous bar matches the requirements of a bearish engulfing candle
export isBearishEC(float allowance = 0.0, float rejectionWickSize = 0.0, bool engulfWick = false, bool NeedRepainting = false) =>
    o           = G_CandleInfo("CandleOpen",0,NeedRepainting)
    h           = G_CandleInfo("CandleHigh",0,NeedRepainting)
    l           = G_CandleInfo("CandleLow",0,NeedRepainting)
    c           = G_CandleInfo("CandleClose",0,NeedRepainting)
    (c[1] >= o[1] and
     c <= o[1] and
     o >= c[1] - allowance) and
     (not engulfWick or c <= l[1]) and
     (rejectionWickSize == 0.0 or G_CandleInfo("BWick",0,NeedRepainting) / G_CandleInfo("Body",0,NeedRepainting) < rejectionWickSize)
//#endregion 

//#region Plot_TrendLineAtDegree
// @function helps you to plot the Trendlines based on the specified angle at the defined price to bar ratio
// @param float Degree (default=14) angle at which Trendline to be plot
// @param float price2bar_ratio (default=1e-10) The maximum rejection wick size compared to the body as a percentage
// @param int Bars2Plot (default=6) Does the engulfing candle require the wick to be engulfed as well?
// @param string LineStyle = 'solid (─)', 'dotted (┈)', 'dashed (╌)', 'arrow left (←)', 'arrow right (→)', 'arrows both (↔)' or default line style 'dotted (┈)' will be the output
// @param bool PlotOnOpen_Close (default=false) Specify True if you need them to calculate on the Open\Close Values
// @returns plot the Trendlines based on the specified angle at the defined price to bar ratio
export Plot_TrendLineAtDegree(float Degree = 14.0, float price2bar_ratio = 1e-10, int Bars2Plot = 6,simple string LineStyle = "dotted (┈)", bool PlotOnOpen_Close = false) =>
    float valMulti      = 1.739e-12
    float TimeMulti     = 1e-10
    float TimeDivider   = price2bar_ratio / TimeMulti
    float Val_Multi     = TimeDivider * (Degree * valMulti)
    wheretoPlot_Top     = PlotOnOpen_Close ? math.max(open, close) : high 
    wheretoPlot_Bottom  = PlotOnOpen_Close ? math.min(open, close) : low 
    
    var HighLines   = array.new_line(Bars2Plot)
    var LowLines    = array.new_line(Bars2Plot)
    for ab = 0 to (Bars2Plot - 1) by 1
        line.delete(array.get(HighLines, ab))
        line.delete(array.get(LowLines, ab))
        float HVal2Plot = wheretoPlot_Top[ab] + Val_Multi
        float LVal2Plot = wheretoPlot_Bottom[ab] - Val_Multi
        array.set(HighLines, ab, 
                  line.new(x1   = bar_index[ab], 
                          y1    = wheretoPlot_Top[ab], 
                          x2    = bar_index[ab] + 1, 
                          y2    = HVal2Plot,
                          color = color.green,
                          extend= extend.right,
                          style = LnStyle(LineStyle)))
        array.set(LowLines, ab, 
                  line.new(x1   = bar_index[ab], 
                          y1    = wheretoPlot_Bottom[ab], 
                          x2    = bar_index[ab] + 1, 
                          y2    = LVal2Plot,
                          color = color.lime,
                          extend= extend.right,
                          style = LnStyle(LineStyle)))
//#endregion 

//#region G_HighFreqValue
// @function to retrieve the highest frequency value for the given lookback period
// @param int LookBackPeriod - default is 50
// @param float FilterVal1 - default is 0 (include All)
// @param float FilterVal2 - default is 0 (will be used only in between scenario)
// @param string FilterValOperator - Set to E for ==, NE for !=, G for >, GE for >=, L for <, LE for <= , BE for <= and >= (Between with Equal) and ather string for >< (Between with greater Lesser)
// @param minTouch = Minimum Touches to be tested (default is 3)
// @param ErrorRateTicks = if in case of exact touch is not happened how much error rate can be included (default is 10 minticks)
// @param bool IncludeOpen - default is true
// @param bool IncludeHigh - default is true
// @param bool IncludeLow - default is true
// @param bool IncludeClose - default is true
// @returns tuples with MaxTouchValue and HighestRange and LowestRange
export G_HighFreqValue(int LookBackPeriod = 50,float FilterVal1 = 0,float FilterVal2 = 0 ,string FilterValOperator = "G",int minTouch = 3, int ErrorRateTicks = 10, bool IncludeOpen = true, bool IncludeHigh = true, bool IncludeLow = true, bool IncludeClose = true) =>
    bool NeedMaxTouch           = true
    FilteredListOfArray         = array.new_float(0)
    int LoadedValues            = 0
    loadAllValues               = false
    HighestH                    = ta.highest(LookBackPeriod)
    LowestL                     = ta.lowest(LookBackPeriod)    
    if (FilterValOperator == "G" or FilterValOperator == "GE") and (FilterVal1 == 0 or FilterVal1 <= LowestL)
        loadAllValues := true
    if (FilterValOperator == "L" or FilterValOperator == "LE") and FilterVal1 >= HighestH
        loadAllValues := true
    for filval = 0 to LookBackPeriod by 1
        if not loadAllValues
            if IncludeOpen and OperatorChk(open[filval+ 1],FilterValOperator,FilterVal1, FilterVal2)
                array.push(FilteredListOfArray,open[filval+ 1])
                LoadedValues+=1
            if IncludeHigh and OperatorChk(high[filval + 1],FilterValOperator,FilterVal1, FilterVal2)
                array.push(FilteredListOfArray,high[filval + 1])
                LoadedValues+=1
            if IncludeLow and OperatorChk(low[filval + 1],FilterValOperator,FilterVal1, FilterVal2)
                array.push(FilteredListOfArray,low[filval + 1])
                LoadedValues+=1
            if IncludeClose and OperatorChk(close[filval + 1],FilterValOperator,FilterVal1, FilterVal2)
                array.push(FilteredListOfArray,close[filval + 1])
                LoadedValues+=1
        else
            array.push(FilteredListOfArray,open[filval+ 1])
            array.push(FilteredListOfArray,high[filval + 1])
            array.push(FilteredListOfArray,low[filval + 1])
            array.push(FilteredListOfArray,close[filval + 1])
            LoadedValues+=4          
    UniqueListOfFilter      = array.new_float(0)
    int intialLoop          = 0
    int ValuesLoaded        = 0
    HowManyUniqueFound = for ohlc_val in FilteredListOfArray
        if intialLoop == 0
            array.push(UniqueListOfFilter,ohlc_val)
            ValuesLoaded+=1
        else
            if not array.includes(UniqueListOfFilter,ohlc_val)
                array.push(UniqueListOfFilter,ohlc_val) 
                ValuesLoaded+=1
        intialLoop+=1
        ValuesLoaded
    int MaxRepeatFound          = NeedMaxTouch ? minTouch < 3 ? 3 : minTouch : minTouch
    float LimitTicks            = ErrorRateTicks * syminfo.mintick
    float LimitTicks_           = (ErrorRateTicks * 10) * syminfo.mintick
    TimesRepeatedArray          = array.new_float(0)    
    ListOfOutputValue           = array.new_float(0)   
    UpSideTimesRepeatedArray    = array.new_float(0)    
    UpSideListOfOutputValue     = array.new_float(0)   
    DnSideTimesRepeatedArray    = array.new_float(0)    
    DnSideListOfOutputValue     = array.new_float(0)   
    for UnqVal in UniqueListOfFilter
        int TimesRepeated = 0
        CalculateRepeats = for ohlc_val in FilteredListOfArray
            if (ohlc_val >= (UnqVal - LimitTicks) and ohlc_val <= (UnqVal + LimitTicks))
                TimesRepeated += 1
            TimesRepeated
        if CalculateRepeats > MaxRepeatFound
            array.push(ListOfOutputValue,UnqVal)
            array.push(TimesRepeatedArray,CalculateRepeats)
        else
            if CalculateRepeats <= MaxRepeatFound
                if OperatorChk(UnqVal,"GE",HighestH - LimitTicks_, 0)
                    array.push(UpSideListOfOutputValue,UnqVal)
                    array.push(UpSideTimesRepeatedArray,CalculateRepeats)
                if OperatorChk(UnqVal,"BE",LowestL, LowestL + LimitTicks_)
                    array.push(DnSideListOfOutputValue,UnqVal)
                    array.push(DnSideTimesRepeatedArray,CalculateRepeats)                    
    
    MaxOfRepeats            = array.size(TimesRepeatedArray) > 0 ? array.max(TimesRepeatedArray) : 0
    IndexOfMaxRepeat        = MaxOfRepeats != 0 ? array.indexof(TimesRepeatedArray,MaxOfRepeats) : -1
    ValueTestedMaxNoOfTimes = IndexOfMaxRepeat != -1 ? array.get(ListOfOutputValue,IndexOfMaxRepeat) : 0
    
    H1_MaxOfRepeats         = array.size(UpSideTimesRepeatedArray) > 0 ? array.max(UpSideTimesRepeatedArray) : 0
    H1_IndexOfMaxRepeat     = H1_MaxOfRepeats != 0 ? array.indexof(UpSideTimesRepeatedArray,H1_MaxOfRepeats) : -1
    H1_ValueRepeatedMax     = H1_IndexOfMaxRepeat != -1 ? array.get(UpSideListOfOutputValue,H1_IndexOfMaxRepeat) : 0

    L1_MaxOfRepeats         = array.size(DnSideTimesRepeatedArray) > 0 ? array.max(DnSideTimesRepeatedArray) : 0
    L1_IndexOfMaxRepeat     = L1_MaxOfRepeats != 0 ? array.indexof(DnSideTimesRepeatedArray,L1_MaxOfRepeats) : -1
    L1_ValueRepeatedMax     = L1_IndexOfMaxRepeat != -1 ? array.get(DnSideListOfOutputValue,L1_IndexOfMaxRepeat) : 0
    
    [ValueTestedMaxNoOfTimes,H1_ValueRepeatedMax,L1_ValueRepeatedMax]
//#endregion

//#region G_Pivots
// @function to retrieve the Pivot High and Low points (Highest Point and the latest PH and similarly Lowest Point and the latest PL)
// @param int StartScanFrom default is 150
// @param float Source - default is close, will be used if the source is an external indicator
// @param string PlotOnType - default is "HL" (possible use cases can be either "HL", "OC" or any any other string character so that it uses Source param to calculate the Pivot points)
// @param int LBC - default is 4 Look Back period for Pivot points
// @param int LBF - default is 2 Look forwar period for confirming the Pivot points
// @returns tuples with useful information regarding pivot points
export G_Pivots(int StartScanFrom = 150,float Source = close, simple string PlotOnType = "HL", int LBC = 4, int LBF = 2)=>
    Initial_PH = nz(ta.pivothigh(PlotOnType == 'HL' ? G_CandleInfo("CandleHigh",0,true) : 
         PlotOnType == 'OC' ? math.max(G_CandleInfo("CandleOpen",0,true), G_CandleInfo("CandleClose",0,true)) :
         Source,
         LBC,
         LBF),0.0)
    Initial_PH_Candle = nz(ta.valuewhen(Initial_PH, bar_index[LBF], 0),0)
    Initial_PL = nz(ta.pivotlow(
         PlotOnType == 'HL' ? G_CandleInfo("CandleLow",0,true) : 
         PlotOnType == 'OC' ? math.min(G_CandleInfo("CandleOpen",0,true), G_CandleInfo("CandleClose",0,true)):
         Source,
         LBC,
         LBF),0.0)
    Initial_PL_Candle   = nz(ta.valuewhen(Initial_PL, bar_index[LBF], 0),0)
    float First_PH          = na
    float First_PL          = na
    int FPH_Bar             = na
    int FPL_Bar             = na
    if BarToStartYourCalculation(StartScanFrom)
        First_PH    := Initial_PH
        First_PL    := Initial_PL
        FPH_Bar     := Initial_PH_Candle
        FPL_Bar     := Initial_PL_Candle

    var SeriesOf_PH         = array.new_float(0)
    var CandleInfoOf_PH     = array.new_int(0)
    var SeriesOf_PL         = array.new_float(0)
    var CandleInfoOf_PL     = array.new_int(0)
    var sizeH               = array.size(SeriesOf_PH)
    var sizeL               = array.size(SeriesOf_PL)
    //========================== PIVOT HIGH CALCULATION ===============================
    if First_PH != 0.00
        if sizeH == 0
            array.push(SeriesOf_PH,First_PH)
            array.push(CandleInfoOf_PH,FPH_Bar)
        else
            if FPH_Bar - array.get(CandleInfoOf_PH, sizeH - 1) > 4 and array.get(SeriesOf_PH, sizeH - 1) >= First_PH
                array.push(SeriesOf_PH,First_PH)
                array.push(CandleInfoOf_PH,FPH_Bar)
            else
                array.pop(SeriesOf_PH)
                array.pop(CandleInfoOf_PH)
                array.push(SeriesOf_PH,First_PH)
                array.push(CandleInfoOf_PH,FPH_Bar)

    
    //========================== PIVOT LOW CALCULATION ===============================
    if First_PL != 0.00
        if sizeL == 0
            array.push(SeriesOf_PL,First_PL)
            array.push(CandleInfoOf_PL,FPL_Bar)
        else
            if FPL_Bar - array.get(CandleInfoOf_PL, sizeL - 1) > 4 and array.get(SeriesOf_PL, sizeL - 1) <= First_PL
                array.push(SeriesOf_PL,First_PL)
                array.push(CandleInfoOf_PL,FPL_Bar)
            else
                array.pop(SeriesOf_PL)
                array.pop(CandleInfoOf_PL)
                array.push(SeriesOf_PL,First_PL)
                array.push(CandleInfoOf_PL,FPL_Bar)

    float LastKnownPL           = 0
    int LastKnownPLBar          = 0
    float SecLowestPL           = 0
    int SecLowestPLBar          = 0
    int indexOfSecLowestPL      = 0 
    int indexOfLastKnownPLBar   = 0 
    CopyOfPL = array.copy(SeriesOf_PL)
    if array.size(SeriesOf_PL)> 3
        array.pop(CopyOfPL)
        LastKnownPL := array.pop(CopyOfPL)    
        array.sort(CopyOfPL)
        array.shift(CopyOfPL)
        SecLowestPL := array.min(CopyOfPL)
        indexOfSecLowestPL:= array.indexof(SeriesOf_PL,SecLowestPL)
        indexOfLastKnownPLBar:= array.indexof(SeriesOf_PL,LastKnownPL)
    
    float LastKnownPH           = 0
    int LastKnownPHBar          = 0
    float SecHighestPH          = 0
    int SecHighestPHBar         = 0
    int indexOfSecHighestPH     = 0
    int indexOfLastKnownPHBar   = 0
    CopyOfPH = array.copy(SeriesOf_PH)
    if array.size(SeriesOf_PH)> 3
        array.pop(CopyOfPH)
        LastKnownPH := array.pop(CopyOfPH)
        array.sort(CopyOfPH,order.descending)
        array.shift(CopyOfPH)
        SecHighestPH := array.max(CopyOfPH)
        indexOfSecHighestPH:= array.indexof(SeriesOf_PH,SecHighestPH)
        indexOfLastKnownPHBar:= array.indexof(SeriesOf_PH,LastKnownPH)

    
    float LL_Val                = 0
    int LL_Bar                  = 0
    float HL_Val                = 0
    int HL_Bar                  = 0
    int indexOfMin              = 0
    float HH_Val                = 0
    int HH_Bar                  = 0
    float LH_Val                = 0
    int LH_Bar                  = 0
    int DindexOfMax             = 0
    LL_Val                      := array.min(SeriesOf_PL)
    HH_Val                      := array.max(SeriesOf_PH)
    indexOfMin                  := array.indexof(SeriesOf_PL,array.min(SeriesOf_PL))
    DindexOfMax                 := array.indexof(SeriesOf_PH,array.max(SeriesOf_PH))
    int cntr                    = 0
    for ele in CandleInfoOf_PL
        if cntr == indexOfMin
            LL_Bar:= ele
        else if cntr == indexOfSecLowestPL
            SecLowestPLBar:= ele
        else if cntr == indexOfLastKnownPLBar
            LastKnownPLBar:= ele
        else
            HL_Bar:= ele
        cntr+= 1
    int cntr1 = 0
    for ele in SeriesOf_PL
        HL_Val:= ele
        cntr1+= 1
    int Rcntr = 0
    for ele in CandleInfoOf_PH
        if Rcntr == DindexOfMax
            HH_Bar:= ele
        else if Rcntr == indexOfSecHighestPH
            SecHighestPHBar:= ele
        else if Rcntr == indexOfLastKnownPHBar
            LastKnownPHBar:= ele
        else
            LH_Bar:= ele
        Rcntr+= 1
    int Rcntr1 = 0
    for ele in SeriesOf_PH
        LH_Val:= ele
        cntr1+= 1
    
    [LL_Bar,
     SecLowestPLBar,
     LastKnownPLBar,
     HL_Bar,
     LL_Val,
     SecLowestPL,
     LastKnownPL,
     HL_Val,
     HH_Bar,
     SecHighestPHBar,
     LastKnownPHBar,
     LH_Bar,
     HH_Val,
     SecHighestPH,
     LastKnownPH,
     LH_Val]
//#endregion

//#region DiscordGetAccountInfo
export DiscordGetAccountInfo(bool OnlyJoin = false, string JoinText = "", int MainOption = 1, int SubOption = 0, string AlternativeSource = "", string AlternativeScriptType = "") =>
    DiscText = switch MainOption
        1 => "PNL"
        2 => "MTM"
        3 => "FUNDS"
        4 => "CEXP"
        5 => "NEXP"
        6 => "ABAL"
        7 => "MAR"
        8 => "PAYIN"
        9 => "PAYOUT"
        10 => SubOption == 0 ? "PRICE " : SubOption == 1 ? "PRICE BNF" : "PRICE NIF"
        11 => "EXITBCO ALL"
        12 => "EXIT_ALL"
    if not OnlyJoin
        if ScriptConvertor("Type", AlternativeSource, AlternativeScriptType) == "F"
            Output = MainOption != 10 or (MainOption == 10 and SubOption != 0) ? '{"content":"' + DiscText +'"}' : MainOption == 10 and SubOption == 0 ? '{"content":"' + DiscText + ScriptConvertor("Name", AlternativeSource, AlternativeScriptType) + " " + ScriptConvertor("Exp", AlternativeSource, AlternativeScriptType) + "F" +'"}' : '{"content":"' + DiscText +'"}'
        else
            Output = MainOption != 10 or (MainOption == 10 and SubOption != 0) ? '{"content":"' + DiscText +'"}' : MainOption == 10 and SubOption == 0 ? '{"content":"' + DiscText + ScriptConvertor("Name", AlternativeSource, AlternativeScriptType) + '"}' : '{"content":"' + DiscText +'"}'
    else
        Output = '{"content":"' + JoinText +'"}'
//#endregion

//#region DiscordTradingJSON
// @param TradeType acceptable Inputs are I (for MIS / Intraday), CO (for Cover Order), BO (for Bracket Order)
// @param BuyOrSell acceptable Inputs are B or S
// @param EqOrFutOrOp acceptable Inputs are E (for Equity), F (for Futures), O (for Options)
// @param TradeQty string format (default = 25 Qty)
// @param ScriptName string format
// @param OrderType acceptable Inputs are "" (for Market), "XXX.XX" (143.05 to place Limit Orders at 143.05), Continued Below 2 other acceptable inputs...
// @param OrderType "X.X%" (1.25% to place Limit orders at specified percentage Below Market price for BUY, and Above Market price for SELL)
// @param OrderType "A X.X%" or "B X.X%" or "A 154.0" or "B 125.0" (A 0.75% to place BUY SL-Limit orders at specified percentage Above Market price for BUY, B 1.05% to place SELL SL-Limit orders at specified percentage Below Market price for SELL)
// @param HowDeep acceptable Inputs are "0" (for ATM option), "1" (Positive Numbers for ITM), "-1" (Negative Numbers for OTM) or "" (for Non Expiry Orders)
// @param CEorPE acceptable Inputs are CE, PE or "" (for Non Option Orders)
// @param Expiry acceptable Inputs are C (for Current Expiry), N (for Next Expiry) or "" (for Non Expiry Orders)
// @param BO_Tgt acceptable Inputs are "X" (5 for Rs.5 Target Point), "X.X%" (1.2% for capturing specified percentage rise in price as Target Points for BUY) or "" (for Non BO Orders)
// @param BO_SL acceptable Inputs are "X" (3 for Rs.3 SL Point), "X.X%" (0.75% for specified percentage fall in price as SL Points for BUY) or "" (for Non BO Orders)
// @param CO_SL acceptable Inputs are "+X" (+3 for Rs.3 above current price for Sell Trade), "-X" (-5 for Rs.5 below current price for Buy Trade), "XXX.X" (125.5 will be applied as SL Value for CO Order) or "" (for Non Expiry Orders)
export DiscordTradingJSON(string TradeType = "I", string BuyOrSell = "B", string EqOrFutOrOp = "O", string TradeQty = "25", string ScriptName, string OrderType = "",
     string HowDeep = "", string CEorPE = "", string Expiry = "", string BO_Tgt = "", string BO_SL = "", string CO_SL = "")=>
    
    TT = switch TradeType
        "CO" => "CO"
        "BO" => "BO"
        => ""
    BS = switch BuyOrSell
        "B" => "B"
        => "S"
    EFO = switch EqOrFutOrOp
        "O" => "O"
        "F" => "F"
        => "E"
    Ex = switch Expiry
        "N" => "N"
        => ""
        

    IsCO        = CO_SL != "" and TT == "CO" and (EFO == "E" or EFO == "F") and OrderType == ""
    IsBO        = BO_SL != "" and BO_Tgt != "" and TT == "BO" and (EFO == "E" or EFO == "F") and OrderType == ""
    IsOptions   = EFO == "O" and HowDeep != "" and TT == "" and CEorPE != "" and Expiry != "" 

    Construct = if TT == ""
        if EFO == "E" or EFO == "F"
            if OrderType == ""
                BS + EFO + " " + TradeQty + " " + ScriptName
            else
                BS + EFO + " " + TradeQty + " " + ScriptName + " " + OrderType
        else if EFO == "O" and IsOptions and not IsBO and not IsCO
            if OrderType == ""
                BS + EFO + " " + TradeQty + " " + ScriptName + "?" + HowDeep + CEorPE + "?" + Ex
            else
                BS + EFO + " " + TradeQty + " " + ScriptName + "?" + HowDeep + CEorPE + "?" + Ex + " " + OrderType
        else
            "Something Wrong"
    else
        if IsCO
            TT + BS + EFO + " " + TradeQty + " " + ScriptName + " " + CO_SL
        else if IsBO
            TT + BS + EFO + " " + TradeQty + " " + ScriptName + " " + BO_Tgt + " " + BO_SL
        else
            "Something Wrong"
//#endregion

//#region TriggerAlertAfter
// @param _cond acceptable Inputs are seconds to wait and then trigger the output as NA
// @param _resetCond acceptable Inputs are true or false
export TriggerAlertAfter(int cond = 15, bool _resetCond = barstate.isnew) =>
    // bool _cond     : condition to test.
    // bool _resetCond: when `true`, the duration resets.
    _cond = timenow <= (time + (cond * 1000))
    varip float _timeBegin = na
    varip bool  _lastCond  = false
    if _resetCond
        // Reset time if required.
        _timeBegin := _cond ? timenow : na
    else if _cond
        if not _lastCond
            // First occurrence of true `_cond`; save beginnning time.
            _timeBegin := timenow
    else
        // Condition is not true; reset beginning time.
        _timeBegin := na
    // Remember the last state of the `_cond` so we can detect transitions.
    _lastCond := _cond
    // Return seconds since beginning of condition, or `na`.
    float _return = (timenow - _timeBegin) / 1000
//#endregion

//#region DiscordStrategyAdd
// @param bool Deploy acceptable Inputs are true for Deploy based Strategy Alert or false for Adding Strategy Alert type
// @param string StrategyShortName specify the shortname for either deploying based or Adding based
// @param string StrategyFullName specify the fullname for strategy Addition
// @param ArrayString LegValues specify the array containing list of Leg Values else use array.new_string(0)
export DiscordStrategyAdd(bool Deploy = true, string StrategyShortName = "", string OnScript = "", int TradingQty = 0, string[] LegValues, string StrategyFullName = "") =>
    Output              = ""
    if Deploy and StrategyShortName != "" and TradingQty == 0
        Output          := '{"content":"STRGY DEP ' + str.upper(StrategyShortName) +'"}'
    else if Deploy and StrategyShortName != "" and TradingQty > 0 and OnScript != ""
        Output          := '{"content":"STRGY DEP ' + str.upper(StrategyShortName) + ' ' + str.upper(OnScript) + ' ' + str.tostring(TradingQty) +'"}'
    else 
        if not Deploy and array.size(LegValues) > 0 and StrategyShortName != "" and StrategyFullName != ""
            InitialText     = '{"content":"STRGY ADD '
            NameText        = '{"Strategy Name": "' + StrategyFullName + '", '
            ShortNameText   = '"Short Name": "' + StrategyShortName + '", "LegValues": ['
            EndText         = ']}"'
            LegJoinText     = ""
            for [cntr, Lv] in LegValues
                if cntr     != array.size(LegValues) - 1
                    LegJoinText := LegJoinText + '{"LEG": "' + str.upper(Lv) + '"}, '
                else
                    LegJoinText := LegJoinText + '{"LEG": "' + str.upper(Lv) + '"}'
            Output          := InitialText + str.upper(NameText) + str.upper(ShortNameText) + str.upper(LegJoinText) + EndText
//#endregion

//#endregion
