OPEN-SOURCE SCRIPT
ICT Fair Value Gap Detector [Eˣ]

//@version=6
indicator(title='Fair Value Gap Detector', shorttitle='FVG', overlay=true, max_boxes_count=500)
// ========== INPUTS ==========
showBullishFVG = input.bool(true, 'Show Bullish FVG', group='Display', inline='bull')
bullishColor = input.color(color.new(color.green, 80), '', group='Display', inline='bull')
showBearishFVG = input.bool(true, 'Show Bearish FVG', group='Display', inline='bear')
bearishColor = input.color(color.new(color.red, 80), '', group='Display', inline='bear')
maxGaps = input.int(20, 'Max FVG to Display', minval=5, maxval=50, tooltip='Limit number of visible gaps', group='Display')
showLabels = input.bool(true, 'Show FVG Labels', group='Display')
extendGaps = input.int(50, 'Extend Gaps (bars)', minval=10, maxval=200, tooltip='How far to extend gaps to the right', group='Display')
minGapSize = input.float(0.05, 'Min Gap Size %', minval=0.01, maxval=2.0, step=0.01, tooltip='Minimum gap size as % of price', group='Filters')
showFilled = input.bool(false, 'Show Filled Gaps', tooltip='Keep showing gaps after price fills them', group='Filters')
autoMitigation = input.bool(true, 'Auto-Detect Mitigation', tooltip='Automatically detect when gaps are filled', group='Advanced')
mitigationType = input.string('Full', 'Mitigation Type', ['Full', 'Partial', '50%'], tooltip='How much fill required to consider gap mitigated', group='Advanced')
highlightActive = input.bool(true, 'Highlight Nearest Gap', tooltip='Show which gap price is approaching', group='Advanced')
// ========== FVG DETECTION ==========
// Bullish FVG: Gap between candle 3 low and candle 1 high (when candle 2 is strong bullish)
// Occurs when: high[2] < low (there's a gap that wasn't filled)
f_detectBullishFVG() =>
bool isFVG = false
float fvgTop = na
float fvgBottom = na
int fvgBar = na
// Check for bullish FVG: current candle's low is above the high from 2 candles ago
if low > high[2]
// Verify middle candle was bullish and strong
if close[1] > open[1]
fvgBottom := high[2]
fvgTop := low
fvgBar := bar_index[2]
isFVG := true
[isFVG, fvgTop, fvgBottom, fvgBar]
// Bearish FVG: Gap between candle 3 high and candle 1 low (when candle 2 is strong bearish)
// Occurs when: low[2] > high (there's a gap that wasn't filled)
f_detectBearishFVG() =>
bool isFVG = false
float fvgTop = na
float fvgBottom = na
int fvgBar = na
// Check for bearish FVG: current candle's high is below the low from 2 candles ago
if high < low[2]
// Verify middle candle was bearish and strong
if close[1] < open[1]
fvgTop := low[2]
fvgBottom := high
fvgBar := bar_index[2]
isFVG := true
[isFVG, fvgTop, fvgBottom, fvgBar]
// Detect FVGs
[bullFVG, bullFVGTop, bullFVGBottom, bullFVGBar] = f_detectBullishFVG()
[bearFVG, bearFVGTop, bearFVGBottom, bearFVGBar] = f_detectBearishFVG()
// ========== FVG STORAGE ==========
var array<float> bullishFVGTops = array.new<float>()
var array<float> bullishFVGBottoms = array.new<float>()
var array<int> bullishFVGBars = array.new<int>()
var array<bool> bullishFVGFilled = array.new<bool>()
var array<float> bullishFVGFillPercent = array.new<float>()
var array<float> bearishFVGTops = array.new<float>()
var array<float> bearishFVGBottoms = array.new<float>()
var array<int> bearishFVGBars = array.new<int>()
var array<bool> bearishFVGFilled = array.new<bool>()
var array<float> bearishFVGFillPercent = array.new<float>()
// Add new bullish FVG
if bullFVG and not na(bullFVGTop) and not na(bullFVGBottom)
float gapSize = ((bullFVGTop - bullFVGBottom) / bullFVGBottom) * 100
// Check minimum size
if gapSize >= minGapSize
array.unshift(bullishFVGTops, bullFVGTop)
array.unshift(bullishFVGBottoms, bullFVGBottom)
array.unshift(bullishFVGBars, bullFVGBar)
array.unshift(bullishFVGFilled, false)
array.unshift(bullishFVGFillPercent, 0.0)
// Limit array size
if array.size(bullishFVGTops) > maxGaps
array.pop(bullishFVGTops)
array.pop(bullishFVGBottoms)
array.pop(bullishFVGBars)
array.pop(bullishFVGFilled)
array.pop(bullishFVGFillPercent)
// Add new bearish FVG
if bearFVG and not na(bearFVGTop) and not na(bearFVGBottom)
float gapSize = ((bearFVGTop - bearFVGBottom) / bearFVGBottom) * 100
if gapSize >= minGapSize
array.unshift(bearishFVGTops, bearFVGTop)
array.unshift(bearishFVGBottoms, bearFVGBottom)
array.unshift(bearishFVGBars, bearFVGBar)
array.unshift(bearishFVGFilled, false)
array.unshift(bearishFVGFillPercent, 0.0)
if array.size(bearishFVGTops) > maxGaps
array.pop(bearishFVGTops)
array.pop(bearishFVGBottoms)
array.pop(bearishFVGBars)
array.pop(bearishFVGFilled)
array.pop(bearishFVGFillPercent)
// ========== MITIGATION DETECTION ==========
if autoMitigation
// Check bullish FVGs (filled when price comes back down)
int bullSize = array.size(bullishFVGTops)
if bullSize > 0
for i = 0 to bullSize - 1
if not array.get(bullishFVGFilled, i)
float fvgTop = array.get(bullishFVGTops, i)
float fvgBottom = array.get(bullishFVGBottoms, i)
float gapSize = fvgTop - fvgBottom
// Calculate how much of the gap has been filled
float fillAmount = 0.0
if low <= fvgTop and low >= fvgBottom
fillAmount := (fvgTop - low) / gapSize
else if low < fvgBottom
fillAmount := 1.0
array.set(bullishFVGFillPercent, i, fillAmount)
// Check mitigation based on type
bool isMitigated = false
if mitigationType == 'Full'
isMitigated := low <= fvgBottom
else if mitigationType == '50%'
isMitigated := fillAmount >= 0.5
else // Partial
isMitigated := low <= fvgTop
if isMitigated
array.set(bullishFVGFilled, i, true)
// Check bearish FVGs (filled when price comes back up)
int bearSize = array.size(bearishFVGTops)
if bearSize > 0
for i = 0 to bearSize - 1
if not array.get(bearishFVGFilled, i)
float fvgTop = array.get(bearishFVGTops, i)
float fvgBottom = array.get(bearishFVGBottoms, i)
float gapSize = fvgTop - fvgBottom
// Calculate how much of the gap has been filled
float fillAmount = 0.0
if high >= fvgBottom and high <= fvgTop
fillAmount := (high - fvgBottom) / gapSize
else if high > fvgTop
fillAmount := 1.0
array.set(bearishFVGFillPercent, i, fillAmount)
// Check mitigation based on type
bool isMitigated = false
if mitigationType == 'Full'
isMitigated := high >= fvgTop
else if mitigationType == '50%'
isMitigated := fillAmount >= 0.5
else // Partial
isMitigated := high >= fvgBottom
if isMitigated
array.set(bearishFVGFilled, i, true)
// ========== FIND NEAREST GAPS ==========
float nearestBullDist = 999999
int nearestBullIdx = -1
float nearestBearDist = 999999
int nearestBearIdx = -1
if highlightActive
int bullSize = array.size(bullishFVGTops)
if bullSize > 0
for i = 0 to bullSize - 1
if not array.get(bullishFVGFilled, i)
float fvgMid = (array.get(bullishFVGTops, i) + array.get(bullishFVGBottoms, i)) / 2
float dist = math.abs(close - fvgMid)
if dist < nearestBullDist and close > fvgMid
nearestBullDist := dist
nearestBullIdx := i
int bearSize = array.size(bearishFVGTops)
if bearSize > 0
for i = 0 to bearSize - 1
if not array.get(bearishFVGFilled, i)
float fvgMid = (array.get(bearishFVGTops, i) + array.get(bearishFVGBottoms, i)) / 2
float dist = math.abs(close - fvgMid)
if dist < nearestBearDist and close < fvgMid
nearestBearDist := dist
nearestBearIdx := i
// ========== VISUALIZATION ==========
var array<box> bullishBoxes = array.new<box>()
var array<label> bullishLabels = array.new<label>()
var array<box> bearishBoxes = array.new<box>()
var array<label> bearishLabels = array.new<label>()
// Clear old drawings
if barstate.islast
if array.size(bullishBoxes) > 0
for i = 0 to array.size(bullishBoxes) - 1
box.delete(array.get(bullishBoxes, i))
array.clear(bullishBoxes)
if array.size(bullishLabels) > 0
for i = 0 to array.size(bullishLabels) - 1
label.delete(array.get(bullishLabels, i))
array.clear(bullishLabels)
if array.size(bearishBoxes) > 0
for i = 0 to array.size(bearishBoxes) - 1
box.delete(array.get(bearishBoxes, i))
array.clear(bearishBoxes)
if array.size(bearishLabels) > 0
for i = 0 to array.size(bearishLabels) - 1
label.delete(array.get(bearishLabels, i))
array.clear(bearishLabels)
// Draw bullish FVGs
if barstate.islast and showBullishFVG
int bullSize = array.size(bullishFVGTops)
if bullSize > 0
for i = 0 to bullSize - 1
bool isFilled = array.get(bullishFVGFilled, i)
if not isFilled or showFilled
float fvgTop = array.get(bullishFVGTops, i)
float fvgBottom = array.get(bullishFVGBottoms, i)
int fvgBar = array.get(bullishFVGBars, i)
float fillPct = array.get(bullishFVGFillPercent, i)
bool isActive = highlightActive and i == nearestBullIdx and not isFilled
color boxColor = isFilled ? color.new(color.gray, 90) : isActive ? color.new(color.lime, 70) : bullishColor
int borderWidth = isActive ? 2 : 1
box b = box.new(fvgBar, fvgTop, bar_index + extendGaps, fvgBottom,
border_color=boxColor,
bgcolor=boxColor,
border_width=borderWidth,
border_style=isFilled ? line.style_dotted : line.style_solid)
array.push(bullishBoxes, b)
// Label
if showLabels and not isFilled
string labelText = isActive ? 'FVG+ 🎯' : 'FVG+'
if fillPct > 0 and fillPct < 1.0
labelText += ' ' + str.tostring(fillPct * 100, '#') + '%'
label lbl = label.new(bar_index + 2, fvgTop, labelText,
color=color.new(color.green, isActive ? 70 : 85),
textcolor=color.white,
style=label.style_label_down,
size=isActive ? size.normal : size.small)
array.push(bullishLabels, lbl)
// Draw bearish FVGs
if barstate.islast and showBearishFVG
int bearSize = array.size(bearishFVGTops)
if bearSize > 0
for i = 0 to bearSize - 1
bool isFilled = array.get(bearishFVGFilled, i)
if not isFilled or showFilled
float fvgTop = array.get(bearishFVGTops, i)
float fvgBottom = array.get(bearishFVGBottoms, i)
int fvgBar = array.get(bearishFVGBars, i)
float fillPct = array.get(bearishFVGFillPercent, i)
bool isActive = highlightActive and i == nearestBearIdx and not isFilled
color boxColor = isFilled ? color.new(color.gray, 90) : isActive ? color.new(color.orange, 70) : bearishColor
int borderWidth = isActive ? 2 : 1
box b = box.new(fvgBar, fvgTop, bar_index + extendGaps, fvgBottom,
border_color=boxColor,
bgcolor=boxColor,
border_width=borderWidth,
border_style=isFilled ? line.style_dotted : line.style_solid)
array.push(bearishBoxes, b)
// Label
if showLabels and not isFilled
string labelText = isActive ? 'FVG- 🎯' : 'FVG-'
if fillPct > 0 and fillPct < 1.0
labelText += ' ' + str.tostring(fillPct * 100, '#') + '%'
label lbl = label.new(bar_index + 2, fvgBottom, labelText,
color=color.new(color.red, isActive ? 70 : 85),
textcolor=color.white,
style=label.style_label_up,
size=isActive ? size.normal : size.small)
array.push(bearishLabels, lbl)
// ========== INFO TABLE ==========
var table infoTable = table.new(position.top_right, 2, 5, border_width=1, bgcolor=color.new(color.black, 85), border_color=color.gray)
if barstate.islast
// Header
table.cell(infoTable, 0, 0, '⚡ Fair Value Gaps', bgcolor=color.new(color.blue, 70), text_color=color.white, text_size=size.normal)
table.merge_cells(infoTable, 0, 0, 1, 0)
// Count unfilled bullish FVGs
int activeBullish = 0
int bullSize = array.size(bullishFVGTops)
if bullSize > 0
for i = 0 to bullSize - 1
if not array.get(bullishFVGFilled, i)
activeBullish += 1
table.cell(infoTable, 0, 1, 'Bullish FVG:', text_color=color.white, text_size=size.small)
table.cell(infoTable, 1, 1, str.tostring(activeBullish), bgcolor=color.new(color.green, 70), text_color=color.white, text_size=size.small)
// Count unfilled bearish FVGs
int activeBearish = 0
int bearSize = array.size(bearishFVGTops)
if bearSize > 0
for i = 0 to bearSize - 1
if not array.get(bearishFVGFilled, i)
activeBearish += 1
table.cell(infoTable, 0, 2, 'Bearish FVG:', text_color=color.white, text_size=size.small)
table.cell(infoTable, 1, 2, str.tostring(activeBearish), bgcolor=color.new(color.red, 70), text_color=color.white, text_size=size.small)
// Bias
string bias = activeBullish > activeBearish ? '⬆ Bullish' : activeBearish > activeBullish ? '⬇ Bearish' : '↔ Neutral'
color biasColor = activeBullish > activeBearish ? color.green : activeBearish > activeBullish ? color.red : color.gray
table.cell(infoTable, 0, 3, 'Bias:', text_color=color.white, text_size=size.small)
table.cell(infoTable, 1, 3, bias, text_color=biasColor, text_size=size.small)
// Nearest gap
if nearestBullIdx >= 0 and nearestBullDist < nearestBearDist
float distPct = (nearestBullDist / close) * 100
table.cell(infoTable, 0, 4, 'Target:', text_color=color.white, text_size=size.tiny)
table.cell(infoTable, 1, 4, 'Bull FVG -' + str.tostring(distPct, '#.##') + '%', text_color=color.lime, text_size=size.tiny)
else if nearestBearIdx >= 0
float distPct = (nearestBearDist / close) * 100
table.cell(infoTable, 0, 4, 'Target:', text_color=color.white, text_size=size.tiny)
table.cell(infoTable, 1, 4, 'Bear FVG +' + str.tostring(distPct, '#.##') + '%', text_color=color.orange, text_size=size.tiny)
else
table.cell(infoTable, 0, 4, 'Status:', text_color=color.white, text_size=size.tiny)
table.cell(infoTable, 1, 4, 'No active gaps', text_color=color.gray, text_size=size.tiny)
// ========== SIGNALS ==========
// Price entering bullish FVG
bool enteringBullFVG = false
if nearestBullIdx >= 0 and bullSize > 0
float fvgTop = array.get(bullishFVGTops, nearestBullIdx)
float fvgBottom = array.get(bullishFVGBottoms, nearestBullIdx)
bool isFilled = array.get(bullishFVGFilled, nearestBullIdx)
enteringBullFVG := not isFilled and low <= fvgTop and low[1] > fvgTop
// Price entering bearish FVG
bool enteringBearFVG = false
if nearestBearIdx >= 0 and bearSize > 0
float fvgTop = array.get(bearishFVGTops, nearestBearIdx)
float fvgBottom = array.get(bearishFVGBottoms, nearestBearIdx)
bool isFilled = array.get(bearishFVGFilled, nearestBearIdx)
enteringBearFVG := not isFilled and high >= fvgBottom and high[1] < fvgBottom
// Plot signals
plotshape(enteringBullFVG, 'Bullish FVG Fill', shape.circle, location.belowbar, color.new(color.lime, 0), size=size.small)
plotshape(enteringBearFVG, 'Bearish FVG Fill', shape.circle, location.abovebar, color.new(color.orange, 0), size=size.small)
// New FVG signals
plotshape(bullFVG, 'New Bullish FVG', shape.triangleup, location.belowbar, color.new(color.green, 30), size=size.tiny)
plotshape(bearFVG, 'New Bearish FVG', shape.triangledown, location.abovebar, color.new(color.red, 30), size=size.tiny)
// ========== ALERTS ==========
alertcondition(enteringBullFVG, 'Price Entering Bullish FVG', '🟢 Price entering Bullish Fair Value Gap on {{ticker}} at {{close}}')
alertcondition(enteringBearFVG, 'Price Entering Bearish FVG', '🔴 Price entering Bearish Fair Value Gap on {{ticker}} at {{close}}')
alertcondition(bullFVG, 'New Bullish FVG Detected', '⚡ New Bullish FVG detected on {{ticker}}')
alertcondition(bearFVG, 'New Bearish FVG Detected', '⚡ New Bearish FVG detected on {{ticker}}')
สคริปต์โอเพนซอร์ซ
ด้วยเจตนารมณ์หลักของ TradingView ผู้สร้างสคริปต์นี้ได้ทำให้เป็นโอเพนซอร์ส เพื่อให้เทรดเดอร์สามารถตรวจสอบและยืนยันฟังก์ชันการทำงานของมันได้ ขอชื่นชมผู้เขียน! แม้ว่าคุณจะใช้งานได้ฟรี แต่โปรดจำไว้ว่าการเผยแพร่โค้ดซ้ำจะต้องเป็นไปตาม กฎระเบียบการใช้งาน ของเรา
Market Solver Pro: The automated system for disciplined traders. Includes: Trend Validation, Entries, and Risk Management. 📉 Stop Guessing. 📈 Start Executing.
Get the Signals & Free Guides:
➡️ patreon.com/marketsolverpro
Get the Signals & Free Guides:
➡️ patreon.com/marketsolverpro
คำจำกัดสิทธิ์ความรับผิดชอบ
ข้อมูลและบทความไม่ได้มีวัตถุประสงค์เพื่อก่อให้เกิดกิจกรรมทางการเงิน, การลงทุน, การซื้อขาย, ข้อเสนอแนะ หรือคำแนะนำประเภทอื่น ๆ ที่ให้หรือรับรองโดย TradingView อ่านเพิ่มเติมใน ข้อกำหนดการใช้งาน
สคริปต์โอเพนซอร์ซ
ด้วยเจตนารมณ์หลักของ TradingView ผู้สร้างสคริปต์นี้ได้ทำให้เป็นโอเพนซอร์ส เพื่อให้เทรดเดอร์สามารถตรวจสอบและยืนยันฟังก์ชันการทำงานของมันได้ ขอชื่นชมผู้เขียน! แม้ว่าคุณจะใช้งานได้ฟรี แต่โปรดจำไว้ว่าการเผยแพร่โค้ดซ้ำจะต้องเป็นไปตาม กฎระเบียบการใช้งาน ของเรา
Market Solver Pro: The automated system for disciplined traders. Includes: Trend Validation, Entries, and Risk Management. 📉 Stop Guessing. 📈 Start Executing.
Get the Signals & Free Guides:
➡️ patreon.com/marketsolverpro
Get the Signals & Free Guides:
➡️ patreon.com/marketsolverpro
คำจำกัดสิทธิ์ความรับผิดชอบ
ข้อมูลและบทความไม่ได้มีวัตถุประสงค์เพื่อก่อให้เกิดกิจกรรมทางการเงิน, การลงทุน, การซื้อขาย, ข้อเสนอแนะ หรือคำแนะนำประเภทอื่น ๆ ที่ให้หรือรับรองโดย TradingView อ่านเพิ่มเติมใน ข้อกำหนดการใช้งาน