Gaps indicator (similar to TradingView)

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #249243 quote
    David1998P
    Participant
    New

    Parameters/inputs:

    • close gap partially: (boolean, YES or NO)
    • minimal deviation: as volatility multiple
      nATR := vol = AverageTrueRange[periodATR] / Close, threshold = x
    • limit max gap trail length: (boolean)
      • if = 1, then length = y
    • up gaps and down gaps colors

    A gap is a visible empty space between two candles.
    Mathematically: Close(t−1) ≠ Open(t), where t defines the time when the gapping candle occurs.

    If
    |Close(t−1) - Open(t)| ≥ x * Vol(t−1)
    then the gap is considered significant.

    If that empty space gets partially or fully filled between t and t+n, the gap must be updated to reflect the remaining unfilled space on the chart.


    Example:

    • Close(t−1) = 5
    • Open(t) = 10
    • Vol(t−1) = 0.1
    • x = 2

    Then:
    |5 - 10| = 5
    2 * 0.1 * 5 = 1
    Since 5 ≥ 1 → gap is significant.

    If in t+1 the candle low reaches 8, then the remaining gap is from 5 to 8.
    Same applies if, in t, the gapping candle has low < open. That portion is considered filled and must be removed.

    General rule:
    0 ≤ unfilled space ≤ |Close(t−1) - Open(t)| := gap


    Here is the Pine Script version

    //@version=6
    indicator("Gaps", overlay = true, max_boxes_count = 500)
    
    closeGapsPartially = input.bool(false, "Close Gaps Partially", display = display.data_window)
    boxLimitInput = input.int(15, "Max Number of Gaps", minval = 1, maxval = 500, display = display.data_window)
    minimalDeviationTooltip = "Specifies the minimal size of detected gaps, as a percentage of the average high-low range for the last 14 bars."
    minimalDeviationInput = nz(input.float(30.0, "Minimal Deviation (%)", tooltip = minimalDeviationTooltip, minval=1, maxval=100, display = display.data_window) / 100 * ta.sma(high-low, 14))
    limitBoxLengthBoolInput = input.bool(false, "Limit Max Gap Trail Length (bars)", inline = "Length Limit", display = display.data_window)
    limitBoxLengthIntInput = input.int(300, "", inline = "Length Limit", minval = 1, display = display.data_window)
    
    groupName = "Border and fill colors"
    colorUpBorderInput = input.color(color.green, "Up Gaps", inline = "Gap Up", group = groupName, display = display.data_window)
    colorUpBackgroundInput = input.color(color.new(color.green, 85), "", inline = "Gap Up", group = groupName, display = display.data_window)
    colorDownBorderInput = input.color(color.red, "Down Gaps", inline = "Gap Down", group = groupName, display = display.data_window)
    colorDownBackgroundInput = input.color(color.new(color.red, 85), "", inline = "Gap Down", group = groupName, display = display.data_window)
    
    type AlertInfo
        int countOpenGap
        int countClosedGap
    
    method hasOpenedGap(AlertInfo this) =>
        this.countOpenGap > 0
    
    method hasClosedGap(AlertInfo this) =>
        this.countClosedGap > 0
    
    AlertInfo alertInfo = AlertInfo.new(0, 0)
    
    type Gap
        bool isActive
        bool isBull
        array<box> boxes
    
    method delete(Gap this) =>
        for _box in this.boxes
            _box.delete()
    
    method partialClose(Gap this) =>
        activeBox = this.boxes.last()
        activeBox.set_extend(extend.none)
    
        top = this.isBull ? activeBox.get_top() : low
        bottom = this.isBull ? high : activeBox.get_bottom()
    
        this.boxes.push(box.new(
          bar_index,
          top,
          bar_index,
          bottom, 
          this.isBull ? colorDownBorderInput : colorUpBorderInput, 
          bgcolor = this.isBull ? colorDownBackgroundInput : colorUpBackgroundInput))
    
    method fullClose(Gap this) =>
        alertInfo.countClosedGap += 1
        activeBox = this.boxes.last()
        activeBox.set_extend(extend.none)
        this.isActive := false
        if closeGapsPartially
            activeBox.delete()    
    
    method checkForClose(Gap this) =>
        if this.isActive
            activeBox = this.boxes.last()
            top = activeBox.get_top() 
            bot = activeBox.get_bottom()
            isBull = this.isBull
            activeBox.set_right(bar_index)
    
            if (high > bot and isBull) or (low < top and not isBull)
                if closeGapsPartially
                    this.partialClose()                
                else
                    this.fullClose()
    
            bool forceCloseBoxExceededLengthLimit = (limitBoxLengthBoolInput and bar_index - activeBox.get_left() >= limitBoxLengthIntInput)
            if ((high > top and isBull) or (low < bot and not isBull)) or forceCloseBoxExceededLengthLimit
                this.fullClose()
    
    var allGaps = array.new<Gap>()
    
    isGapDown = high < low[1] and low[1] - high >= minimalDeviationInput
    isGapUp = low > high[1] and low - high[1] >= minimalDeviationInput
    isGap = isGapDown or isGapUp
    boxBorderColor = isGapDown ? colorDownBorderInput : colorUpBorderInput
    boxBgcolor = isGapDown ? colorDownBackgroundInput : colorUpBackgroundInput
    
    registerNewGap(bool isGapDown) => 
        alertInfo.countOpenGap += 1
        
        newBox = box.new(
          bar_index - 1,
          (isGapDown ? low[1] : low),
          bar_index,
          (isGapDown ? high : high[1]),
          border_color = boxBorderColor,
          bgcolor = boxBgcolor,
          extend = extend.right)
    
        allGaps.push(Gap.new(true, isGapDown, array.from(newBox)))
    
        if allGaps.size() > boxLimitInput
            allGaps.shift().delete()
    
    for gap in allGaps
        gap.checkForClose()
    
    if isGap    
        registerNewGap(isGapDown)
    
    if barstate.islastconfirmedhistory and allGaps.size() == 0
        noGapText = "No gaps found on the current chart. \nThe cause could be that some exchanges align the open of new bars on the close of the previous one, resulting in charts with no gaps. Alternatively, your Minimal Deviation might be too high."
        var infoTable = table.new(position.bottom_right, 1, 1)
        table.cell(infoTable, 0, 0, text = noGapText, text_color = chart.bg_color, bgcolor = chart.fg_color)
    
    alertcondition(alertInfo.hasOpenedGap(), "New Gap Appeared", "A new gap has appeared.")
    alertcondition(alertInfo.hasClosedGap(), "Gap Closed", "A gap was closed.")
    

    Note: The version we want is slightly different — it detects gaps using Close(t−1) instead of High(t−1) which is more accurate.

    #249301 quote
    Zigo
    Participant
    Master

    Gaps & Gapclosing (Long Running Gaps)

    Date 208/2020 #42853

    #249302 quote
    David1998P
    Participant
    New

    Anyone knows how to code it?

    #249303 quote
    JS
    Participant
    Senior
    #249316 quote
    David1998P
    Participant
    New

    This is a completely different indicator…

    There is currently no alternative to the one from TradingView on this forum

    #249317 quote
    JS
    Participant
    Senior

    You’re welcome…

    #249925 quote
    Iván González
    Moderator
    Master

    you can start with this:

    // --- PARÁMETROS CONFIGURABLES ---
    closeGapsPartially = 1
    minimalDeviationPercent = 30
    // --- INICIALIZACIÓN ---
    ONCE gapCount = 0
    ONCE rectCount = 0
    // --- BLOQUE 1: CÁLCULO Y GESTIÓN DE ESTADO (Se ejecuta en cada recálculo de cada barra) ---
    // Resetear contadores en la primera barra del gráfico
    IF BarIndex = 0 THEN
       gapCount = 0
       rectCount = 0
    ENDIF
    // Calcular desviación mínima
    rangeSMA = Average[14](high-low)
    minimalDeviation = (minimalDeviationPercent / 100) * rangeSMA
    // --- Detección de nuevos gaps ---
    isGapUp = low > high[1] AND low - high[1] >= minimalDeviation
    isGapDown = high < low[1] AND low[1] - high >= minimalDeviation
    
    IF isGapUp OR isGapDown THEN
       n=gapCount
       $gapIsActive[n] = 2// 2=Inicial;1=Parcial;0=Cerrado
       $gapStartIndex[n] = BarIndex-1
       $gapEndIndex[n] = BarIndex
       IF isGapUp THEN
          $gapTop[n] = low
          $gapBottom[n] = high[1]
          $gapType[n]=1
       ELSE // isGapDown
          $gapTop[n] = low[1]
          $gapBottom[n] = high
          $gapType[n]=-1
       ENDIF
       gapCount = gapCount + 1
    ENDIF
    // --- Gestión de cierres de gaps activos ---
    FOR i = 0 TO gapCount - 1 DO
       IF $gapIsActive[i] = 2 THEN
          // Cierre COMPLETO
          IF ($gapType[i]>0 AND low < $gapBottom[i]) OR ($gapType[i]<0 AND high > $gapTop[i]) THEN// Guardar el rectángulo final en la memoria
             $rectX1[rectCount] = $gapStartIndex[i]//-1
             $rectY1[rectCount] = $gapTop[i]
             $rectX2[rectCount] = BarIndex
             $rectY2[rectCount] = $gapBottom[i]
             $rectType[rectCount] = $gapType[i]
             $rectGAP[rectCount] = i
             rectCount = rectCount + 1
             $gapIsActive[i] = 0 // Desactivar el gap                  
             // Cierre PARCIAL (solo si la opción está activa)
          ELSIF closeGapsPartially = 1 AND (($gapType[i]>0 AND low < $gapTop[i]) OR ($gapType[i]<0 AND high > $gapBottom[i])) THEN
             $gapIsActive[i] = 1  // Guardar el rectángulo de la parte rellenada en la memoria
             $rectX1[rectCount] = $gapStartIndex[i] //- 1
             $rectX2[rectCount] = BarIndex
             if $gapType[i]>0 then
                $rectY1[rectCount] = $gapBottom[i]
                $rectY2[rectCount] = $gapTop[i]// IIF(cond, true, false)
             else
                $rectY1[rectCount] = $gapTop[i]
                $rectY2[rectCount] = $gapBottom[i]
             endif
             $rectType[rectCount] = $gapType[i]
             $rectGAP[rectCount] = i
             rectCount = rectCount + 1// Actualizar el gap activo con las nuevas coordenadas
             $gapStartIndex[i] = BarIndex
             IF $gapType[i]>0 THEN
                $gapTop[i] = low
             ELSE
                $gapBottom[i] = high
             ENDIF
          ENDIF
       elsif $gapIsActive[i] = 1 then
          // Cierre COMPLETO
          IF ($gapType[i]>0 AND low < $gapBottom[i]) OR ($gapType[i]<0 AND high > $gapTop[i]) THEN// Guardar el rectángulo final en la memoria
             $rectX1[rectCount] = $gapStartIndex[i]//-1
             $rectY1[rectCount] = $gapTop[i]
             $rectX2[rectCount] = BarIndex
             $rectY2[rectCount] = $gapBottom[i]
             $rectType[rectCount] = $gapType[i]
             $rectGAP[rectCount] = i
             rectCount = rectCount + 1
             $gapIsActive[i] = 0 // Desactivar el gap
             // Cierre PARCIAL (solo si la opción está activa)
          ELSIF closeGapsPartially = 1 AND (($gapType[i]>0 AND low < $gapTop[i]) OR ($gapType[i]<0 AND high > $gapBottom[i])) THEN  // Guardar el rectángulo de la parte rellenada en la memoria
             $rectX1[rectCount] = $gapStartIndex[i] //- 1
             $rectX2[rectCount] = BarIndex
             if $gapType[i]>0 then
                $rectY1[rectCount] = $gapBottom[i]
                $rectY2[rectCount] = $gapTop[i] // IIF(cond, true, false)
             else
                $rectY1[rectCount] = $gapTop[i]
                $rectY2[rectCount] = $gapBottom[i]
             endif
             $rectType[rectCount] = $gapType[i]
             $rectGAP[rectCount] = i
             rectCount = rectCount + 1// Actualizar el gap activo con las nuevas coordenadas
             $gapStartIndex[i] = BarIndex
             IF $gapType[i]>0 THEN
                $gapTop[i] = low
             ELSE
                $gapBottom[i] = high
             ENDIF
          endif
       ENDIF
    NEXT
    IF islastbarupdate THEN// Dibujar todos los rectángulos históricos memorizados
       FOR r = 0 TO rectCount - 1 DO
          IF $rectType[r] = 1 THEN
             DRAWRECTANGLE($rectX1[r], $rectY1[r], $rectX2[r], $rectY2[r]) coloured("green")fillcolor("green",30)
          ELSE
             DRAWRECTANGLE($rectX1[r], $rectY1[r], $rectX2[r], $rectY2[r]) coloured("red")fillcolor("red",30)
          ENDIF
       NEXT
       // Dibujar todos los gaps que siguen activos
       FOR i = 0 TO gapCount - 1 DO
          IF $gapIsActive[i] > 0 THEN
             startIndex = $gapStartIndex[i] //- 1
             gapTop = $gapTop[i]
             endIndex = BarIndex
             gapBottom = $gapBottom[i]
             IF $gapType[i]>0 THEN
                DRAWRECTANGLE(startIndex, gapTop, endIndex, gapBottom) coloured("green")fillcolor("green",30)
             ELSE
                DRAWRECTANGLE(startIndex, gapTop, endIndex, gapBottom)coloured("red")fillcolor("red",30)
             ENDIF
          ENDIF
       NEXT
    ENDIF
    
    RETURN
Viewing 7 posts - 1 through 7 (of 7 total)
  • You must be logged in to reply to this topic.

Gaps indicator (similar to TradingView)


ProBuilder: Indicators & Custom Tools

New Reply
Author
author-avatar
David1998P @david1998p Participant
Summary

This topic contains 6 replies,
has 4 voices, and was last updated by Iván González
5 months, 2 weeks ago.

Topic Details
Forum: ProBuilder: Indicators & Custom Tools
Language: English
Started: 08/01/2025
Status: Active
Attachments: 1 files
Logo Logo
Loading...