OPEN-SOURCE SCRIPT

ICT Fair Value Gap Detector [Eˣ]

99
Pine Script®
//@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}}')

Pernyataan Penyangkalan

Informasi dan publikasi ini tidak dimaksudkan, dan bukan merupakan, saran atau rekomendasi keuangan, investasi, trading, atau jenis lainnya yang diberikan atau didukung oleh TradingView. Baca selengkapnya di Ketentuan Penggunaan.