Signals and pivot divergences
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © mickes

//@version=5
indicator("Signals and pivot divergences", overlay = true, max_labels_count = 500, max_lines_count = 500)

_pivotLengthLeft = input.int(8, "Pivot length left", group = "General")
_pivotLengthRight = input.int(5, "Pivot length right", group = "General")
_source = input.source(close, "Source", group = "General")
_showPivots = input.bool(false, "Show pivots", group = "General", tooltip = "Only shows 'real' pivots, not 'end pivots' (if enabled)")
_pivotEnd = input.bool(true, "Different pivot in the end", tooltip = "Will create faster pivots in the end (wothout the right length) that are removed when real pivots are active")
_lastBarIndex = input.int(-1, "Last bar index")

_showSignals = input.bool(true, "Enable", group = "Signals")
_minBarsBackForSignal = input.int(20, "Indicator lookback", tooltip = "If there is a similar buy/sell signal from the same indicator within the bar limit it will not show", group = "Signals")
_minSignals = input.int(5, "Minimum number of signals", minval = 1, group = "Signals", tooltip = "Including the current one")
_minSignalsLookback = input.int(5, "Minimum number of signals lookback", minval = 0, group = "Signals", tooltip = "The minimun number of signals must be fulfilled within the lookback period (e.g. if set to 0 the minimum number of signals must be on the same bar)")
_buySignalColor = input.color(color.new(color.aqua, 20), "Buy", group = "Signals", inline = "buysignal")
_showBuySignals = input.bool(true, "", inline = "buysignal", group = "Signals")
_sellSignalColor = input.color(color.new(#fff59d, 20), "Sell", inline = "sellsignal", group = "Signals")
_showSellSignals = input.bool(true, "", inline = "sellsignal", group = "Signals")

_showBbSignals = input.bool(true, "Bollinger Band (BB)", group = "Signals")
_showRsiSignals = input.bool(true, "Relative Strength Index (RSI)", group = "Signals")
_showMacdSignals = input.bool(true, "Moving Average Convergence/Divergence (MACD)", group = "Signals")
_showCciSignals = input.bool(true, "Commodity Channel Index (CCI)", group = "Signals")
_showStochasticSignals = input.bool(true, "Stochastic", group = "Signals")
_showMfiSignals = input.bool(true, "Money Flow Index (MFI)", group = "Signals")

_showDivergences = input.bool(true, "Enable", group = "Divergences")
_maxDivergenceLookback = input.int(40, "Lookback", group = "Divergences")
_maxBarsOnWrongSide = input.float(20, "Maximum bars on wrong side %", minval = 0, maxval = 100, inline = "maxbars", group = "Divergences")
_maxBarsOnWrongSideCheck = input.bool(true, "", inline = "maxbars", tooltip = "use to disable lines that crosses highs or lows", group = "Divergences")
_minDivergences = input.int(2, "Minimum number of divergences", minval = 1, group = "Divergences")
_positiveRegularDivergenceColor = input.color(color.new(color.green, 20), "Positive regular", inline = "posiveregular", group = "Divergences")
_positiveRegularDivergenceStyle = input.string("Solid", "", ["Solid", "Dashed", "Dotted"], inline = "posiveregular", group = "Divergences")
_showPositiveRegularDivergence = input.bool(true, "", inline = "posiveregular", group = "Divergences")
_negativeRegularDivergenceColor = input.color(color.new(color.red, 20), "Negative regular", inline = "negativeregular", group = "Divergences")
_negativeRegularDivergenceStyle = input.string("Solid", "", ["Solid", "Dashed", "Dotted"], inline = "negativeregular", group = "Divergences")
_showNegativeRegularDivergence = input.bool(true, "", inline = "negativeregular", group = "Divergences")
_positiveHiddenDivergenceColor = input.color(color.new(color.olive, 20), "Positive hidden", inline = "posivehidden", group = "Divergences")
_positiveHiddenDivergenceStyle = input.string("Dashed", "", ["Solid", "Dashed", "Dotted"], inline = "posivehidden", group = "Divergences")
_showPositiveHiddenDivergence = input.bool(true, "", inline = "posivehidden", group = "Divergences")
_negativeHiddenDivergenceColor = input.color(color.new(color.orange, 20), "Negative hidden", inline = "negativehidden", group = "Divergences")
_negativeHiddenDivergenceStyle = input.string("Dashed", "", ["Solid", "Dashed", "Dotted"], inline = "negativehidden", group = "Divergences")
_showNegativeHiddenDivergence = input.bool(true, "", inline = "negativehidden", group = "Divergences")

_showRsiDivergences = input.bool(true, "Relative Strength Index (RSI)", group = "Divergences")
_showMacdDivergences = input.bool(true, "Moving Average Convergence/Divergence (MACD)", group = "Divergences")
_showMomentumDivergences = input.bool(true, "Momentum", group = "Divergences")
_showCciDivergences = input.bool(true, "Commodity Channel Index (CCI)", group = "Divergences")
_showObvDivergences = input.bool(true, "On Balance Volume (OBV)", group = "Divergences")
_showStochasticDivergences = input.bool(true, "Stochastic", group = "Divergences")
_showVwMacdDivergences = input.bool(true, "Volume Weighed MACD (VWMACD)", group = "Divergences")
_showChaikinMoneyFlowDivergences = input.bool(true, "Chaikin Money Flow (CMF)", group = "Divergences")
_showMoneyFlowIndexDivergences = input.bool(true, "Money Flow Index (MFI)", group = "Divergences")
_showVolumeDivergences = input.bool(true, "Volume", group = "Divergences")

_alertOn = input.string("All", "On", ["All", "Signals", "Divergences"], group = "Alert", tooltip = "Divergences alerts lag by right pivot length + 1. Doesn't affect 'end pivots' (if enabled)")
_alertFrequency = input.string("Once per bar close", "Frequency", ["All", "Once per bar", "Once per bar close"], group = "Alert")
_alertPivotEndOnBarClose = input.bool(true, "Alert 'end pivots' only on close", group = "Alert", tooltip = "'end pivots' (if enabled) are always notified when a new one occures (but not when more divergences happens (to avoid spaming)) unless this is checked")

type signalType
    bool Active = false
    bool Shown = false
    bool Show = false
    float Value
    int BarIndex
    string Name

type indicatorType
    float Price
    string Text

type pivotsType
    float Price
    int BarIndex
    int Pivot // 0 = low, 1 = high

    indicatorType Macd
    indicatorType Rsi
    indicatorType Momentum
    indicatorType Cci
    indicatorType OnBalanceVolume
    indicatorType Stochastic
    indicatorType VwMacd
    indicatorType ChaikinMoneyFlow
    indicatorType MoneyFlowIndex
    indicatorType Volume

type divergenceInfoType
    int StartBarIndex
    float StartPrice
    int EndBarIndex
    float EndPrice
    string Text
    string Type
    int Pivot // 0 = low, 1 = high

type divergenceDrawingType
    line Line
    label Label
    int EndBarIndex = 0
    int StartBarIndex = 0

type pivotEndType
    float High = 0
    float Low = 2147483647
    int Start = -2147483648
    int End = 0
    box Box

var _pivotLows = array.new<pivotsType>()
var _pivotHighs = array.new<pivotsType>()

var _divergenceDrawingsEnd = array.new<divergenceDrawingType>()
varip _divergenceAlerts = array.new<int>()

_atr = ta.atr(14)

[_, _bollingerBandUpper, _bollingerBandLower] = ta.bb(_source, 20, 2)
_rsi = ta.rsi(_source, 14)
[_macdLine, _macdSignalLine, _] = ta.macd(_source, 12, 26, 9)

_trueStrengthIndex = ta.tsi(_source, 13, 25)

// taken from 'LonesomeTheBlue's indicator https://www.tradingview.com/v/n8AGnIZd/n8AGnIZd
_momentum = ta.mom(close, 10)
_cci = ta.cci(close, 10)
_onBalanceVolume = ta.obv
_stochastic = ta.sma(ta.stoch(close, high, low, 14), 3)
_maFast = ta.vwma(close, 12), _maSlow = ta.vwma(close, 26), _vwMacd = _maFast - _maSlow
_cmfm = ((close - low) - (high - close)) / (high - low), _cmfv = _cmfm * volume, _chaikinMoneyFlow = ta.sma(_cmfv, 21) / ta.sma(volume, 21)
_moneyFlowIndex = ta.mfi(hlc3, 14)
_volume = volume

lastBarIteration() =>
    barstate.islast and barstate.isconfirmed
toI(array) =>
    toI = array.size(array) - 1
    if toI < 0
        toI := na

    toI
unixTimeToString(unixTime) =>
    timeString = str.format_time(unixTime, "yyyy-MM-dd HH:mm:ss", syminfo.timezone)

    timeString
getAlertText(alertTexts) =>
    string alertText = na
    for [i, txt] in alertTexts
        if i == 0
            alertText := txt
        else
            alertText += "\n" + txt

    alertText
_alertTexts = array.new<string>()
addAlert(type, txt) =>
    if type == _alertOn
      or _alertOn == "All"
        array.push(_alertTexts, txt)
sendAlert() =>
    alerts = array.size(_alertTexts)
    if alerts > 0
        alertText = getAlertText(_alertTexts)

        switch _alertFrequency
            "All" =>
                alert(alertText, alert.freq_all)
            "Once per bar" =>
                alert(alertText, alert.freq_once_per_bar)
            "Once per bar close" =>
                alert(alertText, alert.freq_once_per_bar_close)
_endAlertTexts = array.new<string>()
addEndAlert(type, txt, length) =>
    if type == _alertOn
      or _alertOn == "All"
        if _alertPivotEndOnBarClose
            if lastBarIteration()
                array.push(_endAlertTexts, txt)
        else if not array.includes(_divergenceAlerts, length)
            array.push(_divergenceAlerts, length)
            array.push(_endAlertTexts, txt)
sendEndAlert() =>
    alerts = array.size(_endAlertTexts)
    if alerts > 0
        alertText = getAlertText(_endAlertTexts)
        if _alertPivotEndOnBarClose
            alert(alertText, alert.freq_once_per_bar_close)
        else
            alert(alertText, alert.freq_all)
clearEndDivergenceLengths() =>
    if lastBarIteration()
        array.clear(_divergenceAlerts)
signals(buyCross, sellCross, value, name) =>
    var buys = array.new<signalType>()
    var sells = array.new<signalType>()

    buy = signalType.new(), sell = signalType.new()
    if buyCross
        buy.Active := true
        buy.BarIndex := bar_index
        buy.Name := name
        buy.Value := value
        array.unshift(buys, buy)

    if sellCross
        sell.Active := true
        sell.BarIndex := bar_index
        sell.Name := name
        sell.Value := value
        array.unshift(sells, sell)

    [buy, sell, buys, sells]
bollingerBandSignals(name) =>
    sellCross = ta.crossover(high, _bollingerBandUpper)
    buyCross = ta.crossunder(low, _bollingerBandLower)

    signals(buyCross, sellCross, _bollingerBandUpper, name)
macdSignals(name) =>
    buyCross = ta.crossover(_macdLine, _macdSignalLine)
    sellCross = ta.crossunder(_macdLine, _macdSignalLine)
    
    signals(buyCross, sellCross, -1, name)
rsiSignals(name) =>
    sellCross = ta.crossover(_rsi, 70)
    buyCross = ta.crossunder(_rsi, 30)

    signals(buyCross, sellCross, _rsi, name)
cciSignals(name) =>
    sellCross = ta.crossunder(_cci, 100)
    buyCross = ta.crossover(_cci, -100)

    signals(buyCross, sellCross, _cci, name)
stochasticSignals(name) =>
    sellCross = ta.crossover(_stochastic, 80)
    buyCross = ta.crossunder(_stochastic, 20)

    signals(buyCross, sellCross, _stochastic, name)
mfiSignals(name) =>
    sellCross = ta.crossunder(_moneyFlowIndex, 80)
    buyCross = ta.crossover(_moneyFlowIndex, 20)

    signals(buyCross, sellCross, _moneyFlowIndex, name)
// also handles limitations on the same type indicator
createSignal(s, previousSignals, signals, allSignals) =>
    if s.Active
        array.unshift(allSignals, s)

        for previousSignal in previousSignals
            if previousSignal.BarIndex == bar_index
                continue // skip current if already added

            if previousSignal.Shown
              and previousSignal.BarIndex >= bar_index - _minBarsBackForSignal
                array.push(signals, s)
                break
            else if previousSignal.BarIndex < bar_index - _minBarsBackForSignal
                s.Show := true
                array.push(signals, s)
                break
getSignalsTextAndSetShown(signals, size) =>
    string txt = na, string tooltip = na, allIsNoShow = true
    for i = 0 to size - 1
        s = array.get(signals, i)
        string signalText = na
        switch s.Show
            true =>
                s.Shown := true
                signalText := str.format("{0}", s.Name)
                allIsNoShow := false
            false =>
                signalText := str.format("({0})", s.Name)
                if na(tooltip)
                    tooltip := str.format("{0} should not be shown alone", s.Name)
                else
                    tooltip += str.format("\n{0} should not be shown alone", s.Name)

        if i == 0
            txt := signalText
        else
            txt += "\n" + signalText

    if allIsNoShow
        txt := na

    [txt, tooltip]
createSingnalsLabel(signals, direction) =>
    signalSize = array.size(signals)
    if signalSize > 0
        [txt, tooltip] = getSignalsTextAndSetShown(signals, signalSize)

        if not na(txt)
            alertText = str.replace_all(txt, "\n", ", ")

            switch direction
                0 =>
                    if _showSellSignals
                        label.new(bar_index, high, txt, color = _sellSignalColor, tooltip = tooltip)
                        addAlert("Signals", str.format("Sell signal from {0}", alertText))
                1 =>
                    if _showBuySignals
                        label.new(bar_index, low, txt, color = _buySignalColor, tooltip = tooltip, style = label.style_label_up)
                        addAlert("Signals", str.format("Buy signal from {0}", alertText))
showPivots(pivotLow, pivotHigh) =>
    var float previousPivotPrice = na
    var int previousPivotBarIndex = na

    barIndex = bar_index - _pivotLengthRight
    isLow = not na(pivotLow)
    price = isLow ? low[_pivotLengthRight] : high[_pivotLengthRight]

    if isLow
        label.new(barIndex, price, "▼", style = label.style_label_up, color = color.new(color.red, 70))

        if not na(previousPivotBarIndex)
            line.new(previousPivotBarIndex, previousPivotPrice, barIndex, price, color = color.new(color.orange, 80), style = line.style_dashed)

        previousPivotBarIndex := barIndex
        previousPivotPrice := price
    else
        label.new(barIndex, price, "▲", color = color.new(color.green, 70))

        if not na(previousPivotBarIndex)
            line.new(previousPivotBarIndex, previousPivotPrice, barIndex, price, color = color.new(color.green, 80), style = line.style_dashed)

        previousPivotBarIndex := barIndex
        previousPivotPrice := price
pivots() =>
    pivotLow = ta.pivotlow(low, _pivotLengthLeft, _pivotLengthRight)
    pivotHigh = ta.pivothigh(high, _pivotLengthLeft, _pivotLengthRight)

    pivotLowEnd = ta.pivotlow(low, _pivotLengthLeft, 0)
    pivotHighEnd = ta.pivothigh(high, _pivotLengthLeft, 0)
    
    lastBarIndex = _lastBarIndex != -1 ? _lastBarIndex : last_bar_index

    if _pivotEnd
      and (bar_index > lastBarIndex - _pivotLengthRight and bar_index <= lastBarIndex)
        var pivotEnd = pivotEndType.new()
        if na(pivotEnd.Box)
            pivotEnd.Box := box.new(bar_index, high, lastBarIndex, low, bgcolor = color.new(color.blue,70))

        if low < pivotEnd.Low
            pivotEnd.Low := low
            box.set_bottom(pivotEnd.Box, low)
        if high > pivotEnd.High
            pivotEnd.High := high
            box.set_top(pivotEnd.Box, high)
        if lastBarIndex > pivotEnd.End
            pivotEnd.End := lastBarIndex
            box.set_right(pivotEnd.Box, lastBarIndex)
        if bar_index + _pivotLengthRight > pivotEnd.Start
            pivotEnd.Start := bar_index - (_pivotLengthRight - 1)
            box.set_left(pivotEnd.Box, bar_index - (_pivotLengthRight - 1))

        [pivotLow, pivotHigh, pivotLowEnd, pivotHighEnd]
    else if bar_index < lastBarIndex - _pivotLengthRight
        [pivotLow, pivotHigh, na, na]
    else
        [na, na, na, na]
barsIsOnRightSide(l, pivotExtreme, startBarIndex, endBarIndex) =>
    barsOnWrongSide = 0
    for j = bar_index - startBarIndex to bar_index - endBarIndex
        linePrice = line.get_price(l, bar_index - j)
        if pivotExtreme == 0 // low pivot
            if low[j] < linePrice
                barsOnWrongSide += 1
        else if pivotExtreme == 1 // high pivot
            if high[j] > linePrice
                barsOnWrongSide += 1

    barsIsOnRightSide = barsOnWrongSide / ((bar_index - startBarIndex) - (bar_index - endBarIndex)) <= _maxBarsOnWrongSide * 0.01

    barsIsOnRightSide
getIndicator(pivot, name) =>
    indicatorType indicator = na

    switch name
        "RSI" => indicator := pivot.Rsi
        "MACD" => indicator := pivot.Macd
        "MOM" => indicator := pivot.Momentum
        "CCI" => indicator := pivot.Cci
        "OBV" => indicator := pivot.OnBalanceVolume
        "Stoch" => indicator := pivot.Stochastic
        "VWMACD" => indicator := pivot.VwMacd
        "CMF" =>indicator := pivot.ChaikinMoneyFlow
        "MFI" => indicator := pivot.MoneyFlowIndex
        "VOL" => indicator := pivot.Volume

    indicator
addOrUpdateDivergenceInfos(divergenceInfos, startBarIndex, startPrice, endBarIndex, endPrice, pivotText, pivot, divergenceTypeText) =>
    updated = false
    for divergenceRow in divergenceInfos
        if divergenceRow.StartBarIndex == startBarIndex
            divergenceRow.Text := divergenceRow.Text + "\n" + pivotText
            updated := true
            break

    if not updated
        array.push(divergenceInfos, divergenceInfoType.new(startBarIndex, startPrice, endBarIndex, endPrice, pivotText, divergenceTypeText, pivot))
setDivergence(pivots, name, divergenceInfos, pivotExtreme, barsAgo) =>
    toI = array.size(pivots) - 1
    if toI >= 1
        basePivot = array.get(pivots, toI)
        if basePivot.BarIndex == bar_index - barsAgo
            for i = toI - 1 to 0
                currentPivot = array.get(pivots, i)
            
                if currentPivot.BarIndex <= basePivot.BarIndex - _maxDivergenceLookback
                    break

                baseIndicator = getIndicator(basePivot, name)
                currentIndicator = getIndicator(currentPivot, name)

                if basePivot.Price < currentPivot.Price // \
                  and baseIndicator.Price > currentIndicator.Price
                    string divergenceTypeText = na
                    if pivotExtreme == 0
                      and basePivot.Pivot == 0
                        divergenceTypeText := "Positive regular"
                    else if pivotExtreme == 1
                      and basePivot.Pivot == 1
                        divergenceTypeText := "Negative hidden"


                    addOrUpdateDivergenceInfos(divergenceInfos, currentPivot.BarIndex, currentPivot.Price, basePivot.BarIndex, basePivot.Price, currentIndicator.Text, pivotExtreme, divergenceTypeText)
                else if basePivot.Price > currentPivot.Price // /
                  and baseIndicator.Price < currentIndicator.Price
                    string divergenceTypeText = na
                    if pivotExtreme == 0
                      and basePivot.Pivot == 0
                        divergenceTypeText := "Positive hidden"
                    else if pivotExtreme == 1
                      and basePivot.Pivot == 1
                        divergenceTypeText := "Negative regular"

                    addOrUpdateDivergenceInfos(divergenceInfos, currentPivot.BarIndex, currentPivot.Price, basePivot.BarIndex, basePivot.Price, currentIndicator.Text, pivotExtreme, divergenceTypeText)
createDivergenceLineAndLabel(divergenceInfos) =>
    for divergenceRow in divergenceInfos
        if array.size(str.split(divergenceRow.Text, "\n")) < _minDivergences
            continue

        if not _showPositiveRegularDivergence
          and divergenceRow.Type == "Positive regular"
            continue
        else if not _showNegativeRegularDivergence
          and divergenceRow.Type == "Negative regular"
            continue
        else if not _showPositiveHiddenDivergence
          and divergenceRow.Type == "Positive hidden"
            continue
        else if not _showNegativeHiddenDivergence
          and divergenceRow.Type == "Negative hidden"
            continue

        divergenceLine = line.new(divergenceRow.StartBarIndex, divergenceRow.StartPrice, divergenceRow.EndBarIndex, divergenceRow.EndPrice, width = 2)
        if _maxBarsOnWrongSideCheck
            if not barsIsOnRightSide(divergenceLine, divergenceRow.Pivot, divergenceRow.StartBarIndex, divergenceRow.EndBarIndex)
                line.delete(divergenceLine)
                continue

        labelLocation = divergenceRow.StartBarIndex + ((divergenceRow.EndBarIndex - divergenceRow.StartBarIndex) / 2)
        labelPrice = divergenceRow.Pivot == 0 ? line.get_price(divergenceLine, labelLocation) - _atr : line.get_price(divergenceLine, labelLocation) + _atr
        labelStyle = divergenceRow.Pivot == 0 ? label.style_label_up : label.style_label_down
        end = divergenceRow.EndBarIndex == bar_index
        labelText = end ? "(end)\n" + divergenceRow.Text : divergenceRow.Text
        divergenceLabel = label.new(labelLocation, labelPrice, labelText, style = labelStyle, color = color.green)

        alertText = str.format("{0} pivot divergence ({1} bars) from {2}", divergenceRow.Type, divergenceRow.EndBarIndex - divergenceRow.StartBarIndex, str.replace_all(divergenceRow.Text, "\n", ", "))
        if end
            addEndAlert("Divergences", str.format("{0} (end)", alertText), divergenceRow.EndBarIndex - divergenceRow.StartBarIndex)
        else
            addAlert("Divergences", alertText)

        switch divergenceRow.Type
            "Positive regular" =>
                line.set_color(divergenceLine, _positiveRegularDivergenceColor)
                switch _positiveRegularDivergenceStyle
                    "Dashed" => line.set_style(divergenceLine, line.style_dashed)
                    "Dotted" => line.set_style(divergenceLine, line.style_dotted)
                label.set_color(divergenceLabel, _positiveRegularDivergenceColor)
            "Negative regular" =>
                line.set_color(divergenceLine, _negativeRegularDivergenceColor)
                switch _negativeRegularDivergenceStyle
                    "Dashed" => line.set_style(divergenceLine, line.style_dashed)
                    "Dotted" => line.set_style(divergenceLine, line.style_dotted)
                label.set_color(divergenceLabel, _negativeRegularDivergenceColor)
            "Positive hidden" =>
                line.set_color(divergenceLine, _positiveHiddenDivergenceColor)
                switch _positiveHiddenDivergenceStyle
                    "Dashed" => line.set_style(divergenceLine, line.style_dashed)
                    "Dotted" => line.set_style(divergenceLine, line.style_dotted)
                label.set_color(divergenceLabel, _positiveHiddenDivergenceColor)
            "Negative hidden" =>
                line.set_color(divergenceLine, _negativeHiddenDivergenceColor)
                switch _negativeHiddenDivergenceStyle
                    "Dashed" => line.set_style(divergenceLine, line.style_dashed)
                    "Dotted" => line.set_style(divergenceLine, line.style_dotted)
                label.set_color(divergenceLabel, _negativeHiddenDivergenceColor)

        if end
            array.push(_divergenceDrawingsEnd, divergenceDrawingType.new(divergenceLine, divergenceLabel, divergenceRow.EndBarIndex, divergenceRow.StartBarIndex))
removeLimitSignals(signals, allSignals) =>
    previousSignalsWithinLookback = 0
    for s in allSignals
        if s.BarIndex >= bar_index - _minSignalsLookback
            previousSignalsWithinLookback += 1

        if s.BarIndex < bar_index - _minSignalsLookback
            if previousSignalsWithinLookback < _minSignals
                array.clear(signals)
            break
signalsLabelsAll() =>
    var allSellSignals = array.new<signalType>()
    var allBuySignals = array.new<signalType>()

    [bollingerBandBuy, bollingerBandSell, bollingerBandBuys, bollingerBandSells] = bollingerBandSignals("BB")
    [macdBuy, macdSell, macdBuys, macdSells] = macdSignals("MACD")
    [rsiBuy, rsiSell, rsiBuys, rsiSells] = rsiSignals("RSI")
    [cciBuy, cciSell, cciBuys, cciSells] = cciSignals("CCI")
    [stochasticBuy, stochasticSell, stochasticBuys, stochasticSells] = stochasticSignals("Stoch")
    [mfiBuy, mfiSell, mfiBuys, mfiSells] = mfiSignals("MFI")

    sellSignals = array.new<signalType>()
    buySignals = array.new<signalType>()

    if _showBbSignals
        createSignal(bollingerBandSell, bollingerBandSells, sellSignals, allSellSignals)
        createSignal(bollingerBandBuy, bollingerBandBuys, buySignals, allBuySignals)
    if _showMacdSignals
        createSignal(macdSell, macdSells, sellSignals, allSellSignals)
        createSignal(macdBuy, macdBuys, buySignals, allBuySignals)
    if _showRsiSignals
        createSignal(rsiSell, rsiSells, sellSignals, allSellSignals)
        createSignal(rsiBuy, rsiBuys, buySignals, allBuySignals)
    if _showCciSignals
        createSignal(cciSell, cciSells, sellSignals, allSellSignals)
        createSignal(cciBuy, cciBuys, buySignals, allBuySignals)
    if _showStochasticSignals
        createSignal(stochasticSell, stochasticSells, sellSignals, allSellSignals)
        createSignal(stochasticBuy, stochasticBuys, buySignals, allBuySignals)
    if _showMfiSignals
        createSignal(mfiSell, mfiSells, sellSignals, allSellSignals)
        createSignal(mfiBuy, mfiBuys, buySignals, allBuySignals)

    removeLimitSignals(sellSignals, allSellSignals)
    removeLimitSignals(buySignals, allBuySignals)

    createSingnalsLabel(sellSignals, 0)
    createSingnalsLabel(buySignals, 1)
setIndicatorValues(pivot, value, name) =>
    switch name
        "RSI" => pivot.Rsi := indicatorType.new(value, name)
        "MACD" => pivot.Macd := indicatorType.new(value, name)
        "MOM" => pivot.Momentum := indicatorType.new(value, name)
        "CCI" => pivot.Cci := indicatorType.new(value, name)
        "OBV" => pivot.OnBalanceVolume := indicatorType.new(value, name)
        "Stoch" => pivot.Stochastic := indicatorType.new(value, name)
        "VWMACD" => pivot.VwMacd := indicatorType.new(value, name)
        "CMF" => pivot.ChaikinMoneyFlow := indicatorType.new(value, name)
        "MFI" => pivot.MoneyFlowIndex := indicatorType.new(value, name)
        "VOL" => pivot.Volume := indicatorType.new(value, name)
divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, value, name, barsAgo) =>
    setIndicatorValues(pivot, value, name)
    setDivergence(pivots, name, divergenceInfos, pivotExtreme, barsAgo)
divergencePivotAndValuesAll(pivotLow, pivotHigh, pivotLowEnd, pivotHighEnd) =>
    if pivotLow
        divergenceInfos = array.new<divergenceInfoType>()
        pivotExtreme = 0, barsAgo = _pivotLengthRight

        pivot = pivotsType.new(pivotLow, bar_index - barsAgo, pivotExtreme)
        array.push(_pivotLows, pivot)
        
        if _showMacdDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _macdLine[barsAgo], "MACD", barsAgo)
        if _showRsiDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _rsi[barsAgo], "RSI", barsAgo)
        if _showMomentumDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _momentum[barsAgo], "MOM", barsAgo)
        if _showCciDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _cci[barsAgo], "CCI", barsAgo)
        if _showObvDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _onBalanceVolume[barsAgo], "OBV", barsAgo)
        if _showStochasticDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _stochastic[barsAgo], "Stoch", barsAgo)
        if _showVwMacdDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _vwMacd[barsAgo], "VWMACD", barsAgo)
        if _showChaikinMoneyFlowDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _chaikinMoneyFlow[barsAgo], "CMF", barsAgo)
        if _showMoneyFlowIndexDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _moneyFlowIndex[barsAgo], "MFI", barsAgo)
        if _showVolumeDivergences
            divergencePivotAndValue(_pivotLows, pivot, divergenceInfos, pivotExtreme, _volume[barsAgo], "VOL", barsAgo)

        createDivergenceLineAndLabel(divergenceInfos)
    if pivotLowEnd
        divergenceInfos = array.new<divergenceInfoType>()
        pivotExtreme = 0, barsAgo = 0, pivots = _pivotLows

        pivot = pivotsType.new(pivotLowEnd, bar_index - barsAgo, pivotExtreme)
        array.push(pivots, pivot)
        
        if _showMacdDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _macdLine[barsAgo], "MACD", barsAgo)
        if _showRsiDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _rsi[barsAgo], "RSI", barsAgo)
        if _showMomentumDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _momentum[barsAgo], "MOM", barsAgo)
        if _showCciDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _cci[barsAgo], "CCI", barsAgo)
        if _showObvDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _onBalanceVolume[barsAgo], "OBV", barsAgo)
        if _showStochasticDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _stochastic[barsAgo], "Stoch", barsAgo)
        if _showVwMacdDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _vwMacd[barsAgo], "VWMACD", barsAgo)
        if _showChaikinMoneyFlowDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _chaikinMoneyFlow[barsAgo], "CMF", barsAgo)
        if _showMoneyFlowIndexDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _moneyFlowIndex[barsAgo], "MFI", barsAgo)
        if _showVolumeDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _volume[barsAgo], "VOL", barsAgo)

        createDivergenceLineAndLabel(divergenceInfos)

    if pivotHigh
        divergenceInfos = array.new<divergenceInfoType>()
        pivotExtreme = 1, barsAgo = _pivotLengthRight

        pivot = pivotsType.new(pivotHigh, bar_index - barsAgo, pivotExtreme)
        array.push(_pivotHighs, pivot)

        if _showMacdDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _macdLine[barsAgo], "MACD", barsAgo)
        if _showRsiDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _rsi[barsAgo], "RSI", barsAgo)
        if _showMomentumDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _momentum[barsAgo], "MOM", barsAgo)
        if _showCciDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _cci[barsAgo], "CCI", barsAgo)
        if _showObvDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _onBalanceVolume[barsAgo], "OBV", barsAgo)
        if _showStochasticDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _stochastic[barsAgo], "Stoch", barsAgo)
        if _showVwMacdDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _vwMacd[barsAgo], "VWMACD", barsAgo)
        if _showChaikinMoneyFlowDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _chaikinMoneyFlow[barsAgo], "CMF", barsAgo)
        if _showMoneyFlowIndexDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _moneyFlowIndex[barsAgo], "MFI", barsAgo)
        if _showVolumeDivergences
            divergencePivotAndValue(_pivotHighs, pivot, divergenceInfos, pivotExtreme, _volume[barsAgo], "VOL", barsAgo)

        createDivergenceLineAndLabel(divergenceInfos)
    if pivotHighEnd
        divergenceInfos = array.new<divergenceInfoType>()
        pivotExtreme = 1, barsAgo = 0, pivots = _pivotHighs

        pivot = pivotsType.new(pivotHighEnd, bar_index - barsAgo, pivotExtreme)
        array.push(pivots, pivot)

        if _showMacdDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _macdLine[barsAgo], "MACD", barsAgo)
        if _showRsiDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _rsi[barsAgo], "RSI", barsAgo)
        if _showMomentumDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _momentum[barsAgo], "MOM", barsAgo)
        if _showCciDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _cci[barsAgo], "CCI", barsAgo)
        if _showObvDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _onBalanceVolume[barsAgo], "OBV", barsAgo)
        if _showStochasticDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _stochastic[barsAgo], "Stoch", barsAgo)
        if _showVwMacdDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _vwMacd[barsAgo], "VWMACD", barsAgo)
        if _showChaikinMoneyFlowDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _chaikinMoneyFlow[barsAgo], "CMF", barsAgo)
        if _showMoneyFlowIndexDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _moneyFlowIndex[barsAgo], "MFI", barsAgo)
        if _showVolumeDivergences
            divergencePivotAndValue(pivots, pivot, divergenceInfos, pivotExtreme, _volume[barsAgo], "VOL", barsAgo)

        createDivergenceLineAndLabel(divergenceInfos)
clearDivergenceDrawings() =>
    if bar_index >= last_bar_index - _pivotLengthRight// barstate.islast
        removed = 0, toI = toI(_divergenceDrawingsEnd)
        for i = 0 to toI
            if removed == toI
                break

            drawing = array.get(_divergenceDrawingsEnd, i - removed)

            if drawing.EndBarIndex == bar_index - _pivotLengthRight
                line.delete(drawing.Line)
                label.delete(drawing.Label)
                array.remove(_divergenceDrawingsEnd, i - removed)
                removed += 1
clearEndPivots(pivots) =>
    removed = 0, toI = toI(pivots)
    for i = 0 to toI
        if removed == toI
            break

        pivot = array.get(pivots, i - removed)
        if pivot.BarIndex == last_bar_index - _pivotLengthRight
            array.remove(pivots, i - removed)
            removed += 1

if _pivotEnd
    clearEndPivots(_pivotLows)
    clearEndPivots(_pivotHighs)

if _showSignals
    signalsLabelsAll()

[pivotLow, pivotHigh, pivotLowEnd, pivotHighEnd] = pivots()

if _showPivots
  and (not na(pivotHigh) or not na(pivotLow))
    showPivots(pivotLow, pivotHigh)

if _showDivergences
    divergencePivotAndValuesAll(pivotLow, pivotHigh, pivotLowEnd, pivotHighEnd)
    clearDivergenceDrawings()

sendAlert()
sendEndAlert()

clearEndDivergenceLengths()