//@version=6
indicator('Fair Value Gap Finder - Close Condition Mode', overlay = true)
// Inputs
useVolume = input.bool(false, 'Use Volume to determine FVGs (Recommended)', group = 'Volume')
vr = input.int(2, 'Volume must be X times average', step = 1, group = 'Volume')
vl = input.int(20, 'Volume moving average length', step = 1, group = 'Volume')
emaLength = input.int(20, 'EMA Length', step = 1, group = 'Trend Identification')
ol = input.int(60, 'Gap length', step = 10, group = 'Mind The Gap')
useAutoMinGapSize = input.bool(true, 'Automatically adjust minimum gap size based on ATR', group = 'Mind The Gap')
minGapSize = input.float(0.25, 'Minimum Gap Size to show (ignored if Automatically adjust gap is selected)', step = 0.05, group = 'Mind The Gap')
onlyShowLast = input.bool(false, 'Only show latest gap', group = 'Mind The Gap')
deleteFilledGaps = input.bool(true, 'Delete Filled Gaps', group = 'Mind The Gap')
if useAutoMinGapSize
minGapSize := ta.atr(14)
minGapSize
ob = volume[1] > ta.sma(volume[1], vl) * vr
// Colors and Label Styles
sellFVGColor = input.color(color.new(color.red, 70), group = 'Styles')
buyFVGColor = input.color(color.new(color.green, 70), group = 'Styles')
buyFVGText = input.string('Buyers Fair Value Gap', group = 'Styles')
sellFVGText = input.string('Sellers Fair Value Gap', group = 'Styles')
textColor = input.color(color.new(color.gray, 10), group = 'Styles')
sizeOption = input.string(title = 'Label Size', options = ['Auto', 'Huge', 'Large', 'Normal', 'Small', 'Tiny'], defval = 'Small', group = 'Styles')
labelSize = sizeOption == 'Huge' ? size.huge : sizeOption == 'Large' ? size.large : sizeOption == 'Small' ? size.small : sizeOption == 'Tiny' ? size.tiny : sizeOption == 'Auto' ? size.auto : size.normal
// Initialize variables
ema = ta.ema(close, emaLength)
var float buyVolume = na
var float sellVolume = na
buyVolume := high[1] == low[1] ? 0 : volume[1] * (close[1] - low[1]) / (high[1] - low[1])
sellVolume := high[1] == low[1] ? 0 : volume[1] * (high[1] - close[1]) / (high[1] - low[1])
var box b = na
var boxarray = array.new<box>()
var boolarray = array.new<bool>()
var createdBarArray = array.new<int>()
var idArray = array.new<int>() // To track unique box identifiers
var int currentId = 0 // Unique ID counter
// Create Fair Value Gaps and push to array
if useVolume
if ob and buyVolume > sellVolume and high[2] < low and low - high[2] > minGapSize and close[1] > ema
if onlyShowLast
if not na(b)
box.delete(b)
b := box.new(bar_index, high[2], bar_index + ol, low, border_color = color.new(color.black, 100), bgcolor = buyFVGColor, text = buyFVGText + ' #' + str.tostring(currentId), text_color = textColor, text_size = labelSize, text_halign = text.align_right)
array.push(boxarray, b)
array.push(boolarray, true)
array.push(createdBarArray, bar_index)
array.push(idArray, currentId)
//label.new(bar_index, high, "Created Box ID: " + str.tostring(currentId), color=color.green) // Debug: Log box creation
currentId := currentId + 1
currentId
else if ob and buyVolume < sellVolume and low[2] > high and low[2] - high > minGapSize and close[1] < ema
if onlyShowLast
if not na(b)
box.delete(b)
b := box.new(bar_index, low[2], bar_index + ol, high, border_color = color.new(color.black, 100), bgcolor = sellFVGColor, text = sellFVGText + ' #' + str.tostring(currentId), text_color = textColor, text_size = labelSize, text_halign = text.align_right)
array.push(boxarray, b)
array.push(boolarray, false)
array.push(createdBarArray, bar_index)
array.push(idArray, currentId)
//label.new(bar_index, high, "Created Box ID: " + str.tostring(currentId), color=color.green) // Debug: Log box creation
currentId := currentId + 1
currentId
else
if high[2] < low and low - high[2] > minGapSize and close[1] > ema
if onlyShowLast
if not na(b)
box.delete(b)
b := box.new(bar_index, high[2], bar_index + ol, low, border_color = color.new(color.black, 100), bgcolor = buyFVGColor, text = buyFVGText + ' #' + str.tostring(currentId), text_color = textColor, text_size = labelSize, text_halign = text.align_right)
array.push(boxarray, b)
array.push(boolarray, true)
array.push(createdBarArray, bar_index)
array.push(idArray, currentId)
//label.new(bar_index, high, "Created Box ID: " + str.tostring(currentId), color=color.green) // Debug: Log box creation
currentId := currentId + 1
currentId
else if low[2] > high and low[2] - high > minGapSize and close[1] < ema
if onlyShowLast
if not na(b)
box.delete(b)
b := box.new(bar_index, low[2], bar_index + ol, high, border_color = color.new(color.black, 100), bgcolor = sellFVGColor, text = sellFVGText + ' #' + str.tostring(currentId), text_color = textColor, text_size = labelSize, text_halign = text.align_right)
array.push(boxarray, b)
array.push(boolarray, false)
array.push(createdBarArray, bar_index)
array.push(idArray, currentId)
//label.new(bar_index, high, "Created Box ID: " + str.tostring(currentId), color=color.green) // Debug: Log box creation
currentId := currentId + 1
currentId
// Iterate over boxarray to check if any boxes have been filled
if deleteFilledGaps and array.size(boxarray) > 0
i = array.size(boxarray) - 1
while i >= 0
box currentBox = array.get(boxarray, i)
bool isBuy = array.get(boolarray, i)
// Use left and right boundaries of the box to determine if within timeframe
int boxLeft = box.get_left(currentBox)
int boxRight = box.get_right(currentBox)
bool withinTimeframe = bar_index <= boxRight
if withinTimeframe
float bottom = box.get_top(currentBox)
float top = box.get_bottom(currentBox)
// Debugging: Log every deletion attempt
//label.new(bar_index, high, "Checking Deletion for Box ID: " + str.tostring(array.get(idArray, i)) + " Close: " + str.tostring(close) + " Low: " + str.tostring(low) + " Bottom: " + str.tostring(bottom) + " Top: " + str.tostring(top), color=color.yellow)
// Buyers gap is filled if the close price or low price falls below the bottom of the box
if isBuy
if (close < bottom or low < bottom) and bar_index > boxLeft + 1 and bar_index <= boxRight
box.delete(currentBox)
array.remove(boxarray, i)
array.remove(boolarray, i)
array.remove(createdBarArray, i)
array.remove(idArray, i)
// Sellers gap is filled if the close price or high price rises above the top of the box
else
if (close > top or high > top) and bar_index > boxLeft + 1 and bar_index <= boxRight
box.delete(currentBox)
array.remove(boxarray, i)
array.remove(boolarray, i)
array.remove(createdBarArray, i)
array.remove(idArray, i)
i := i - 1 // Decrement the loop counter
i