SFP Momentum
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Austimize

//Custom swing fail detector with levels, breakouts, and colored candles.

//Key: Arrow = pivot, x = anchor (major pivot), B = breakout, b = minor breakout, F = swing failure, f(rare) = minor swing failure, A = accumulation, D = distribution
//Blue line = last anchor (major pivot) level, gray = last pivot, green lines = last high anchor/pivot, red lines = last low anchor/pivot

//@version=5
indicator("Swing Fail Levels, Breakouts, Candles", "SF LBC", overlay = true)

lbL = input.int(4, "Left Lookback")
lbR = input.int(3, "Right Lookback")
useAccDist = input.bool(true, "Accumulation/Distribution?")
usePrevPivots = input.bool(true, "Plot Last Pivots?")
colorCandles = input.bool(true, "Color Candles?")

anchorR = math.round(lbR * 1.5)
anchorL = lbL * 2

get_lowest (source, num) =>
    lowval = 99999999.9
    for i = 1 to num
        if source[i] < lowval
            lowval := source[i]
    lowval

get_highest (source, num) =>
    highval = 0.0
    for i = 1 to num
        if source[i] > highval
            highval := source[i]
    highval

swingBull = 0 + close > open ? 1 : 0 + close > close[1] ? 1 : 0
swingBear = 0 + close > open ? 0 : 1 + close > close[1] ? 1 : 0

isHighAnchor = ta.pivothigh(high, anchorL, anchorR)
prevHighAnchor = ta.valuewhen(isHighAnchor > 0.0, isHighAnchor, 1)

isLowAnchor = ta.pivotlow(low, anchorL, anchorR)
prevLowAnchor = ta.valuewhen(isLowAnchor > 0.0, isLowAnchor, 1)

lastAnchor = 0
lastAnchor := isHighAnchor and not isLowAnchor ? 1 : isLowAnchor and not isHighAnchor ? -1 : lastAnchor[1]

swingBull += lastAnchor == -1 ? 1 : 0
swingBear += lastAnchor == 1 ? 1 : 0

plotshape(isHighAnchor, "High Anchor", style = shape.xcross, location = location.abovebar, color = color.green, offset = -anchorR)
plotshape(isLowAnchor, "Low Anchor", style = shape.xcross, location = location.belowbar, color = color.red, offset = -anchorR)

barsSinceHigh = 0
barsSinceLow = 0
barsSinceHigh := barsSinceHigh[1] + 1
barsSinceLow := barsSinceLow[1] + 1

lowSwing = low[barsSinceLow]
highSwing = high[barsSinceHigh]

pivotHigh = ta.pivothigh(high, lbL, lbR)
prevHighPivot = ta.valuewhen(pivotHigh > 0.0, pivotHigh, 1)

pivotLow = ta.pivotlow(low, lbL, lbR)
prevLowPivot = ta.valuewhen(pivotLow > 0.0, pivotLow, 1)

highSameSame = prevHighAnchor == prevHighPivot
lowSameSame = prevLowAnchor == prevLowPivot
prevHighColor = highSameSame ? color.new(color.green, 70) : color.new(color.green, 90)
prevLowColor = lowSameSame ? color.new(color.red, 70) : color.new(color.red, 90)

plot(usePrevPivots and highSameSame ? prevHighAnchor : na, color = prevHighColor, style=plot.style_stepline, linewidth = 3)
plot(usePrevPivots and lowSameSame ? prevLowAnchor : na, color = prevLowColor, style=plot.style_stepline, linewidth = 3)

plot(usePrevPivots and not highSameSame ? prevHighAnchor : na, color = prevHighColor, style=plot.style_stepline, linewidth = 2)
plot(usePrevPivots and not lowSameSame ? prevLowAnchor : na, color = prevLowColor, style=plot.style_stepline, linewidth = 2)

plot(usePrevPivots ? prevHighPivot : na, color = prevHighColor, style=plot.style_stepline, linewidth = 1)
plot(usePrevPivots ? prevLowPivot : na, color = prevLowColor, style=plot.style_stepline, linewidth = 1)


lastPivot = 0
lastPivot := pivotHigh and not pivotLow ? 1 : pivotLow and not pivotHigh ? -1 : lastPivot[1]

barsSinceHighPivot = 0
barsSinceLowPivot = 0
barsSinceHighPivot := barsSinceHighPivot[1] + 1
barsSinceLowPivot := barsSinceLowPivot[1] + 1

pivotHighline = high[barsSinceHighPivot]
pivotLowline = low[barsSinceLowPivot]

plot(pivotHighline, "Pivot High", color = color.new(color.white, 75))
plot(pivotLowline, "Pivot Low", color = color.new(color.white, 75))

swingBull += lastPivot == -1 ? 1 : 0
swingBear += lastPivot == 1 ? 1 : 0

plotshape(pivotHigh, "High Pivot", style = shape.arrowup, location = location.abovebar, color = color.green, offset = -lbR)
plotshape(pivotLow, "Low Pivot", style = shape.arrowdown, location = location.belowbar, color = color.red, offset = -lbR)

swingFailHigh = pivotHigh and close[lbR] < highSwing ? true : false
swingFailLow = pivotLow and close[lbR] > lowSwing ? true : false

swingFailHighMinor = pivotHigh and close[lbR] < pivotHighline ? true : false
swingFailLowMinor = pivotLow and close[lbR] > pivotLowline ? true : false

lastMinorFail = 0
lastMinorFail := swingFailHighMinor and not swingFailLowMinor ? 1 : swingFailLowMinor and not swingFailHighMinor ? -1 : lastMinorFail[1]

swingBull += lastMinorFail == -1 and lastPivot == -1 ? 2 : lastMinorFail == -1 ? 1 : 0 
swingBear += lastMinorFail == 1 and lastPivot == 1 ? 2 : lastMinorFail == 1 ? 1 : 0

plotHighFail = swingFailHighMinor and not swingFailHigh 
plotLowFail = swingFailLowMinor and not swingFailLow
plotchar(plotHighFail, "Minor Swing Fail", char = "f", location = location.belowbar, color = color.green, offset = -lbR)
plotchar(plotLowFail, "Minor Swing Fail", char = "f", location = location.abovebar, color = color.red, offset = -lbR)

lastFail = 0
lastFail := swingFailHigh and not swingFailLow ? 1 : swingFailLow and not swingFailHigh ? -1 : lastFail[1]

swingBull += lastFail == -1 and lastAnchor == -1 ? 2 : lastFail == -1 ? 1 : 0 
swingBear += lastFail == 1 and lastAnchor == 1 ? 2 : lastFail == 1 ? 1 : 0

plotchar(swingFailHigh, "Swing Fail", char = "F", location = location.belowbar, color = color.green, offset = -lbR)
plotchar(swingFailLow, "Swing Fail", char = "F", location = location.abovebar, color = color.red, offset = -lbR)

highBreakout = close > highSwing and close > get_highest(close, anchorR - 1)
lowBreakout = close < lowSwing and close < get_lowest(close, anchorR - 1)

lastBreakout = 0
prevBreakout = lastBreakout[1]
lastBreakout := highBreakout ? 1 : lowBreakout? -1 : prevBreakout

lastBreakout := lastBreakout >= 1 and isHighAnchor ? -1 : lastBreakout
lastBreakout := lastBreakout <= 1 and isLowAnchor ? 1 : lastBreakout

minorHighBreakout = close > pivotHighline and close > get_highest(close, lbR - 1)
minorLowBreakout = close < pivotLowline and close < get_lowest(close, lbR - 1)

lastMinorBreakout = 0
prevMinorBreakout = lastMinorBreakout[1]
lastMinorBreakout := minorHighBreakout ? 1 : minorLowBreakout? -1 : prevMinorBreakout

lastMinorBreakout := lastMinorBreakout >= 1 and pivotHigh ? -1 : lastMinorBreakout
lastMinorBreakout := lastMinorBreakout <= 1 and pivotLow ? 1 : lastMinorBreakout

isMinorBreakHigh = minorHighBreakout and not highBreakout
isMinorBreakLow = minorLowBreakout and not lowBreakout

barsSinceHigh := isHighAnchor ? anchorR : barsSinceHigh
barsSinceLow := isLowAnchor ? anchorR : barsSinceLow

barsSinceHighPivot := pivotHigh ? lbR : barsSinceHighPivot
barsSinceLowPivot := pivotLow ? lbR : barsSinceLowPivot

swingBull += (lastBreakout == 1 ? 2 : 0) + (highBreakout and lastBreakout == 1 ? 3 : 0) + (lastMinorBreakout == 1 and (lastBreakout == 1 or lastMinorBreakout == 1) ? 2 : 0) + (lastMinorBreakout == 1 ? 1 : 0)
swingBear += (lastBreakout == -1 ? 2 : 0) + (lowBreakout and lastBreakout == -1 ? 3 : 0) + (lastMinorBreakout == -1 and (lastBreakout == -1 or lastMinorBreakout == -1) ? 2 : 0) + (lastMinorBreakout == -1 ? 1 : 0)

barsSinceHigh := isHighAnchor ? anchorR : barsSinceHigh
barsSinceLow := isLowAnchor ? anchorR : barsSinceLow

lowSwing := low[barsSinceLow]
highSwing := high[barsSinceHigh]
midSwing = (highSwing + lowSwing) / 2

lastCrossover = 0
lastCrossover := ta.crossover(close, lowSwing) or ta.crossover(close, midSwing) or ta.crossover(close, highSwing) ? 1 : lastCrossover[1]
lastCrossover := ta.crossunder(close, lowSwing) or ta.crossunder(close, midSwing) or ta.crossunder(close, highSwing) ? -1 : lastCrossover[1]

accum = highBreakout and prevBreakout == -1
dist = lowBreakout and prevBreakout == 1

accumM1 = minorHighBreakout and prevMinorBreakout == -1
distM1 = minorLowBreakout and prevMinorBreakout == 1

accumM2 = minorHighBreakout and prevBreakout == -1
distM2 = minorLowBreakout and prevBreakout == 1

accumM3 = highBreakout and prevMinorBreakout == -1
distM3 = lowBreakout and prevMinorBreakout == 1

isAccum = accum or accumM1 or accumM2 or accumM3
isDist = dist or distM1 or distM2 or distM3
currentAD = 0
lastAD = currentAD[1]
currentAD := isAccum ? 1 : isDist ? -1 : lastAD

plotchar(accum and useAccDist, "Accumulation", char = "A", location = location.top, color = color.green)
plotchar(dist and useAccDist, "Distribution", char = "D", location = location.bottom, color = color.red)

plotchar(isAccum and not accum and useAccDist, "Accumulation", char = "a", location = location.top, color = color.green)
plotchar(isDist and not dist and useAccDist, "Distribution", char = "d", location = location.bottom, color = color.red)

plotchar(highBreakout and not (isAccum and useAccDist), "High Breakout", char = "B", location = location.top, color = color.green)
plotchar(lowBreakout and not (isDist and useAccDist), "Low Breakout", char = "B", location = location.bottom, color = color.red)

plotchar(isMinorBreakHigh and not (isAccum and useAccDist), "Minor High Breakout", char = "b", location = location.top, color = color.green)
plotchar(isMinorBreakLow and not(isDist and useAccDist), "Minor Low Breakout", char = "b", location = location.bottom, color = color.red)

swingBull += (lastCrossover > 0 ? 2 : 0) + (close > midSwing ? 3 : 0) + (close > lowSwing and close < midSwing ? 1 : 0) + (currentAD == 1 ? 2 : 0) + (isAccum ? 2 : 0)
swingBear += (lastCrossover < 0 ? 2 : 0) + (close < midSwing ? 3 : 0) + (close < highSwing and close > midSwing? 1 : 0) + (currentAD == -1 ? 2 : 0) + (isDist ? 2 : 0)

plot(highSwing, "Anchored High")
plot(lowSwing, "Anchored Low")
plot(midSwing, "Midline", color = color.orange)

plotColor = color.orange
plotColor := swingBull > swingBear ? color.green : swingBear > swingBull ? color.red : swingBull[1] > swingBear[1] ? color.red : swingBear[1] > swingBull[1] ? color.green : plotColor[1]
barcolor(colorCandles ? plotColor : na, title = "Bar Color")