Change in State of Delivery (CISD)

Category: Indicators By: Iván González Created: May 20, 2026, 4:26 PM
May 20, 2026, 4:26 PM
Indicators
0 Comments

Introduction

Change in State of Delivery (CISD) is one of the most useful ICT concepts to emerge after 2023. It tries to answer a very specific question: at what exact bar does control of the tape shift from buyers to sellers (or vice versa)? The classic structural answer is Market Structure Shift (MSS) — wait for price to break a confirmed pivot. CISD goes earlier: it fires the moment a candle closes its body beyond the open of the first candle that started the current directional leg.

That’s it. No moving averages, no oscillator, no break of structure. Just: did the close cross back through the open of the candle that initiated the run? If yes, the leg has failed and the other side is now in control.

The trade-off is obvious. CISD is faster than MSS, so it gives more signals — and more false ones. The original LuxAlgo implementation therefore offers two modes:

  1. Classic CISD — every directional change prints a signal. Permissive.
  2. Liquidity Sweep CISD — a signal is only valid if the previous leg actually swept a prior swing high or swing low. Far more selective; closer to a structured ICT entry.

This ProRealTime port reproduces both modes, the minimum/maximum duration filters, and the historical visualisation (horizontal level from origin to trigger, recoloured on activation).

Theory Behind the Indicator

What CISD actually measures

After the first candle of a new directional leg appears, its open becomes a candidate level. As long as the leg unfolds normally, price stays on the same side of that level. The moment a later candle closes back across it — body, not wick — the leg is considered failed and a CISD prints.

  • Bullish leg starts on the first bull candle following a bear bar. Its open is the Bull CISD level. If a later close drops below it → Bearish CISD (control flipping back to sellers).
  • Bearish leg starts on the first bear candle following a bull bar. Its open is the Bear CISD level. If a later close climbs above it → Bullish CISD (control flipping back to buyers).

Two duration filters refine the signal:

  • minSeqLen: minimum bars between level creation and trigger. Filters whip-saws on the bar that created the level.
  • maxSeqLen: maximum lifetime of a pending level. If the leg runs long enough that the level becomes irrelevant, the level is dropped without firing.

Classic vs Liquidity Sweep

In Classic mode, every directional change creates a pending level. There is exactly one bullish pending level and one bearish pending level alive at any time; a new leg replaces the previous pending level even if it never triggered. This is the original LuxAlgo behaviour (oBull/oBear singletons in the Pine source).

In Liquidity Sweep mode, a level is only armed when the previous leg has barred a prior swing: price spiked above (or below) a confirmed pivot wick and closed back on the original side — the textbook stop-hunt-then-rejection. Several swept pivots can have pending levels at the same time, so this mode uses a FIFO pool of up to ten simultaneous levels per side.

Why CISD is not MSS

MSS waits for a confirmed pivot to be broken. That requires a swing point to exist and be taken out — typically several bars after the actual change of intent. CISD fires on the first body-close back through the leg’s origin, often several bars before the next pivot is even formed. CISD is the early trigger; MSS is the confirmation. They are complementary, not redundant.

How to Read the Indicator

  1. The label sits on the level itself. Each “CISD” tag is drawn at the level price (the open of the leg’s first candle), horizontally centred between the leg origin and the bar where the close crossed it back. The colour of the label and segment indicates which side triggered: green for a bullish CISD, red for a bearish one.
  2. Dim labels mean counter-trend signals. When a CISD prints in the same direction as the previous one (two bearish CISDs in a row, for example), the second is shown with reduced opacity.
  3. Classic prints noise on purpose. Every directional change arms a new pending level, even if the previous one never fired. Use Classic for context (where are the active levels?) and Sweep for actual triggers.
  4. Sweep mode is the structured entry. A signal here means: price recently grabbed liquidity above a swing high (or below a swing low), then closed back through the leg’s origin candle. That is the textbook ICT reversal sequence.
  5. CISD ≠ MSS. CISD fires earlier than a Market Structure Shift. Use it as a trigger and look for MSS as later confirmation, not the other way round.

Practical Applications

  1. Tight-stop reversal entries. A Sweep-mode CISD with stop just above (below) the swept wick is a classic high-RR ICT entry. The level itself is the structural invalidation.
  2. Confluence with order blocks / FVGs. A CISD that triggers inside a previously-marked order block or fair-value gap is a strong combined signal.
  3. Trend continuation, not just reversal. A bullish CISD that prints after a bear correction in an established uptrend is a continuation trigger. Track which direction the current alpha-100% signal points to.
  4. Discretionary filter. Even traders who do not enter on the bar of the signal can use CISD as an “is the leg over?” gate before pulling the trigger on a discretionary setup.

Indicator Configuration

| Parameter | Default | Description |
|---|---|---|
| `mode` | 1 | 0 = Classic, 1 = Liquidity Sweep |
| `swingLen` | 10 | Pivot lookback (used only in Sweep mode) |
| `minSeqLen` | 0 | Minimum bars between level creation and trigger |
| `maxSeqLen` | 100 | Maximum lifetime of a pending level (bars) |
| `MAXC` | 10 | Active CISD levels per side (Sweep mode) |
| `MAXP` | 10 | Active pivots per side (Sweep mode) |
| `MAXS` | 50 | Historical signals kept on screen |

 

Code

//--------------------------------------------
// PRC_CISD (by LuxAlgo)
// version = 0
// 20.05.2026
// Iván González @ www.prorealcode.com
// Sharing ProRealTime knowledge
// Concepto y código Pine original: LuxAlgo
//--------------------------------------------
DEFPARAM DRAWONLASTBARONLY = TRUE

//=== PARAMETROS ===
mode      = 1      // 0 = Classic, 1 = Liquidity Sweep
swingLen  = 10     // Lookback pivots (solo modo Sweep)
minSeqLen = 0      // Barras minimas antes de aceptar disparo
maxSeqLen = 100    // Caducidad: barras maximas de un nivel sin disparar

//=== CAPACIDADES POOL (constantes) ===
MAXC = 10          // niveles CISD vivos por lado
MAXP = 10          // pivots vivos por lado (modo Sweep)
MAXS = 50          // historico de senales CISD para render

//=== DETECCION DE VELA ===
bull = close > open
bear = close < open

// Track de "primera vela contraria" = candidato a nivel CISD
ONCE trackBullPrc = 0
ONCE trackBullBix = 0
ONCE trackBearPrc = 0
ONCE trackBearBix = 0

IF bull AND bear[1] THEN
   trackBullPrc = open
   trackBullBix = barindex
ENDIF
IF bear AND bull[1] THEN
   trackBearPrc = open
   trackBearBix = barindex
ENDIF

ONCE trend = 0     // +1 ultimo CISD bullish, -1 ultimo CISD bearish

//=== MODO CLASSIC: singleton por direccion (replica oBull/oBear del Pine) ===
// Pine mantiene UN solo nivel activo por direccion: cada nueva pierna sobrescribe el anterior
// si no habia disparado (oBull.ln.delete()). NO es un pool en modo Classic.
IF mode = 0 THEN
   // Primera vela alcista tras pierna bajista -> reset + arm cBull[0] (vigila CISD bearish)
   IF bull AND bear[1] THEN
      FOR iRb = 0 TO MAXC - 1 DO
         $cBullAct[iRb] = 0
      NEXT
      $cBullX[0] = trackBullBix
      $cBullY[0] = trackBullPrc
      $cBullBorn[0] = barindex
      $cBullAct[0] = 1
   ENDIF
   
   // Primera vela bajista tras pierna alcista -> reset + arm cBear[0] (vigila CISD bullish)
   IF bear AND bull[1] THEN
      FOR iRc = 0 TO MAXC - 1 DO
         $cBearAct[iRc] = 0
      NEXT
      $cBearX[0] = trackBearBix
      $cBearY[0] = trackBearPrc
      $cBearBorn[0] = barindex
      $cBearAct[0] = 1
   ENDIF
ENDIF

//=== MODO SWEEP: detectar pivots + sweep, luego armar nivel ===
IF mode = 1 THEN
   // Pivot high confirmado (asimetrico: swingLen barras izq, 1 barra der; barra pivote = [1])
   IF barindex > swingLen + 1 THEN
      IF high[1] = highest[swingLen + 2](high) THEN
         // FIFO insert pivot high
         slotPH = -1
         cntPH = 0
         oldIdxPH = -1
         oldBornPH = 0
         FOR iPH = 0 TO MAXP - 1 DO
            IF $pHAct[iPH] = 1 THEN
               cntPH = cntPH + 1
               IF oldIdxPH = -1 THEN
                  oldBornPH = $pHBorn[iPH]
                  oldIdxPH = iPH
               ELSIF $pHBorn[iPH] < oldBornPH THEN
                  oldBornPH = $pHBorn[iPH]
                  oldIdxPH = iPH
               ENDIF
            ELSE
               IF slotPH = -1 THEN
                  slotPH = iPH
               ENDIF
            ENDIF
         NEXT
         IF slotPH = -1 THEN
            slotPH = oldIdxPH
         ENDIF
         $pHX[slotPH] = barindex - 1
         $pHY[slotPH] = high[1]
         $pHBorn[slotPH] = barindex
         $pHAct[slotPH] = 1
      ENDIF
      
      IF low[1] = lowest[swingLen + 2](low) THEN
         // FIFO insert pivot low
         slotPL = -1
         cntPL = 0
         oldIdxPL = -1
         oldBornPL = 0
         FOR iPL = 0 TO MAXP - 1 DO
            IF $pLAct[iPL] = 1 THEN
               cntPL = cntPL + 1
               IF oldIdxPL = -1 THEN
                  oldBornPL = $pLBorn[iPL]
                  oldIdxPL = iPL
               ELSIF $pLBorn[iPL] < oldBornPL THEN
                  oldBornPL = $pLBorn[iPL]
                  oldIdxPL = iPL
               ENDIF
            ELSE
               IF slotPL = -1 THEN
                  slotPL = iPL
               ENDIF
            ENDIF
         NEXT
         IF slotPL = -1 THEN
            slotPL = oldIdxPL
         ENDIF
         $pLX[slotPL] = barindex - 1
         $pLY[slotPL] = low[1]
         $pLBorn[slotPL] = barindex
         $pLAct[slotPL] = 1
      ENDIF
   ENDIF
   
   // Comprobar SWEEP en cada pivot vivo
   // Pivot high sweepeado (high > pivot AND close < pivot) -> arma nivel cBull (vigila CISD bearish)
   FOR iSH = 0 TO MAXP - 1 DO
      IF $pHAct[iSH] = 1 THEN
         IF barindex - $pHBorn[iSH] > maxSeqLen THEN
            $pHAct[iSH] = 0
         ELSIF close > $pHY[iSH] THEN
            // pivot roto al alza sin sweep -> retirar
            $pHAct[iSH] = 0
         ELSIF high > $pHY[iSH] AND close < $pHY[iSH] THEN
            // SWEEP detectado -> verifico trackBull intacto, armo cBull (CISD bearish pendiente)
            ok = 1
            gap = barindex - trackBullBix
            IF gap > maxSeqLen THEN
               ok = 0
            ENDIF
            IF ok = 1 THEN
               FOR jG = 0 TO gap DO
                  IF close[jG] < trackBullPrc THEN
                     ok = 0
                  ENDIF
               NEXT
            ENDIF
            IF ok = 1 THEN
               slotG = -1
               cntG = 0
               oldIdxG = -1
               oldBornG = 0
               FOR iG = 0 TO MAXC - 1 DO
                  IF $cBullAct[iG] = 1 THEN
                     cntG = cntG + 1
                     IF oldIdxG = -1 THEN
                        oldBornG = $cBullBorn[iG]
                        oldIdxG = iG
                     ELSIF $cBullBorn[iG] < oldBornG THEN
                        oldBornG = $cBullBorn[iG]
                        oldIdxG = iG
                     ENDIF
                  ELSE
                     IF slotG = -1 THEN
                        slotG = iG
                     ENDIF
                  ENDIF
               NEXT
               IF slotG = -1 THEN
                  slotG = oldIdxG
               ENDIF
               $cBullX[slotG] = trackBullBix
               $cBullY[slotG] = trackBullPrc
               $cBullBorn[slotG] = barindex
               $cBullAct[slotG] = 1
            ENDIF
            $pHAct[iSH] = 0
         ENDIF
      ENDIF
   NEXT
   
   // Pivot low sweepeado (low < pivot AND close > pivot) -> arma nivel cBear (vigila CISD bullish)
   FOR iSL = 0 TO MAXP - 1 DO
      IF $pLAct[iSL] = 1 THEN
         IF barindex - $pLBorn[iSL] > maxSeqLen THEN
            $pLAct[iSL] = 0
         ELSIF close < $pLY[iSL] THEN
            $pLAct[iSL] = 0
         ELSIF low < $pLY[iSL] AND close > $pLY[iSL] THEN
            ok2 = 1
            gap2 = barindex - trackBearBix
            IF gap2 > maxSeqLen THEN
               ok2 = 0
            ENDIF
            IF ok2 = 1 THEN
               FOR jG2 = 0 TO gap2 DO
                  IF close[jG2] > trackBearPrc THEN
                     ok2 = 0
                  ENDIF
               NEXT
            ENDIF
            IF ok2 = 1 THEN
               slotG2 = -1
               cntG2 = 0
               oldIdxG2 = -1
               oldBornG2 = 0
               FOR iG2 = 0 TO MAXC - 1 DO
                  IF $cBearAct[iG2] = 1 THEN
                     cntG2 = cntG2 + 1
                     IF oldIdxG2 = -1 THEN
                        oldBornG2 = $cBearBorn[iG2]
                        oldIdxG2 = iG2
                     ELSIF $cBearBorn[iG2] < oldBornG2 THEN
                        oldBornG2 = $cBearBorn[iG2]
                        oldIdxG2 = iG2
                     ENDIF
                  ELSE
                     IF slotG2 = -1 THEN
                        slotG2 = iG2
                     ENDIF
                  ENDIF
               NEXT
               IF slotG2 = -1 THEN
                  slotG2 = oldIdxG2
               ENDIF
               $cBearX[slotG2] = trackBearBix
               $cBearY[slotG2] = trackBearPrc
               $cBearBorn[slotG2] = barindex
               $cBearAct[slotG2] = 1
            ENDIF
            $pLAct[iSL] = 0
         ENDIF
      ENDIF
   NEXT
ENDIF

//=== DISPARO: en cada nivel CISD vivo ===
ONCE sigCnt = 0

bullSig = 0
bearSig = 0
sigY = 0

// Pool cBull (armado en primera vela alcista) -> dispara CISD BEARISH cuando close < level
sigOX = 0
FOR iA = 0 TO MAXC - 1 DO
   IF $cBullAct[iA] = 1 THEN
      ageA = barindex - $cBullBorn[iA]
      IF ageA > maxSeqLen THEN
         $cBullAct[iA] = 0
      ELSIF close < $cBullY[iA] THEN
         IF ageA >= minSeqLen THEN
            bearSig = 1
            sigY = $cBullY[iA]
            sigOX = $cBullX[iA]
         ENDIF
         $cBullAct[iA] = 0
      ENDIF
   ENDIF
NEXT

// Pool cBear (armado en primera vela bajista) -> dispara CISD BULLISH cuando close > level
FOR iB2 = 0 TO MAXC - 1 DO
   IF $cBearAct[iB2] = 1 THEN
      ageB = barindex - $cBearBorn[iB2]
      IF ageB > maxSeqLen THEN
         $cBearAct[iB2] = 0
      ELSIF close > $cBearY[iB2] THEN
         IF ageB >= minSeqLen THEN
            bullSig = 1
            sigY = $cBearY[iB2]
            sigOX = $cBearX[iB2]
         ENDIF
         $cBearAct[iB2] = 0
      ENDIF
   ENDIF
NEXT

//=== GUARDAR SENAL EN HISTORICO (FIFO) ===
IF bullSig + bearSig > 0 THEN
   IF sigCnt < MAXS THEN
      slotSig = sigCnt
      sigCnt = sigCnt + 1
   ELSE
      FOR kSh = 0 TO MAXS - 2 DO
         $sX[kSh] = $sX[kSh + 1]
         $sY[kSh] = $sY[kSh + 1]
         $sOX[kSh] = $sOX[kSh + 1]
         $sDir[kSh] = $sDir[kSh + 1]
         $sTrnd[kSh] = $sTrnd[kSh + 1]
      NEXT
      slotSig = MAXS - 1
   ENDIF
   $sX[slotSig] = barindex
   $sY[slotSig] = sigY
   $sOX[slotSig] = sigOX
   IF bullSig = 1 THEN
      $sDir[slotSig] = 1
      // Trend opuesto al previo? -> contradice = dashed
      IF trend = 1 THEN
         $sTrnd[slotSig] = 1   // contradice
      ELSE
         $sTrnd[slotSig] = 0
      ENDIF
      trend = 1
   ELSE
      $sDir[slotSig] = -1
      IF trend = -1 THEN
         $sTrnd[slotSig] = 1
      ELSE
         $sTrnd[slotSig] = 0
      ENDIF
      trend = -1
   ENDIF
ENDIF

//=== RENDER ===
IF islastbarupdate THEN
   // 1) Niveles CISD activos: linea horizontal punteada (level pendiente)
   FOR iR1 = 0 TO MAXC - 1 DO
      IF $cBullAct[iR1] = 1 THEN
         DRAWSEGMENT($cBullX[iR1], $cBullY[iR1], barindex, $cBullY[iR1]) STYLE(dottedLine, 1) COLOURED(8, 153, 129, 160)
      ENDIF
      IF $cBearAct[iR1] = 1 THEN
         DRAWSEGMENT($cBearX[iR1], $cBearY[iR1], barindex, $cBearY[iR1]) STYLE(dottedLine, 1) COLOURED(242, 54, 69, 160)
      ENDIF
   NEXT
   
   // 2) Pivots vivos (modo Sweep): linea horizontal gris fina del pivot hacia adelante
   IF mode = 1 THEN
      FOR iR2 = 0 TO MAXP - 1 DO
         IF $pHAct[iR2] = 1 THEN
            DRAWSEGMENT($pHX[iR2], $pHY[iR2], barindex, $pHY[iR2]) STYLE(dottedLine, 1) COLOURED(120, 123, 134, 100)
         ENDIF
         IF $pLAct[iR2] = 1 THEN
            DRAWSEGMENT($pLX[iR2], $pLY[iR2], barindex, $pLY[iR2]) STYLE(dottedLine, 1) COLOURED(120, 123, 134, 100)
         ENDIF
      NEXT
   ENDIF
   
   // 3) Historico de senales disparadas: linea horizontal (origen->disparo) + DRAWTEXT centrado
   FOR iR3 = 0 TO sigCnt - 1 DO
      IF $sDir[iR3] = 1 THEN
         rT = 8
         gT = 153
         bT = 129
      ELSE
         rT = 242
         gT = 54
         bT = 69
      ENDIF
      // Senal contra trend previo -> alpha menor (sustituye 'dashed' Pine)
      IF $sTrnd[iR3] = 1 THEN
         alpha = 120
      ELSE
         alpha = 230
      ENDIF
      // Linea horizontal del nivel: desde origen hasta barra de disparo
      DRAWSEGMENT($sOX[iR3], $sY[iR3], $sX[iR3], $sY[iR3]) STYLE(line, 1) COLOURED(rT, gT, bT, alpha)
      // Etiqueta CISD centrada entre origen y disparo
      xMid = ($sOX[iR3] + $sX[iR3]) / 2
      DRAWTEXT("CISD", xMid, $sY[iR3]) COLOURED(rT, gT, bT, alpha)
   NEXT
ENDIF

RETURN

Download
Filename: PRC_CISD.itf
Downloads: 9
Iván González Master
This author is like an anonymous function, present but not directly identifiable. More details on this code architect as soon as they exit 'incognito' mode.
Author’s Profile

Comments

Logo Logo
Loading...