Fair Value Gaps by DGT

dgtrd · study · 415 行 · 点赞 5,598 · TradingView 原页

本页源码来自 TradingView 公开发布的开源脚本,版权归原作者所有, 请遵循其原始许可(Pine 脚本常见 CC BY-NC-SA / MPL-2.0 / MIT)。 本项目仅用于研究检索与许可范围内的移植。

Pine Script

//@version=6
// ══════════════════════════════════════════════════════════════════════════════════════════════════ //
//# * ══════════════════════════════════════════════════════════════════════════════════════════════
//# *
//# * Study       : Fair Value Gaps
//# * Author      : © dgtrd
//# *
//# * Revision History
//# *  Release    : Oct 13, 2025
//# *
//# * ══════════════════════════════════════════════════════════════════════════════════════════════
// ══════════════════════════════════════════════════════════════════════════════════════════════════ //
 
indicator('Fair Value Gaps by DGT', 'FVGs · ☼☾', true, max_boxes_count = 500, max_lines_count = 500, max_labels_count = 500)
 
//---------------------------------------------------------------------------------------------------------------------//
// Settings
//---------------------------------------------------------------------------------------------------------------------//
 
display = display.all - display.status_line
 
fvgGR = 'Fair Value Gaps #1, Chart/HTF'
 
fvgTip1 = 'Select the timeframe from which Fair Value Gaps (FVGs) will be detected.\n' +
         '• Chart — uses the current chart timeframe.\n' +
         '• Higher timeframes — plots FVGs derived from the selected timeframe above the chart.\n\n' +
         'If a lower timeframe than the chart is selected, the script automatically defaults to the chart timeframe.'
 
fvgFillTip1 = 'Defines how a Fair Value Gap (FVG) is considered filled:\n' +
             '• Any Touch — when price (wick or body) touches any part of the imbalance zone.\n' +
             '• Midpoint Reached — when price trades to at least the midpoint (50%) of the zone.\n' +
             '• Wick Sweep — when a wick fully passes through the entire FVG and returns inside the zone.\n' +
             '• Body Beyond — when the candle body closes beyond the opposite edge of the FVG, confirming full repair.'
 
fvgLBTip1 = 'Controls the visibility of Fair Value Gap (FVG) labels.\n\n' +
             '• **Show** — displays labels directly on the chart. Each label indicates the timeframe and fill progress of the zone.\n' +
             '• **Hide** — hides on-chart labels, but tooltips remain accessible by hovering over the midpoint of each FVG zone.'
 
fvgSH1 = input.bool(true, 'Fair Value Gaps #1, Chart/HTF', inline = 'fvg1', group = fvgGR)
fvgTF1 = input.string('Chart', '', options = ['Chart', '5 Minutes', '15 Minutes', '1 Hour', '4 Hours', '1 Day', '1 Week', '1 Month'], inline = 'fvg1', group = fvgGR, tooltip = fvgTip1, display = display)
fvgFill1 = input.string('Wick Sweep', '  FVG #1 Fill Method', options = ['Any Touch', 'Midpoint Reached', 'Wick Sweep', 'Body Beyond'], display = display, tooltip = fvgFillTip1, group = fvgGR)
fvgCb1 = input.color(#00B8D9, '  FVG #1 Zones    ', inline = 'fvg', group = fvgGR)
fvgCs1 = input.color(#FF8B00, '', inline = 'fvg', group = fvgGR)
fvgLB1 = input.string('Show', '  FVG #1 Labels', options = ['Show', 'Hide'], tooltip = fvgLBTip1, display = display, group = fvgGR)
maxBars = input.int(360, '  Max Bars Back', minval = 1, step = 20, group = fvgGR, display = display, tooltip = 'This determines how far back the script will analyze for Fair Value Gaps.')
 
fvgGR2 = 'Fair Value Gaps #, Chart/HTF'
 
fvgSH2 = input.bool(false, 'Fair Value Gaps #2, Chart/HTF', inline = 'fvg2', group = fvgGR2)
fvgTF2 = input.string('1 Hour', '', options = ['Chart', '5 Minutes', '15 Minutes', '1 Hour', '4 Hours', '1 Day', '1 Week', '1 Month'], inline = 'fvg2', group = fvgGR2, tooltip = fvgTip1, display = display)
fvgFill2 = input.string('Wick Sweep', '  FVG #2 Fill Method', options = ['Any Touch', 'Midpoint Reached', 'Wick Sweep', 'Body Beyond'], display = display, tooltip = fvgFillTip1, group = fvgGR2)
fvgCb2 = input.color(#6EE7B7, '  FVG #2 Zones    ', inline = 'fvg', group = fvgGR2)
fvgCs2 = input.color(#FF6B6B, '', inline = 'fvg', group = fvgGR2)
fvgLB2 = input.string('Hide', '  FVG #2 Labels', options = ['Show', 'Hide'], tooltip = fvgLBTip1, display = display, group = fvgGR2)
maxBars2 = input.int(360, '  Max Bars Back', minval = 1, step = 20, group = fvgGR2, display = display, tooltip = 'This determines how far back the script will analyze for Fair Value Gaps.')
 
fvgGR3 = 'Fair Value Gaps #3, LTF'
fvgTip3 = 'Intrabar timeframe (lower timeframe, LTF) setting option. ' +
         'When set to "Auto", the lower timeframe is determined by the following algorithm:\n' +
         '  Chart Timeframe → Lower Timeframe\n' +
         '\t\t<= 3 min\t\t→ 15 sec\n' +
         '\t\t<= 30 min\t\t→ 1 min\n' +
         '\t\t<= 2 hour\t\t→ 5 min\n' +
         '\t\t<= 4 hour\t\t→ 15 min\n' +
         '\t\t<= 1 day\t\t→ 1 hour\n' +
         '\t\t<= 1 week\t\t→ 4 hour\n' +
         '\t\t<= 1 month\t→ 1 day\n' +
         '\t\t>  1 month\t→ 1 week'
 
fvgSH3 = input.bool(false, 'Fair Value Gaps #3, LTF', inline = 'fvg3', group = fvgGR3)
 
fvgTF3 = input.timeframe('Auto', '', options = ['Auto', '1S', '5S', '10S', '15S', '30S', '1m', '3m', '5m', '15m', '30m', '45m', '1H', '2H', '3H', '4H', 'D'], inline = 'fvg3', group = fvgGR3, tooltip = fvgTip3)
fvgThTip = 'Checks the significance of the detected fair value gap compared to a fixed-length average true range. ' + 'Setting the value to 0 means that no filtering will be applied, whereas values greater than zero determine the filtering threshold value'
fvgTH3 = input.float(.7, '  FVG Filter Threshold', minval = 0, step = .1, group = fvgGR3, tooltip = fvgThTip)
 
fvgCb3 = input.color(#8BC34A, '  FVG #3 Zones    ', inline = 'VD', group = fvgGR3)
fvgCs3 = input.color(#E91E63, '', inline = 'VD', group = fvgGR3)
 
fvgLBTip3 = 'Controls the visibility of lower-timeframe Fair Value Gap (FVG) labels.\n\n' +
               '• **Show** — displays labels for each detected LTF FVG, indicating the source lower timeframe of the zone.\n' +
               '• **Hide** — hides the on-chart labels, while tooltips remain available when hovering over the midpoint of each LTF FVG zone.'
fvgLB3 = input.string('Hide', '  FVG #3 Labels', options = ['Show', 'Hide'], tooltip = fvgLBTip3, display = display, group = fvgGR3)
 
//---------------------------------------------------------------------------------------------------------------------//
// User Defined Types
//---------------------------------------------------------------------------------------------------------------------//
 
type BAR
	float open  = open
	float high  = high
	float low   = low
	float close = close
	int   index = bar_index
 
	float ltfHigh
	float ltfHigh1
	float ltfHigh2
	float ltfLow
	float ltfLow1
	float ltfLow2
	float ltfClose
	float ltfClose1
 
type HTFBar
	float price = na
	int   index = na
 
type HTFBarOHLC
	HTFBar open
	HTFBar high
	HTFBar low
	HTFBar close
 
type FVG
    label  lbTxt
	line   lnTop
	line   lnAvg
	line   lnBtm
	float  mitPrice
    float  mit
    string txt
    
BAR bar = BAR.new()
 
//---------------------------------------------------------------------------------------------------------------------//
// Functions / Methods
//---------------------------------------------------------------------------------------------------------------------//
 
ltfTF(_t) =>
    str.contains(_t, 'S') or str.contains(_t, 'D') or str.contains(_t, 'W') or str.contains(_t, 'M') ? _t : 
     str.contains(_t, 'm') ? str.replace(_t, 'm', '', 0) : str.contains(_t, 'H') ? str.tostring(str.tonumber(str.replace(_t, 'H', '', 0)) * 60) : '12M'
 
 
ltfTF() =>
    int tfInMs = timeframe.in_seconds(timeframe.period)
    int mInMS = 60
    switch 
        tfInMs <= 1 * mInMS => '5S'
        tfInMs <= 3 * mInMS => '15S'
        tfInMs <= 30 * mInMS => '1'
        tfInMs <= 120 * mInMS => '5'
        tfInMs <= 240 * mInMS => '15'
        tfInMs <= 1440 * mInMS => '60'
        tfInMs <= 7 * 1440 * mInMS => '240'
        tfInMs <= 31 * 1440 * mInMS => 'D'
        => 'W'
 
htfTF(usrTF) =>
    float chartTFin = timeframe.in_seconds() / 60
 
    switch 
        usrTF == '5 Minutes' and chartTFin <= 5 => '5'
        usrTF == '15 Minutes' and chartTFin <= 15 => '15'
        usrTF == '1 Hour' and chartTFin <= 60 => '60'
        usrTF == '4 Hours' and chartTFin <= 240 => '240'
        usrTF == '1 Day' and timeframe.isintraday => 'D'
        usrTF == '1 Week' and (timeframe.isdaily or timeframe.isintraday) => 'W'
        usrTF == '1 Month' and (timeframe.isweekly or timeframe.isdaily or timeframe.isintraday) => 'M'
        => timeframe.period
 
updateFvg(fvgArray, fill, side, fvgLb) =>
 
    var string fiiled = ''
    if fvgArray.size() > 0
        for i = array.size(fvgArray) - 1 to 0
            this = array.get(fvgArray, i)
 
            if side == "bull" ? (fill == 'Body Beyond' ? bar.close : bar.low) <= this.mitPrice : (fill == 'Body Beyond' ? bar.close : bar.high) > this.mitPrice
                fvgArray.remove(i)
 
                this.lbTxt.delete()
                this.lnTop.delete()
                this.lnAvg.delete()
                this.lnBtm.delete()
            else
                this.lbTxt.set_x(bar.index + 1)
                this.lnTop.set_x2(bar.index + 1)
                this.lnAvg.set_x2(bar.index + 1)
                this.lnBtm.set_x2(bar.index + 1)
 
                fillRatio = switch side
                    "bull" => (this.lnTop.get_y1() - bar.low)  / (this.lnTop.get_y1() - this.lnBtm.get_y1())
                    "bear" => (bar.high - this.lnBtm.get_y1()) / (this.lnTop.get_y1() - this.lnBtm.get_y1())
                    => 0.0
                
                if fillRatio > this.mit
                    this.mit := fillRatio
                    this.lbTxt.set_text( fvgLb == 'Show' ? this.txt + str.format("{0,number,(#.#%)}", fillRatio) : '') 
 
renderFvg(show, tfIn, fill, cBull, cBear, fvgLb) =>
 
    if show
        var array<FVG> bullFvgArray = array.new<FVG>()
        var array<FVG> bearFvgArray = array.new<FVG>()
        var array<HTFBarOHLC> barArray = array.new<HTFBarOHLC>()
 
        var HTFBar barOpen = HTFBar.new(bar.open, bar.index)
        var HTFBar barHigh = HTFBar.new(bar.high, bar.index)
        var HTFBar barLow = HTFBar.new(bar.low, bar.index)
        var HTFBar barClose = HTFBar.new(bar.close, bar.index)
 
        var barHist = HTFBarOHLC.new(barOpen, barHigh, barLow, barClose)
 
        var int bullBarIndexEnd = na
        var int bullBarIndexEnd1 = na
        var int bearBarIndexEnd = na
        var int bearBarIndexEnd1 = na
 
        var bool bullProcess = false
        var bool bullCondition = false
        var bool bearProcess = false
        var bool bearCondition = false
 
        var bool bullG = false
        var bool bearG = false
 
        string tf = htfTF(tfIn)
        fvgTf = tf == timeframe.period
 
        //log.info("yaz_kizim {0} {1}", htfATR, tf)
 
        if timeframe.change(tf)
            bullProcess := false
            bearProcess := false
 
            if barArray.size() > 2
                barArray.pop()
 
            barArray.unshift(barHist)
 
            barOpen  := HTFBar.new(bar.open, bar.index)
            barHigh  := HTFBar.new(bar.high, bar.index)
            barLow   := HTFBar.new(bar.low, bar.index)
            barClose := HTFBar.new(bar.close, bar.index)
 
            barHist  := HTFBarOHLC.new(barOpen, barHigh, barLow, barClose) 
 
            bullBarIndexEnd1 := bullBarIndexEnd
            bullBarIndexEnd  := bar.index
            bearBarIndexEnd1 := bearBarIndexEnd
            bearBarIndexEnd  := bar.index
            bearBarIndexEnd
 
        else
            if bar.high > barHigh.price
                barHigh.price := bar.high
                barHigh.index := bar.index
                barHigh.index
 
            if bar.low < barLow.price
                barLow.price := bar.low
                barLow.index := bar.index
                barLow.index
 
            barClose.price := bar.close
            barClose.index := bar.index
 
            if barArray.size() > 2
                if bar.low < barArray.get(1).high.price
                    bullBarIndexEnd := bar.index
                    bullBarIndexEnd
 
                if bar.high > barArray.get(1).low.price
                    bearBarIndexEnd := bar.index
                    bearBarIndexEnd
 
        if barArray.size() > 2
            bullG := barArray.get(0).low.price > barArray.get(1).high.price or barArray.get(1).low.price > barArray.get(2).high.price
            bullCondition := barArray.get(0).low.price > barArray.get(2).high.price and barArray.get(1).close.price > barArray.get(2).high.price and not bullG
 
            bearG := barArray.get(0).high.price < barArray.get(1).low.price or barArray.get(1).high.price < barArray.get(2).low.price
            bearCondition := barArray.get(0).high.price < barArray.get(2).low.price and barArray.get(1).close.price < barArray.get(2).low.price and not bearG
 
        if bullCondition and not bullProcess
            bullProcess := true
 
            bullFvgArray.unshift(
             FVG.new(
                 label.new(bar.index + 1, math.avg(barArray.get(0).low.price, barArray.get(2).high.price), fvgLb == 'Show' ? fvgTf ? 'Chart(' + tf + ') FVG' : 'HTF(' + tf + ') FVG' : '', xloc.bar_index, color = color(na), style = label.style_label_left, textcolor = cBull, size = size.small, 
                  tooltip = (fvgTf ? 'Chart(' + tf + ') Bullish FVG' : 'HTF(' + tf + ') Bullish FVG') + '\n\nZone Top · ' + str.format("{0, number, #,###.##}", barArray.get(0).low.price) + '\nZone Mid · ' + str.format("{0, number, #,###.##}", math.avg(barArray.get(0).low.price, barArray.get(2).high.price)) + '\nZone Btm · ' + str.format("{0, number, #,###.##}", barArray.get(2).high.price) ),
                 line.new(barArray.get(0).low.index, barArray.get(0).low.price, bar.index + 1, barArray.get(0).low.price, color = color.new(cBull, 65)),
                 line.new(math.ceil(math.avg(barArray.get(0).low.index, barArray.get(2).high.index)), math.avg(barArray.get(0).low.price, barArray.get(2).high.price), bar.index + 1, math.avg(barArray.get(0).low.price, barArray.get(2).high.price), style = line.style_dotted, color = color.new(cBull, 25)),
                 line.new(barArray.get(2).high.index, barArray.get(2).high.price, bar.index + 1, barArray.get(2).high.price, color = color.new(cBull, 50), width = 2), 
                 fill == 'Midpoint Reached' ? math.avg(barArray.get(2).low.price, barArray.get(0).high.price) : fill == 'Any Touch' ? barArray.get(0).low.price : barArray.get(2).high.price, 0, fvgTf ? 'Chart(' + tf + ') FVG' : 'HTF(' + tf + ') FVG')
             )
 
            first = bullFvgArray.first()
            linefill.new(first.lnTop, first.lnBtm, color.new(cBull, 89))
 
        if bearCondition and not bearProcess
            bearProcess := true
 
            bearFvgArray.unshift(
             FVG.new(
                 label.new(bar.index + 1, math.avg(barArray.get(2).low.price, barArray.get(0).high.price), fvgLb == 'Show' ? fvgTf ? 'Chart(' + tf + ') FVG' : 'HTF(' + tf + ') FVG' : '', xloc.bar_index, color = color(na), style = label.style_label_left, textcolor = cBear, size = size.small, 
                  tooltip = (fvgTf ? 'Chart(' + tf + ') Bearish FVG' : 'HTF(' + tf + ') Bearish FVG') + '\n\nZone Top · ' + str.format("{0, number, #,###.##}", barArray.get(2).low.price) + '\nZone Mid · ' + str.format("{0, number, #,###.##}", math.avg(barArray.get(2).low.price, barArray.get(0).high.price)) + '\nZone Btm · ' + str.format("{0, number, #,###.##}", barArray.get(0).high.price) ),
                 line.new(barArray.get(2).low.index, barArray.get(2).low.price, bar.index + 1, barArray.get(2).low.price, color = color.new(cBear, 50), width = 2), 
                 line.new(math.ceil(math.avg(barArray.get(2).low.index, barArray.get(0).high.index)), math.avg(barArray.get(2).low.price, barArray.get(0).high.price), bar.index + 1, math.avg(barArray.get(2).low.price, barArray.get(0).high.price), style = line.style_dotted, color = color.new(cBear, 25)), 
                 line.new(barArray.get(0).high.index, barArray.get(0).high.price, bar.index + 1, barArray.get(0).high.price, color = color.new(cBear, 65)), 
                 fill == 'Midpoint Reached' ? math.avg(barArray.get(2).low.price, barArray.get(0).high.price) : fill == 'Any Touch' ? barArray.get(0).high.price : barArray.get(2).low.price, 0, fvgTf ? 'Chart(' + tf + ') FVG' : 'HTF(' + tf + ') FVG')
             )
             
            first = bearFvgArray.first()
            linefill.new(first.lnTop, first.lnBtm, color.new(cBear, 89))
 
 
        updateFvg(bullFvgArray, fill, 'bull', fvgLb)
        updateFvg(bearFvgArray, fill, 'bear', fvgLb)
 
 
//---------------------------------------------------------------------------------------------------------------------//
// Calculations
//---------------------------------------------------------------------------------------------------------------------//
 
var fvgLTFbx = array.new_box()
var fvgLTFlb = array.new_label()
 
ltf = fvgTF3 == 'Auto' ? ltfTF() : ltfTF(fvgTF3)
[aO, aH, aL, aC, atr] = request.security_lower_tf(syminfo.tickerid, ltf, [bar.open, bar.high, bar.low, bar.close, ta.atr(13)])
 
if fvgSH3 and timeframe.change(timeframe.period) and array.size(aH) > 0 
 
    for i = 0 to aH.size() - 1 by 1
 
        bar.ltfClose1 := bar.ltfClose
        bar.ltfClose  := aC.get(i)
 
        bar.ltfHigh2  := bar.ltfHigh1
        bar.ltfHigh1  := bar.ltfHigh
        bar.ltfHigh   := aH.get(i)
 
        bar.ltfLow2   := bar.ltfLow1
        bar.ltfLow1   := bar.ltfLow
        bar.ltfLow    := aL.get(i)
 
        bull = bar.ltfLow - bar.ltfHigh2 > atr.get(i) * fvgTH3 and bar.ltfLow > bar.ltfHigh2 and bar.ltfClose1 > bar.ltfHigh2
 
        if bull
            fvgLTFbx.push(box.new(bar.index, bar.ltfLow, bar.index + 1, bar.ltfHigh2, color.new(fvgCb3, 73), bgcolor = color.new(fvgCb3, 73))) 
            fvgLTFlb.push(
             label.new(bar.index + 1, math.avg(bar.ltfLow, bar.ltfHigh2), fvgLB3 == 'Show' ? 'LTF(' + ltf + ') FVG' : '', xloc.bar_index, color = color(na), style = label.style_label_left, textcolor = fvgCb3, size = size.small, 
                  tooltip = 'LTF(' + ltf + ') Bullish FVG' )
             )
 
        bear = bar.ltfLow2 - bar.ltfHigh > atr.get(i) * fvgTH3 and bar.ltfHigh < bar.ltfLow2 and bar.ltfClose1 < bar.ltfLow2
 
        if bear
            fvgLTFbx.push(box.new(bar.index, bar.ltfLow2, bar.index + 1, bar.ltfHigh, color.new(fvgCs3, 73), bgcolor = color.new(fvgCs3, 73)))
            fvgLTFlb.push(
             label.new(bar.index + 1, math.avg(bar.ltfLow2, bar.ltfHigh), fvgLB3 == 'Show' ? 'LTF(' + ltf + ') FVG' : '', xloc.bar_index, color = color(na), style = label.style_label_left, textcolor = fvgCs3, size = size.small, 
                  tooltip = 'LTF(' + ltf + ') Bearish FVG' )
             )
 
        if fvgLTFbx.size() > 0
            qt = fvgLTFbx.size()
 
            for bn = qt - 1 to 0 by 1
                if bn < fvgLTFbx.size()
 
                    cBX = fvgLTFbx.get(bn)
                    cLB = fvgLTFlb.get(bn)
                    tBX = cBX.get_top()
                    bBX = cBX.get_bottom()
 
                    if bar.ltfHigh >= tBX and bar.ltfLow <= bBX
                        cBX.delete()
                        fvgLTFbx.remove(bn)
                        fvgLTFlb.remove(bn)
                        cLB.delete()
                    else
                        if bar.ltfHigh > bBX and bar.ltfHigh < tBX
                            cBX.set_bottom(bar.ltfHigh)
                            cLB.set_y(math.avg(cBX.get_top(), bar.ltfHigh))
 
                        if bar.ltfLow < tBX and bar.ltfLow > bBX
                            cBX.set_top(bar.ltfLow)
                            cLB.set_y(math.avg(cBX.get_bottom(), bar.ltfLow))
 
        if fvgLTFbx.size() > 250
            cBX = fvgLTFbx.shift()
            cLB = fvgLTFlb.shift()
            cBX.delete()
            cLB.delete()
 
if fvgLTFbx.size() > 0
    qt = fvgLTFbx.size()
 
    for bn = qt - 1 to 0 by 1
        if bn < fvgLTFbx.size()
 
            cBX = fvgLTFbx.get(bn)
            cLB = fvgLTFlb.get(bn)
            tBX = cBX.get_top()
            bBX = cBX.get_bottom()
 
            if bar.high > tBX and bar.low < bBX and bar.index > cBX.get_left()
                fvgLTFbx.remove(bn)
                cBX.delete()
                fvgLTFlb.remove(bn)
                cLB.delete()
            else
                cBX.set_right(bar.index + 1)
                cBX.set_border_color(na)
                cLB.set_x(bar.index + 1)
 
 
if last_bar_index - bar_index <= maxBars and fvgSH1
    renderFvg(fvgSH1, fvgTF1, fvgFill1, fvgCb1, fvgCs1, fvgLB1)
 
if last_bar_index - bar_index <= maxBars2 and fvgSH2
    renderFvg(fvgSH2, fvgTF2, fvgFill2, fvgCb2, fvgCs2, fvgLB2)
 
table logo = table.new(position.bottom_right, 1, 1)
if barstate.isfirst
    table.cell(logo, 0, 0, '☼☾  ', text_size = size.normal, text_color = color.teal)

← 返回列表