UFO + Realtime Divergences (UO x MFI)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tvenn

//@version=5
indicator("UFO + Realtime Divergences", shorttitle="UFO+", overlay=false, max_bars_back = 1000, max_lines_count = 400, max_labels_count = 400, precision=3)

pulldatafromtimeframe = input.string("Chart", title="Select alternate timeframe in mins", options=["Chart", "1", "2", "3", "4", "5", "10", "15", "30", "45", "60", "120", "240"])
customOscColor      = input.color(color.new(color.blue, 0), title="Oscillator color")

green   = color.new(#95BD5F, 30)
red     = color.new(#EA1889, 30)

//UO
grp_UOS = "UO Settings"
length1 = input.int(7, minval=1, title = "Fast Length", group=grp_UOS)
length2 = input.int(14, minval=1, title = "Middle Length", group=grp_UOS)
length3 = input.int(28, minval=1, title = "Slow Length", group=grp_UOS)

//MFI
grp_MFIS    = "MFI Settings"
mfilength1 = input(10, title="MFI Length", group=grp_MFIS)
mfisrc = input(hlc3, title="MFI Source", group=grp_MFIS)
mfi = ta.mfi(mfisrc, mfilength1)
//MFI end

//Divergence Settings
grp_DivS    = "Divergence Settings"
showlines   = input(defval = true, title = "Show Divergence Lines", group=grp_DivS)
showlast    = input(defval = true, title = "Show Only Last Divergence", group=grp_DivS)
dontconfirm = input(defval = true, title = "Don't Wait for Confirmation", group=grp_DivS)

//Detail
grp_Detail          = "Details"
centerline          = input(true, title="Centerline", group=grp_Detail, inline="0")
showBands           = input(false, title="Range bands", group=grp_Detail, inline="1")
obosHighlightEnabled= input(false, title="Highlight overbought & oversold", group=grp_Detail, inline="2")
fadeOutOsc          = input(false, title="Fade out oscillator", group=grp_Detail, tooltip="Face out the oscillator leaving only the most recent periods prominent for a clearer chart.")
flipOsc             = input(false, title="Flip Oscillator", group=grp_Detail, tooltip="This will flip the oscillator upside down. The purpose is for use with the flip chart feature of Tradingview (Alt+i), which does not also flip the oscillator. This may help those with a particular long/short bias to see the other side of things. Divergence lines will not be drawn.")
showMTFRibbon       = input(true, title="Enable MTF ribbon", group=grp_Detail)
showLabel           = input(true, title="Show MTF OB/OS label", group=grp_Detail, tooltip="This will show a text label in the bottom left of the panel to notify when the MTF CCI or MTF Stoch RSI is either overbought or oversold, for clarity, or colour blind users.")
showMTFVals         = input(false, title="Show MTF table for", group=grp_Detail, inline="table")
MTFTableType        = input.string("MTF Stoch RSI", options=["MTF Stoch RSI", "MTF CCI", "Both"], title="", group=grp_Detail, inline="table")
MTFTableTheme       = input.string("Dark", options=["Dark", "Light"], title="", group=grp_Detail, inline="table")
// MTFOBOSDivSignal    = input(true, title="Show MTF OB/OS + Div", group=grp_Detail, tooltip="Places label to indicate where MTF Stoch RSI / MTF CCI oversold AND recent bullish divergence, and where MTF Stoch RSI / MTF CCI overbought AND recent bearish divergence.\n\nYou can alert these using the alert called 'MTF OB/OS + Div on RF+'")


grp_BG       = "Background styles"
backgroundColor     = input(color.new(color.white, 97), title = "", group=grp_BG, inline='clc')
showBackground      = input(false, title="Background", group=grp_BG, inline='clc')
centerlineXBgColor  =  input(color.new(red, 90), title='', group=grp_BG, inline='clc1')
backgroundSignalOn  = input(false, title="Centerline crossunder background color", group=grp_BG, tooltip="This will colour the background according to whether the RSI is above or below the 50 level.", inline='clc1')
centerlineXOscColor = input(color.new(red, 10), title='', group=grp_BG, inline='clc2')
oscSignalOn         = input(false, title="Centerline crossunder oscillator color", group=grp_BG, tooltip="This will colour the oscillator according to whether the RSI is above or below the 50 level.", inline='clc2')



//UO
average(bp, tr_, length) => math.sum(bp, length) / math.sum(tr_, length)
high_ = math.max(high, close[1])
low_ = math.min(low, close[1])

bp = close - low_
tr_ = high_ - low_
avg7 = average(bp, tr_, length1)
avg14 = average(bp, tr_, length2)
avg28 = average(bp, tr_, length3)
uo_val = 100 * (4*avg7 + 2*avg14 + avg28)/7
//UO end

ufo_val = (uo_val + mfi) / 2
osc = (flipOsc ? 100-ufo_val : ufo_val)

ultimate_value = request.security(syminfo.tickerid, pulldatafromtimeframe == "Chart" ? "" : pulldatafromtimeframe, osc, barmerge.gaps_on)
oscColor = ((obosHighlightEnabled and ultimate_value > 75) ? red : ((obosHighlightEnabled and ultimate_value < 25) ? red : (oscSignalOn and ultimate_value > 50 ? customOscColor : (oscSignalOn and ultimate_value < 50 ? centerlineXOscColor : customOscColor))))
distanceTransparency = (bar_index > (last_bar_index - 30) ? 10 : (bar_index > last_bar_index - 60 ? 20 : (bar_index > last_bar_index - 80 ? 30 : (bar_index > last_bar_index - 100 ? 40 : (bar_index > last_bar_index - 120 ? 50 : (bar_index > last_bar_index - 140 ? 60 : (bar_index > last_bar_index - 160 ? 70 : 80)))))))

plot(ultimate_value, color=(fadeOutOsc ? color.new(oscColor, distanceTransparency) : oscColor), linewidth=2, title="Ultimate Oscillator")



//MTF confluences
//===============================================================================================================================

grp_SRSI    = "MTF Stoch RSI for ribbon"
srsiribbonPos= input.string(title="MTF Stoch RSI ribbon position", defval='Absolute', options=['Top', 'Bottom', 'Absolute'], group=grp_SRSI)
smoothK     = input.int(3, "K line", minval=1, group=grp_SRSI)
smoothD     = input.int(3, "D line", minval=1, group=grp_SRSI)
lengthRSI   = input.int(14, "RSI Length", minval=1, group=grp_SRSI)
lengthStoch = input.int(14, "Stochastic Length", minval=1, group=grp_SRSI)
src         = input(close, title="RSI Source", group=grp_SRSI)
rsi1        = ta.rsi(src, lengthRSI)
k           = ta.sma(ta.stoch(rsi1, rsi1, rsi1, lengthStoch), smoothK)
d           = ta.sma(k, smoothD)

k1 = request.security(syminfo.tickerid, pulldatafromtimeframe == "Chart" ? "" : pulldatafromtimeframe, k, barmerge.gaps_off)
d1 = request.security(syminfo.tickerid, pulldatafromtimeframe == "Chart" ? "" : pulldatafromtimeframe, d, barmerge.gaps_off)

//Bands
bandColor = showBands ? color.new(#777777, 30) : color.new(#777777, 100)

band1       = hline(75, color=bandColor, linestyle=hline.style_dotted, title="Band high", linewidth=1)
band2       = hline(50, color=(centerline ? color.new(#777777, 0) : na), linestyle=hline.style_dotted, title="Centerline", linewidth=1)
band0       = hline(25, color=bandColor, linestyle=hline.style_dotted, title="Band low", linewidth=1)


//Primary timeframes confluences
grp_MTFSRSI1                = "MTF Stoch RSI for ribbon"
overboughtColor_1           = input.color(color.new(red, 0), title="MTF Stoch RSI OB color", group=grp_MTFSRSI1)
oversoldColor_1             = input.color(color.new(green, 0), title="MTF Stoch RSI OS color", group=grp_MTFSRSI1)
useBgColorMTFStochOBOS_1    = input(true, title="Color ribbon where MTF Stoch RSI OB/OS", group=grp_MTFSRSI1, tooltip="This will signal the overbought / oversold state of the Stochastic RSI on all 3x selected timeframes together at the same time.")
useBarColorMTFStochOBOS_1   = input(false, title="Color candles where MTF Stoch RSI OB/OS", group=grp_MTFSRSI1, tooltip="This will signal the overbought / oversold state of the Stochastic RSI on all 3x selected timeframes together at the same time.")

grp_CTFSRSI1          = "Show Current Timeframe OB/OS in Ribbon"
includeCurrentTFOBOS  = input(false, title="Show selected timeframe OB/OS in ribbon", group=grp_CTFSRSI1, tooltip="this will include in the ribbon indications where the Stoch RSI is overbought or oversold on the current timeframe")
CTF_1                 = input.string("Chart", title="CTF", options=["Chart"], group=grp_CTFSRSI1, inline="tf1")
CTF_OSTHRESH_1        = input(20, title="", group=grp_CTFSRSI1, inline="tf1")
CTF_OBTHRESH_1        = input(80, title="", group=grp_CTFSRSI1, inline="tf1")


grp_MTFSRSI1Conf    = "MTF Stoch RSI confluences [Timeframe] [OS level] [OB level]"
TF_1_1 = input.string("1", title="TF1", options=["Chart", "1", "2", "3", "4", "5", "8", "10", "15", "20", "30", "45", "60", "120", "240"], group=grp_MTFSRSI1Conf, inline="mtf1")
TF_2_1 = input.string("5", title="TF2", options=["Chart", "1", "2", "3", "4", "5", "8", "10", "15", "20", "30", "45", "60", "120", "240"], group=grp_MTFSRSI1Conf, inline="mtf2")
TF_3_1 = input.string("15", title="TF3", options=["Chart", "1", "2", "3", "4", "5", "8", "10", "15", "20", "30", "45", "60", "120", "240"], group=grp_MTFSRSI1Conf, inline="mtf3")

MTF_OSTHRESH_1_1 = input(20, title="", group=grp_MTFSRSI1Conf, inline="mtf1")
MTF_OSTHRESH_2_1 = input(20, title="", group=grp_MTFSRSI1Conf, inline="mtf2")
MTF_OSTHRESH_3_1 = input(20, title="", group=grp_MTFSRSI1Conf, inline="mtf3")

MTF_OBTHRESH_1_1 = input(80, title="", group=grp_MTFSRSI1Conf, inline="mtf1")
MTF_OBTHRESH_2_1 = input(80, title="", group=grp_MTFSRSI1Conf, inline="mtf2")
MTF_OBTHRESH_3_1 = input(80, title="", group=grp_MTFSRSI1Conf, inline="mtf3")

MTF1_1 = request.security(syminfo.tickerid, TF_1_1 == "Chart" ? "" : TF_1_1, k, barmerge.gaps_off)
MTF2_1 = request.security(syminfo.tickerid, TF_2_1 == "Chart" ? "" : TF_2_1, k, barmerge.gaps_off)
MTF3_1 = request.security(syminfo.tickerid, TF_3_1 == "Chart" ? "" : TF_3_1, k, barmerge.gaps_off)

allMTFStochOB_1 = (MTF1_1 > MTF_OBTHRESH_1_1 and MTF2_1 > MTF_OBTHRESH_2_1 and MTF3_1 > MTF_OBTHRESH_3_1)
allMTFStochOS_1 = (MTF1_1 < MTF_OSTHRESH_1_1 and MTF2_1 < MTF_OSTHRESH_2_1 and MTF3_1 < MTF_OSTHRESH_3_1)

allOBColor_1 = (allMTFStochOB_1 ? overboughtColor_1 : na)
allOSColor_1 = (allMTFStochOS_1 ? oversoldColor_1 : na)



isCTFOB_1 = (k > CTF_OBTHRESH_1 ? true : false)
isCTFOS_1 = (k < CTF_OSTHRESH_1 ? true : false)

ctfOBColor_1 = (k > CTF_OBTHRESH_1+10 ? color.new(overboughtColor_1, 60) : k > CTF_OBTHRESH_1+10 ? color.new(overboughtColor_1, 80) : na)
ctfOSColor_1 = (k < CTF_OSTHRESH_1-10 ? color.new(oversoldColor_1, 60) : k < CTF_OSTHRESH_1 ? color.new(oversoldColor_1, 80) : na)

//Secondary timeframes confluences
grp_CCI = "MTF CCI settings for ribbon"
cciribbonPos= input.string(title="MTF CCI ribbon position", defval='Top', options=['Top', 'Bottom', 'Absolute'], group=grp_CCI)
ccilength  = input.int(20, minval=1, title="CCI source", group=grp_CCI)
ccisrc  = input(hlc3, title="CCI source", group=grp_CCI)
ma      = ta.sma(src, ccilength)
cci_value = (src - ma) / (0.015 * ta.dev(src, ccilength))


grp_MTFCCI2             = "MTF CCI for ribbon"
overboughtColor_2       = input.color(color.new(color.red, 50), title="MTF CCI OB color", group=grp_MTFCCI2)
oversoldColor_2         = input.color(color.new(color.green, 50), title="MTF CCI OS color", group=grp_MTFCCI2)
useBgColorMTFCCIOBOS_2  = input(true, title="Color ribbon where MTF CCI OB/OS", group=grp_MTFCCI2, tooltip="This will signal the overbought / oversold state of the CCI on all 3x selected timeframes together at the same time.")
useBarColorMTFCCIOBOS_2 = input(false, title="Color candles where MTF CCI OB/OS", group=grp_MTFCCI2, tooltip="This will signal the overbought / oversold state of the CCI on all 3x selected timeframes together at the same time.")


grp_MTFCCI2Conf         = "MTF CCI confluences 2 [Timeframe] [OS level] [OB level]"
TF_1_2                 = input.string("1", title="TF1", options=["Chart", "1", "2", "3", "4", "5", "8", "10", "15", "20", "30", "45", "60", "120", "240"], group=grp_MTFCCI2Conf, inline="mtf1")
TF_2_2                 = input.string("5", title="TF2", options=["Chart", "1", "2", "3", "4", "5", "8", "10", "15", "20", "30", "45", "60", "120", "240"], group=grp_MTFCCI2Conf, inline="mtf2")
TF_3_2                 = input.string("15", title="TF3", options=["Chart", "1", "2", "3", "4", "5", "8", "10", "15", "20", "30", "45", "60", "120", "240"], group=grp_MTFCCI2Conf, inline="mtf3")

MTF_OSTHRESH_1_2        = input(-150, title="", group=grp_MTFCCI2Conf, inline="mtf1")
MTF_OSTHRESH_2_2        = input(-150, title="", group=grp_MTFCCI2Conf, inline="mtf2")
MTF_OSTHRESH_3_2        = input(-150, title="", group=grp_MTFCCI2Conf, inline="mtf3")

MTF_OBTHRESH_1_2        = input(150, title="", group=grp_MTFCCI2Conf, inline="mtf1")
MTF_OBTHRESH_2_2        = input(150, title="", group=grp_MTFCCI2Conf, inline="mtf2")
MTF_OBTHRESH_3_2        = input(150, title="", group=grp_MTFCCI2Conf, inline="mtf3")

MTF1_2 = request.security(syminfo.tickerid, TF_1_2 == "Chart" ? "" : TF_1_2, cci_value, barmerge.gaps_off)
MTF2_2 = request.security(syminfo.tickerid, TF_2_2 == "Chart" ? "" : TF_2_2, cci_value, barmerge.gaps_off)
MTF3_2 = request.security(syminfo.tickerid, TF_3_2 == "Chart" ? "" : TF_3_2, cci_value, barmerge.gaps_off)

allMTFCCIOB_2 = (MTF1_2 > MTF_OBTHRESH_1_2 and MTF2_2 > MTF_OBTHRESH_2_2 and MTF3_2 > MTF_OBTHRESH_3_2)
allMTFCCIOS_2 = (MTF1_2 < MTF_OSTHRESH_1_2 and MTF2_2 < MTF_OSTHRESH_2_2 and MTF3_2 < MTF_OSTHRESH_3_2)

allOBColor_2 = (allMTFCCIOB_2 ? overboughtColor_2 : na)
allOSColor_2 = (allMTFCCIOS_2 ? oversoldColor_2 : na)

_srsiribbonLocation = switch srsiribbonPos
    "Absolute" => location.absolute
    "Top" => location.top
    "Bottom" => location.bottom

_cciribbonLocation = switch cciribbonPos
    "Absolute" => location.absolute
    "Top" => location.top
    "Bottom" => location.bottom

plotshape(showMTFRibbon and useBgColorMTFStochOBOS_1 and allMTFStochOB_1, title="OB/OS", location=_srsiribbonLocation, color=allOBColor_1, style=shape.circle, size=size.auto)
plotshape(showMTFRibbon and useBgColorMTFStochOBOS_1 and allMTFStochOS_1, title="OB/OS", location=_srsiribbonLocation, color=allOSColor_1, style=shape.circle, size=size.auto)
plotshape(showMTFRibbon and useBgColorMTFCCIOBOS_2 and allMTFCCIOB_2, title="OB/OS", location=_cciribbonLocation, color=allOBColor_2, style=shape.circle, size=size.auto)
plotshape(showMTFRibbon and useBgColorMTFCCIOBOS_2 and allMTFCCIOS_2, title="OB/OS", location=_cciribbonLocation, color=allOSColor_2, style=shape.circle, size=size.auto)

barcolor(useBarColorMTFStochOBOS_1 and allMTFStochOB_1 ? allOBColor_1 : (useBarColorMTFStochOBOS_1 and allMTFStochOS_1 ? allOSColor_1 : na))
barcolor(useBarColorMTFCCIOBOS_2 and allMTFCCIOB_2 ? allOBColor_2 : (useBarColorMTFCCIOBOS_2 and allMTFCCIOS_2 ? allOSColor_2 : na))

plotshape(showMTFRibbon and includeCurrentTFOBOS and isCTFOB_1, title="Current TF SRSI overbought", color=(showMTFRibbon and includeCurrentTFOBOS ? ctfOBColor_1 : na), location=_srsiribbonLocation, style=shape.circle, size=size.auto)
plotshape(showMTFRibbon and includeCurrentTFOBOS and isCTFOS_1, title="Current TF SRSI oversold", color=(showMTFRibbon and includeCurrentTFOBOS ? ctfOSColor_1 : na), location=_srsiribbonLocation, style=shape.circle, size=size.auto)

//Pivot settings
grp_PPS = "Pivot Point Settings"
pp      = input.int(defval = 12, title = "Pivot period", minval = 1, maxval = 50, group=grp_PPS)
maxpp   = input.int(defval = 5, title = "Maximum Pivot periods to check for divs", minval = 1, maxval = 100, group=grp_PPS)
maxbars = input.int(defval = 100, title = "Maximum Bars to Check", minval = 1, maxval = 300, group=grp_PPS)
source  = "Close"

searchdiv = input.string(defval = "Regular/Hidden", title = "Divergence Type", options = ["Regular", "Hidden", "Regular/Hidden"], group=grp_DivS)


prd = pp


//Styles
grp_STY         = "Styles"
MTFTableColor   = input.color(color.new(color.black, 100), title="Table Color", group=grp_STY)
pos_reg_div_col = input(defval = green, title = "Positive Regular Divergence", group=grp_STY)
neg_reg_div_col = input(defval = red, title = "Negative Regular Divergence", group=grp_STY)
pos_hid_div_col = input(defval = green, title = "Positive Hidden Divergence", group=grp_STY)
neg_hid_div_col = input(defval = red, title = "Negative Hidden Divergence", group=grp_STY)
reg_div_l_style_= input.string(defval = "Solid", title = "Regular Divergence Line Style", options = ["Solid", "Dashed", "Dotted"], group=grp_STY)
hid_div_l_style_= input.string(defval = "Dotted", title = "Hdden Divergence Line Style", options = ["Solid", "Dashed", "Dotted"], group=grp_STY)
reg_div_l_width = input.int(defval = 2, title = "Regular Divergence Line Width", minval = 1, maxval = 2, group=grp_STY)
hid_div_l_width = input.int(defval = 2, title = "Hidden Divergence Line Width", minval = 1, maxval = 2, group=grp_STY)


//background fill options
fill(band0, band1, backgroundSignalOn and ultimate_value > 50 ? na : backgroundSignalOn and ultimate_value < 50 ? centerlineXBgColor : na, title="Centerline crossover background color", editable=1)
fill(band0, band1, showBackground ? backgroundColor :  na, title="Centerline crossover background color", editable=1)


// set line styles
var reg_div_l_style = reg_div_l_style_ == "Solid" ? line.style_solid : 
                       reg_div_l_style_ == "Dashed" ? line.style_dashed :
                       line.style_dotted
var hid_div_l_style = hid_div_l_style_ == "Solid" ? line.style_solid : 
                       hid_div_l_style_ == "Dashed" ? line.style_dashed :
                       line.style_dotted

// get indicators
uo = ultimate_value

// keep indicator colors in arrays
var indicators_name = array.new_string(11)
var div_colors = array.new_color(4)
if barstate.isfirst
    //colors
    array.set(div_colors, 0, pos_reg_div_col)
    array.set(div_colors, 1, neg_reg_div_col)
    array.set(div_colors, 2, pos_hid_div_col)
    array.set(div_colors, 3, neg_hid_div_col)

// Check if we get new Pivot High Or Pivot Low
float ph = ta.pivothigh(close, prd, prd)
float pl = ta.pivotlow(close, prd, prd)

// keep values and positions of Pivot Highs/Lows in the arrays
var int maxarraysize = 20
var ph_positions = array.new_int(maxarraysize, 0)
var pl_positions = array.new_int(maxarraysize, 0)
var ph_vals = array.new_float(maxarraysize, 0.)
var pl_vals = array.new_float(maxarraysize, 0.)

// add PHs to the array
if ph
    array.unshift(ph_positions, bar_index)
    array.unshift(ph_vals, ph)
    if array.size(ph_positions) > maxarraysize
        array.pop(ph_positions)
        array.pop(ph_vals)

// add PLs to the array
if pl
    array.unshift(pl_positions, bar_index)
    array.unshift(pl_vals, pl)
    if array.size(pl_positions) > maxarraysize
        array.pop(pl_positions)
        array.pop(pl_vals)

// functions to check Regular Divergences and Hidden Divergences

// function to check positive regular or negative hidden divergence
// cond == 1 => positive_regular, cond == 2=> negative_hidden
positive_regular_positive_hidden_divergence(src, cond)=>
    divlen = 0
    prsc = close
    // if indicators higher than last value and close price is higher than las close 
    if dontconfirm or src > src[1] or close > close[1]
        startpoint = dontconfirm ? 0 : 1 // don't check last candle
        // we search last 15 PPs
        for x = 0 to maxpp - 1
            len = bar_index - array.get(pl_positions, x) + prd
            // if we reach non valued array element or arrived 101. or previous bars then we don't search more
            if array.get(pl_positions, x) == 0 or len > maxbars
                break
            if len > 5 and 
               ((cond == 1 and src[startpoint] > src[len] and prsc[startpoint] < nz(array.get(pl_vals, x))) or
               (cond == 2 and src[startpoint] < src[len] and prsc[startpoint] > nz(array.get(pl_vals, x))))
                slope1 = (src[startpoint] - src[len]) / (len - startpoint)
                virtual_line1 = src[startpoint] - slope1
                slope2 = (close[startpoint] - close[len]) / (len - startpoint)
                virtual_line2 = close[startpoint] - slope2
                arrived = true
                for y = 1 + startpoint to len - 1
                    if src[y] < virtual_line1 or nz(close[y]) < virtual_line2
                        arrived := false
                        break
                    virtual_line1 := virtual_line1 - slope1
                    virtual_line2 := virtual_line2 - slope2
                
                if arrived
                    divlen := len
                    break
    divlen

// function to check negative regular or positive hidden divergence
// cond == 1 => negative_regular, cond == 2=> positive_hidden
negative_regular_negative_hidden_divergence(src, cond)=>
    divlen = 0
    prsc = close
    // if indicators higher than last value and close price is higher than las close 
    if dontconfirm or src < src[1] or close < close[1]
        startpoint = dontconfirm ? 0 : 1 // don't check last candle
        // we search last 15 PPs
        for x = 0 to maxpp - 1
            len = bar_index - array.get(ph_positions, x) + prd
            // if we reach non valued array element or arrived 101. or previous bars then we don't search more
            if array.get(ph_positions, x) == 0 or len > maxbars
                break
            if len > 5 and 
               ((cond == 1 and src[startpoint] < src[len] and prsc[startpoint] > nz(array.get(ph_vals, x))) or 
               (cond == 2 and src[startpoint] > src[len] and prsc[startpoint] < nz(array.get(ph_vals, x))))
                slope1 = (src[startpoint] - src[len]) / (len - startpoint)
                virtual_line1 = src[startpoint] - slope1
                slope2 = (close[startpoint] - nz(close[len])) / (len - startpoint)
                virtual_line2 = close[startpoint] - slope2
                arrived = true
                for y = 1 + startpoint to len - 1
                    if src[y] > virtual_line1 or nz(close[y]) > virtual_line2
                        arrived := false
                        break
                    virtual_line1 := virtual_line1 - slope1
                    virtual_line2 := virtual_line2 - slope2
                
                if arrived
                    divlen := len
                    break
    divlen

// calculate 4 types of divergence if enabled in the options and return divergences in an array
calculate_divs(cond, indicator)=>
    divs = array.new_int(4, 0)
    array.set(divs, 0, cond and (searchdiv == "Regular" or searchdiv == "Regular/Hidden") ? positive_regular_positive_hidden_divergence(indicator, 1) : 0)
    array.set(divs, 1, cond and (searchdiv == "Regular" or searchdiv == "Regular/Hidden") ? negative_regular_negative_hidden_divergence(indicator, 1) : 0)
    array.set(divs, 2, cond and (searchdiv == "Hidden" or searchdiv == "Regular/Hidden")  ? positive_regular_positive_hidden_divergence(indicator, 2) : 0)
    array.set(divs, 3, cond and (searchdiv == "Hidden" or searchdiv == "Regular/Hidden")  ? negative_regular_negative_hidden_divergence(indicator, 2) : 0)
    divs

// array to keep all divergences
var all_divergences = array.new_int(4) // 1 indicator * 4 divergence = 4 elements
// set related array elements
array_set_divs(div_pointer, index)=>
    for x = 0 to 3
        array.set(all_divergences, index * 4 + x, array.get(div_pointer, x))

// set divergences array 
array_set_divs(calculate_divs(true, uo), 0)

// keep line in an array
var pos_div_lines = array.new_line(0)
var neg_div_lines = array.new_line(0)
var pos_div_labels = array.new_label(0)
var neg_div_labels = array.new_label(0) 

// remove old lines and labels if showlast option is enabled
delete_old_pos_div_lines()=>
    if array.size(pos_div_lines) > 0    
        for j = 0 to array.size(pos_div_lines) - 1 
            line.delete(array.get(pos_div_lines, j))
        array.clear(pos_div_lines)

delete_old_neg_div_lines()=>
    if array.size(neg_div_lines) > 0    
        for j = 0 to array.size(neg_div_lines) - 1 
            line.delete(array.get(neg_div_lines, j))
        array.clear(neg_div_lines)

delete_old_pos_div_labels()=>
    if array.size(pos_div_labels) > 0 
        for j = 0 to array.size(pos_div_labels) - 1 
            label.delete(array.get(pos_div_labels, j))
        array.clear(pos_div_labels)

delete_old_neg_div_labels()=>
    if array.size(neg_div_labels) > 0    
        for j = 0 to array.size(neg_div_labels) - 1 
            label.delete(array.get(neg_div_labels, j))
        array.clear(neg_div_labels)

// delete last creted lines and labels until we met new PH/PV 
delete_last_pos_div_lines_label(n)=>
    if n > 0 and array.size(pos_div_lines) >= n    
        asz = array.size(pos_div_lines)
        for j = 1 to n
            line.delete(array.get(pos_div_lines, asz - j))
            array.pop(pos_div_lines)
        if array.size(pos_div_labels) > 0  
            label.delete(array.get(pos_div_labels, array.size(pos_div_labels) - 1))
            array.pop(pos_div_labels)

delete_last_neg_div_lines_label(n)=>
    if n > 0 and array.size(neg_div_lines) >= n    
        asz = array.size(neg_div_lines)
        for j = 1 to n
            line.delete(array.get(neg_div_lines, asz - j))
            array.pop(neg_div_lines)
        if array.size(neg_div_labels) > 0  
            label.delete(array.get(neg_div_labels, array.size(neg_div_labels) - 1))
            array.pop(neg_div_labels)
            
// variables for Alerts
pos_reg_div_detected = false
neg_reg_div_detected = false
pos_hid_div_detected = false
neg_hid_div_detected = false

// to remove lines/labels until we met new // PH/PL
var last_pos_div_lines = 0
var last_neg_div_lines = 0
var remove_last_pos_divs = false 
var remove_last_neg_divs = false
if pl
    remove_last_pos_divs := false
    last_pos_div_lines := 0
if ph
    remove_last_neg_divs := false
    last_neg_div_lines := 0

// draw divergences lines and labels
divergence_text_top = ""
divergence_text_bottom = ""
distances = array.new_int(0)
dnumdiv_top = 0
dnumdiv_bottom = 0
top_label_col = color.white
bottom_label_col = color.white
old_pos_divs_can_be_removed = true
old_neg_divs_can_be_removed = true
startpoint = dontconfirm ? 0 : 1 // used for don't confirm option

for x = 0 to 0
    div_type = -1
    for y = 0 to 3
        if array.get(all_divergences, x * 4 + y) > 0 // any divergence?
            div_type := y
            if (y % 2) == 1 
                dnumdiv_top := dnumdiv_top + 1
                top_label_col := array.get(div_colors, y)
            if (y % 2) == 0
                dnumdiv_bottom := dnumdiv_bottom + 1
                bottom_label_col := array.get(div_colors, y)
            if not array.includes(distances, array.get(all_divergences, x * 4 + y))  // line not exist ?
                array.push(distances, array.get(all_divergences, x * 4 + y))
                new_line = (showlines and not flipOsc) ? line.new(x1 = bar_index - array.get(all_divergences, x * 4 + y), 
                          y1 = (source == "Close" ? uo[array.get(all_divergences, x * 4 + y)] : 
                                           (y % 2) == 0 ? low[array.get(all_divergences, x * 4 + y)] : 
                                                          high[array.get(all_divergences, x * 4 + y)]),
                          x2 = bar_index - startpoint,
                          y2 = (source == "Close" ? uo[startpoint] : 
                                           (y % 2) == 0 ? low[startpoint] : 
                                                          high[startpoint]),
                          color = array.get(div_colors, y),
                          style = y < 2 ? reg_div_l_style : hid_div_l_style,
                          width = y < 2 ? reg_div_l_width : hid_div_l_width
                          )
                          : na
                if (y % 2) == 0
                    if old_pos_divs_can_be_removed
                        old_pos_divs_can_be_removed := false
                        if not showlast and remove_last_pos_divs
                            delete_last_pos_div_lines_label(last_pos_div_lines)
                            last_pos_div_lines := 0
                        if showlast
                            delete_old_pos_div_lines()
                    array.push(pos_div_lines, new_line)
                    last_pos_div_lines := last_pos_div_lines + 1
                    remove_last_pos_divs := true
                    
                if (y % 2) == 1
                    if old_neg_divs_can_be_removed
                        old_neg_divs_can_be_removed := false
                        if not showlast and remove_last_neg_divs
                            delete_last_neg_div_lines_label(last_neg_div_lines)
                            last_neg_div_lines := 0
                        if showlast
                            delete_old_neg_div_lines()
                    array.push(neg_div_lines, new_line)
                    last_neg_div_lines := last_neg_div_lines + 1
                    remove_last_neg_divs := true
                    
            // set variables for alerts
            if y == 0
                pos_reg_div_detected := true
            if y == 1
                neg_reg_div_detected := true
            if y == 2
                pos_hid_div_detected := true
            if y == 3
                neg_hid_div_detected := true


alertcondition(pos_reg_div_detected and not pos_reg_div_detected[1], title='Bullish Regular Divergence in UFO', message='Bullish Regular Divergence in UFO')
alertcondition(pos_hid_div_detected and not pos_hid_div_detected[1], title='Bullish Hidden Divergence in UFO', message='Bullish Hidden Divergence in UFO')
alertcondition(neg_reg_div_detected and not neg_reg_div_detected[1], title='Bearish Regular Divergence in UFO', message='Bearish Regular Divergence in UFO')
alertcondition(neg_hid_div_detected and not neg_hid_div_detected[1], title='Bearish Hidden Divergence in UFO', message='Bearish Hidden Divergence in UFO')

alertcondition((pos_reg_div_detected and not pos_reg_div_detected[1]) or (pos_hid_div_detected and not pos_hid_div_detected[1]), title='Bullish Divergence in UFO', message='Bullish Divergence in UFO')
alertcondition((neg_reg_div_detected and not neg_reg_div_detected[1]) or (neg_hid_div_detected and not neg_hid_div_detected[1]), title='Bearish Divergence in UFO', message='Bearish Divergence in UFO')

alertcondition((neg_reg_div_detected and not neg_reg_div_detected[1]) or (neg_hid_div_detected and not neg_hid_div_detected[1]) or (pos_reg_div_detected and not pos_reg_div_detected[1]) or (pos_hid_div_detected and not pos_hid_div_detected[1]), title='Divergence in UFO', message='Divergence in UFO')

alertcondition((allMTFStochOB_1 and not allMTFStochOB_1[1]) or (allMTFCCIOB_2 and not allMTFCCIOB_2[1]), title='MTF Overbought on UFO', message='MTF Overbought on UFO')
alertcondition((allMTFStochOS_1 and not allMTFStochOS_1[1]) or (allMTFCCIOS_2 and not allMTFCCIOS_2[1]), title='MTF Oversold on UFO', message='MTF Oversold on UFO')

alertcondition((allMTFStochOS_1 and not allMTFStochOS_1[1]) or (allMTFStochOB_1 and not allMTFStochOB_1[1]) or (allMTFCCIOB_2 and not allMTFCCIOB_2[1]) or (allMTFCCIOS_2 and not allMTFCCIOS_2[1]), title='MTF OB/OS on UFO', message='MTF OB/OS on UFO')



//Oscillator name label
var table label = table.new(position.bottom_left, 1, 1, bgcolor = color.new(color.black, 100), frame_width = 0, frame_color = color.new(#000000, 100))
// var table flippedLabel = table.new(position.bottom_left, 1, 1, bgcolor = color.new(color.black, 100), frame_width = 0, frame_color = color.new(#000000, 100))

if barstate.islast and flipOsc
    // We only populate the table on the last bar.
    table.cell(label, 0, 0, text="Flipped", text_color=color.gray, text_halign=text.align_left, text_size=size.small, bgcolor=color.new(#000000, 100))
if barstate.islast and (not flipOsc) and allMTFStochOB_1 and showLabel
    table.cell(label, 0, 0, text="MTF Stoch RSI Overbought", text_color=overboughtColor_1, text_halign=text.align_left, text_size=size.small, bgcolor=color.new(#000000, 100))
if barstate.islast and (not flipOsc) and allMTFStochOS_1 and showLabel
    table.cell(label, 0, 0, text="MTF Stoch RSI Oversold", text_color=oversoldColor_1, text_halign=text.align_left, text_size=size.small, bgcolor=color.new(#000000, 100))
if barstate.islast and (not flipOsc) and allMTFCCIOB_2 and showLabel
    table.cell(label, 0, 0, text="MTF CCI Overbought", text_color=overboughtColor_2, text_halign=text.align_left, text_size=size.small, bgcolor=color.new(#000000, 100))
if barstate.islast and (not flipOsc) and allMTFCCIOS_2 and showLabel
    table.cell(label, 0, 0, text="MTF CCI Oversold", text_color=oversoldColor_2, text_halign=text.align_left, text_size=size.small, bgcolor=color.new(#000000, 100))

if barstate.islast and (not flipOsc and not allMTFStochOB_1 and not allMTFStochOS_1 and not allMTFCCIOB_2 and not allMTFCCIOS_2)
    table.cell(label, 0, 0, text="", text_color=color.black, text_halign=text.align_left, text_size=size.small, bgcolor=color.new(#000000, 100))


// We use `var` to only initialize the table on the first bar.
var table dashboard = table.new(position.top_right, 4, 4, bgcolor = MTFTableColor, frame_width = 0, frame_color = na)

LabelColor      = MTFTableTheme=="Dark" ? color.new(color.white, 70) : color.new(color.black, 70)
ValueColor      = MTFTableTheme=="Dark" ? color.new(color.white, 20) : color.new(color.black, 20)

_CCIlabelColor(val, ob, os) =>
    val > ob ? overboughtColor_2 : (val < os ? oversoldColor_2 : ValueColor)

_SRSIlabelColor(val, ob, os) =>
    val > ob ? overboughtColor_1 : (val < os ? oversoldColor_1 : ValueColor)

// We call functions like `ta.atr()` outside the `if` block so it executes on each bar.
if barstate.islast and showMTFVals
    // We only populate the table on the last bar.
    if (MTFTableType=="MTF CCI")
        table.cell(dashboard, 0, 0, text="CCI", text_color=LabelColor, text_halign=text.align_left, text_size=size.small, width=2)
        table.cell(dashboard, 0, 1, text=TF_1_2+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 0, 2, text=TF_2_2+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 0, 3, text=TF_3_2+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 1, 0, text="", width=3, text_color=LabelColor, text_halign=text.align_left, text_size=size.small)
        table.cell(dashboard, 1, 1, text=str.tostring(math.round(MTF1_2, 0)) , text_color=_CCIlabelColor(MTF1_2, MTF_OBTHRESH_1_2, MTF_OSTHRESH_1_2), text_halign=text.align_right, text_size=size.small)
        table.cell(dashboard, 1, 2, text=str.tostring(math.round(MTF2_2, 0)) , text_color=_CCIlabelColor(MTF2_2, MTF_OBTHRESH_2_2, MTF_OSTHRESH_2_2), text_halign=text.align_right, text_size=size.small)
        table.cell(dashboard, 1, 3, text=str.tostring(math.round(MTF3_2, 0)), text_color=_CCIlabelColor(MTF3_2, MTF_OBTHRESH_3_2, MTF_OSTHRESH_3_2), text_halign=text.align_right, text_size=size.small)
    if (MTFTableType=="MTF Stoch RSI")
        table.cell(dashboard, 2, 0, text="SRSI", text_color=LabelColor, text_halign=text.align_left, text_size=size.small, width=2)
        table.cell(dashboard, 2, 1, text=TF_1_1+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 2, 2, text=TF_2_1+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 2, 3, text=TF_3_1+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 3, 0, text="", width=3, text_color=LabelColor, text_halign=text.align_left, text_size=size.small)
        table.cell(dashboard, 3, 1, text=str.tostring(math.round(MTF1_1, 0)) , text_color=_SRSIlabelColor(MTF1_1, MTF_OBTHRESH_1_1, MTF_OSTHRESH_1_1), text_halign=text.align_right, text_size=size.small)
        table.cell(dashboard, 3, 2, text=str.tostring(math.round(MTF2_1, 0)) , text_color=_SRSIlabelColor(MTF2_1, MTF_OBTHRESH_2_1, MTF_OSTHRESH_2_1), text_halign=text.align_right, text_size=size.small)
        table.cell(dashboard, 3, 3, text=str.tostring(math.round(MTF3_1, 0)) , text_color=_SRSIlabelColor(MTF3_1, MTF_OBTHRESH_3_1, MTF_OSTHRESH_3_1), text_halign=text.align_right, text_size=size.small)
    if (MTFTableType=="Both")
        table.cell(dashboard, 0, 0, text="CCI", text_color=LabelColor, text_halign=text.align_left, text_size=size.small, width=2)
        table.cell(dashboard, 0, 1, text=TF_1_2+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 0, 2, text=TF_2_2+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 0, 3, text=TF_3_2+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 1, 0, text="", width=3, text_color=LabelColor, text_halign=text.align_left, text_size=size.small)
        table.cell(dashboard, 1, 1, text=str.tostring(math.round(MTF1_2, 0)) , text_color=_CCIlabelColor(MTF1_2, MTF_OBTHRESH_1_2, MTF_OSTHRESH_1_2), text_halign=text.align_right, text_size=size.small)
        table.cell(dashboard, 1, 2, text=str.tostring(math.round(MTF2_2, 0)) , text_color=_CCIlabelColor(MTF2_2, MTF_OBTHRESH_2_2, MTF_OSTHRESH_2_2), text_halign=text.align_right, text_size=size.small)
        table.cell(dashboard, 1, 3, text=str.tostring(math.round(MTF3_2, 0)), text_color=_CCIlabelColor(MTF3_2, MTF_OBTHRESH_3_2, MTF_OSTHRESH_3_2), text_halign=text.align_right, text_size=size.small)

        table.cell(dashboard, 2, 0, text="SRSI", text_color=LabelColor, text_halign=text.align_left, text_size=size.small, width=2)
        table.cell(dashboard, 2, 1, text=TF_1_1+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 2, 2, text=TF_2_1+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 2, 3, text=TF_3_1+"m", text_color=LabelColor, text_halign=text.align_left, text_size=size.tiny)
        table.cell(dashboard, 3, 0, text="", width=3, text_color=LabelColor, text_halign=text.align_left, text_size=size.small)
        table.cell(dashboard, 3, 1, text=str.tostring(math.round(MTF1_1, 0)) , text_color=_SRSIlabelColor(MTF1_1, MTF_OBTHRESH_1_1, MTF_OSTHRESH_1_1), text_halign=text.align_right, text_size=size.small)
        table.cell(dashboard, 3, 2, text=str.tostring(math.round(MTF2_1, 0)) , text_color=_SRSIlabelColor(MTF2_1, MTF_OBTHRESH_2_1, MTF_OSTHRESH_2_1), text_halign=text.align_right, text_size=size.small)
        table.cell(dashboard, 3, 3, text=str.tostring(math.round(MTF3_1, 0)) , text_color=_SRSIlabelColor(MTF3_1, MTF_OBTHRESH_3_1, MTF_OSTHRESH_3_1), text_halign=text.align_right, text_size=size.small)

