OPEN-SOURCE SCRIPT

ICT Fair Value Gap Detector [Eˣ]

102
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}}')

כתב ויתור

המידע והפרסומים אינם מיועדים להיות, ואינם מהווים, ייעוץ או המלצה פיננסית, השקעתית, מסחרית או מכל סוג אחר המסופקת או מאושרת על ידי TradingView. קרא עוד ב־תנאי השימוש.