Failed Breakdown Detection
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © xfuturesgod

//@version=5
indicator("FBD Detection", overlay = true)

//user input
n_bars = input.int(defval=55, title="n bars", minval=1, step=1)
amp_thr = input.float(defval=0.05, title="amplitude threshold in % of past max amplitude", minval=0.0, maxval=1.0, step=0.01)
lengthGroupTitle = "LENGTH LEFT / RIGHT"
colorGroupTitle = "Text Color / Label Color"
leftLenL = input.int(title="Pivot Low", defval=10, minval=1, inline="Pivot Low", group=lengthGroupTitle)
rightLenL = input.int(title="/", defval=10, minval=1, inline="Pivot Low", group=lengthGroupTitle)
textColorL = input(title="Pivot Low", defval=color.black, inline="Pivot Low", group=colorGroupTitle)
labelColorL = input(title="", defval=color.white, inline="Pivot Low", group=colorGroupTitle)
lowerFBDThreshold = input.float(defval = 1.5, title = "FBD New Low Threshold - Lower", minval = 1.5)
upperFBDThreshold = input.float(defval = 10, title = "FBD New Low Threshold - Upper", minval = 1.5)
barLimit = input.int(defval = 10, title = "Number of Bars Under which the Previous Low Must Reclaim", minval = 2)
reclaimValue = input.float(defval = 8, title = "Price Reclaim Amount", minval = 3)


// declare pivot low variable and create array persistent array to store pivot lows
// draw pivot low labels
pivotLow = ta.pivotlow(leftLenL, rightLenL)


drawLabel(_offset, _pivot, _style, _color, _textColor) =>
    if not na(_pivot)
        label.new(bar_index[_offset], _pivot, str.tostring(_pivot, format.mintick), style=_style, color=_color, textcolor=_textColor)

drawLabel(rightLenL, pivotLow, label.style_label_up, labelColorL, textColorL)

plotchar(pivotLow,title = "PL", char = "P", location = location.abovebar,color = color.white)
var pivotLowArray = array.new_float(10,pivotLow)
pivotLowArraySize = array.size(pivotLowArray)

if pivotLow
	array.push(pivotLowArray,pivotLow)


// plot first bar (18:00 EST for ES futures)
firstbar = session.isfirstbar
plotshape(firstbar,"opening bar", style = shape.flag, location = location.abovebar, color=color.blue )
EST730 = ta.barssince(firstbar) == 54
plotshape(EST730,"opening bar", style = shape.flag, location = location.abovebar, color=color.rgb(65, 209, 225) )

//amplitude

n_bars_hist = n_bars * 10

amp_up = (high - open) / open
amp_down = (open - low) / open

max_amp_up = ta.highest(amp_up, n_bars_hist)
max_amp_down = ta.highest(amp_down, n_bars_hist)

rel_amp_up = amp_up / max_amp_up
rel_amp_down = amp_down / max_amp_down

//nlnh value
//lowest/highest close/wick since x bars (relative to n_bars from 0 to 1)

lowest_wick_since = 1.0
highest_wick_since = 1.0

for i=1 to n_bars
	if lowest_wick_since == 1.0 and low > low[i]
    	lowest_wick_since := (i - 1) / n_bars
	if highest_wick_since == 1.0 and high < high[i]
    	highest_wick_since := (i - 1) / n_bars
	if lowest_wick_since != 1.0 and highest_wick_since != 1.0
    	break

new_low = lowest_wick_since == 1.0 and rel_amp_down >= amp_thr



plotshape(new_low, style=shape.triangledown, location=location.belowbar, color=color.rgb(82, 183, 255))

// // alerts for new highs or lows made during all hours
alertcondition(new_low, "New Low Made", message = "New Low")

// variable for detecting new lows during "prime time" hours - 730AM and 400PM EST
rthNewLow = new_low and ta.barssince(EST730) > 0 and ta.barssince(EST730) < 33

// alerts when a new low occurs during or close to rth - regular US trading hours
alertcondition(rthNewLow, "New Low - RTH (7:30AM - 4:00PM EST", message = "New Low - RTH")


// determine if we have a valid set up failed breakdown and mark the entry candle
// valid set up = the new low must be within a certain threshold (1.5-10 points) vs the most recent pivot low, and pivot low must be "reclaimed"
// .... reclaim = 8 (default) points above the pivot low, within 10 bars of the occurence of the new low, and must be a green candle
// valid set up is a reclaim off the most recent pivot low. Once that pivot low is "reclaimed", it is added to an array to avoid duplicate signals
// ... from the same pivot low

latestPivotLow = array.get(pivotLowArray,pivotLowArraySize-1)
validLow = (latestPivotLow - low >= lowerFBDThreshold) and (latestPivotLow - low <= upperFBDThreshold)
underCut = new_low and validLow
var validSetupLevels = array.new_float(10,close)
validSetupLevelsArraySize = array.size(validSetupLevels)
lastLevelTraded = array.get(validSetupLevels,validSetupLevelsArraySize-1)


fbdEntry = (ta.barssince(underCut) < barLimit) and (high > (latestPivotLow + reclaimValue)) and (close > open) and (lastLevelTraded != latestPivotLow)

if fbdEntry
	array.push(validSetupLevels,latestPivotLow)
	

plotchar(fbdEntry,title="fbdEntry",char = "X", location = location.abovebar, color = color.rgb(0, 255, 255))
alertcondition(fbdEntry, "Failed Breakdown Detected", message = "FBD Detected")

// variable for detecting new lows during "prime time" hours - 730AM and 400PM EST
rthFBD = fbdEntry and ta.barssince(EST730) > 0 and ta.barssince(EST730) < 33

// alerts when a new low occurs during or close to rth - regular US trading hours
alertcondition(rthFBD, "FBD - RTH (7:30AM - 4:00PM EST", message = "New Low - RTH")
