Swing Fibonacci: An Archimedean Spiral Anchored on the Last Confirmed Swing

Category: Indicators By: Iván González Created: September 10, 2026, 1:07 PM
September 10, 2026, 1:07 PM
Indicators
0 Comments

Introduction

Most structure indicators stop once they have found the pivot. They draw the zigzag, label the high or the low, and leave the rest to you. Swing Fibonacci, an indicator by BigBeluga, adds a second layer on top: from the last confirmed pivot it grows an Archimedean spiral whose size is fixed by the market itself, and then leaves it frozen there until the structure flips again.

The result looks unusual on a chart, and it invites a misreading worth clearing up in the first paragraph. The spiral is not a forecast and it does not track price. It is a ruler. Its arms mark, in price and in bars at the same time, how far the market would travel if it kept expanding at the pace of the leg that has just been confirmed. Price wandering inside the inner coils is a market that has not yet used up its own recent range; price reaching an outer arm is a market that has.

Theory Behind the Indicator

The swing engine, and why the pivot appears in the past

The structure part is deliberately blunt. A new 50-bar high turns the state bullish; a new 50-bar low turns it bearish. Nothing else moves it.

What matters is what happens when the state flips, because that is the only moment the indicator commits to anything:

  • The pivot is placed retroactively, on the opposite extreme. When the state turns bullish, the indicator does not mark the bar that made the new high — it marks the low the market left behind, which may be forty bars back. That low is the anchor.
  • The leg just completed is drawn, from the previous extreme to the new pivot.
  • Two numbers are frozen: the range of the last 500 bars, and the distance in bars from the anchor to the current bar plus twenty.

Those two frozen numbers are the whole geometry of the spiral. Between one flip and the next they do not change, so the figure stays exactly where it was put. This is the property that makes it usable: a shape that moved with every new bar would tell you nothing, because you could never say whether price had reached it or it had come to price.

The 50-bar minimum on the structure length is not decoration either. Shorter values make the state flip constantly, and every flip re-anchors and redraws the whole figure.

How the spiral is built

The spiral is Archimedean — the distance between consecutive coils is constant, unlike a logarithmic spiral where it grows geometrically. Walking an angle θ from 0 to arms × 180 degrees, with u running from 0 to 1 over the same walk:

x(u) = anchorBar   -     u * cos(θ) * radius * legLength
y(u) = anchorPrice + s * u * sin(θ) * range * radius / dY

 

s is −1 in an uptrend and +1 in a downtrend, which flips the spiral so that it opens on the side the market is coming from. dY is the peak-to-peak span of u × sin(θ), and it is the one quantity that cannot be known in advance: it depends on how many coils are drawn, so the curve has to be walked once to measure it before it can be walked again to draw it.

Two things fall out of those formulas that are worth stating plainly, because they change how you set the indicator up.

The number of arms cancels out. It appears in the numerator and again in the denominator on both axes. Adding coils does not make the figure bigger — it makes it denser. Eight arms and three arms occupy the same box on the chart; the difference is how many times the curve wraps around inside it.

Radius is not a size, it is a multiplier of the market’s own range. After the cancellation the total height of the spiral is exactly:

height = range(500 bars) × radius

 

So radius = 4 draws a figure four times as tall as everything price has done in the last 500 bars, and the candles get squeezed into a quarter of the pane. This is the parameter to touch first, and downwards. The value shipped here is 2.5, which is what the chart in the forum topic uses, and it is already generous.

The horizontal reach follows the same rule: ± radius × legLength bars around the anchor. With a long leg the spiral runs several hundred bars into the future, so the chart needs free space on its right for the outer arm to be visible.

Reading the colour

The trace runs through a gradient from the bearish colour at the centre to the bullish colour at the outermost arm. That is a position cue, not a signal: it tells you which end of the curve you are looking at when the coils overlap, nothing more.

The dashed line from the anchor to the live price, and the Swing % reading in the panel, are the part that does carry information — that percentage is the distance travelled since the anchor, and it is the number to compare against previous legs on the same instrument.

How to Use It

The honest use of this indicator is as a framing tool, not a trigger. Three readings it gives cheaply:

  1. Where the current leg stands. The Swing % against the anchor, compared with how far previous legs ran on the same instrument and timeframe. A leg that has already covered more than its predecessors while the state has not flipped is a stretched leg.
  2. Whether the market is using its range. Price crawling around the inner coils means the move is small relative to the last 500 bars, regardless of how dramatic it looks on a zoomed chart.
  3. Where the structure actually turned. The retroactive pivot is a genuinely useful piece of information on its own, and it is drawn conservatively: no pivot appears until the state has flipped, so nothing on the chart is provisional.

What it will not do is time an entry. There is no rule in the construction that connects an arm of the spiral to a reaction in price — the arms are where the geometry puts them, and the geometry only knows the 500-bar range and the length of one leg.

Parameters

  • Swing Structure (50): how significant a high or low must be. Higher values find major turns only.
  • Spirals qty (8): number of coils. Changes density, not size.
  • Radius (2.5): multiplies the 500-bar range to set the height, and the leg length to set the reach. Raise it carefully.
  • Segments of the spiral (300): resolution of the trace. Lower it to 150 if the chart feels heavy; the shape holds.
  • Bars drawn to the right (500): how far into the future the figure is allowed to extend.
  • Swing marks kept on the chart (60): depth of the zigzag history.
  • Show panel / Show regime tint: the top-right readout and the background tint that marks which state each stretch of the chart was in.

Conclusion

Swing Fibonacci is one of those indicators whose visual is louder than its logic, and the two should not be confused. Underneath the figure there is a plain 50-bar structure engine and two frozen numbers. Everything else is geometry.

Used as it is meant to be — a frozen ruler anchored on a confirmed pivot, read together with the percentage travelled since that pivot — it answers a question most structure tools leave open: not where did the market turn, but how much of its own recent range has this leg already spent.

 

//---------------------------------------------------------------------------//
//PRC_Swing Fibonacci
//version = 1
//10.09.2026
//Ivan Gonzalez @ www.prorealcode.com
//Author: BigBeluga
//Sharing ProRealTime knowledge
//---------------------------------------------------------------------------//
//Swing structure zigzag whose last confirmed pivot anchors an Archimedean
//spiral. The spiral is a compass, not a forecast: it is drawn from the anchor
//with a fixed geometry, so its arms mark where the market would be if it kept
//expanding at the pace of the leg that has just been confirmed.
//---------------------------------------------------------------------------//
DEFPARAM drawonlastbaronly = true


//-----Swing structure-------------------------------------------------------//
swingLength = 50       //Swing Structure (50..100): how significant a high or low must be
maxMarks    = 60       //Swing marks kept on the chart


//-----Spiral----------------------------------------------------------------//
spiralArms  = 8        //Spirals qty (1..10): rotations around the anchor
spiralRad   = 2.5      //Radius (1..8): vertical stretch AND horizontal reach
spiralRes   = 300      //Segments of the spiral, 3 or more (original resolution: 300)
maxFwd      = 500      //Bars drawn to the right of the last one (original cut-off)


//-----Panel and regime------------------------------------------------------//
showPanel   = 1        //1 = anchor price / direction / swing % panel
showRegime  = 1        //1 = trend tint behind the price
dashX       = -270     //panel: horizontal offset of the label column
dashY       = -20      //panel: vertical offset from the top edge
dashCol     = 110      //panel: column width


//-----Palette---------------------------------------------------------------//
// The lime / fuchsia of the original are calibrated for a black TradingView
// chart. Both are pulled down in luminance so they stay readable on the light
// PRT background, keeping the hue.
bullR = 0
bullG = 170
bullB = 80
bearR = 190
bearG = 0
bearB = 170
neuR  = 105
neuG  = 110
neuB  = 120
txtR  = 35
txtG  = 38
txtB  = 45


//---------------------------------------------------------------------------//
//1. SWING STRUCTURE
//---------------------------------------------------------------------------//
// A new N-bar high turns the structure bullish, a new N-bar low turns it
// bearish. A swing is CONFIRMED when the state flips: the pivot is then placed
// retroactively on the opposite extreme, which is the one that has just been
// left behind.
//---------------------------------------------------------------------------//
ONCE hiIdx = 0
ONCE hiVal = high
ONCE loIdx = 0
ONCE loVal = low
ONCE hiSet = 0
ONCE loSet = 0
ONCE dirUp = 0
ONCE nMk = 0
ONCE swIdx = 0
ONCE swPrice = 0
ONCE swReady = 0
ONCE lockRng = 0
ONCE lockLen = 0


mkRing = maxMarks + 1        // one spare slot: it is written on the live bar


// state as it stood at the close of the previous bar
prevDir = dirUp
pHiIdx  = hiIdx
pHiVal  = hiVal
pLoIdx  = loIdx
pLoVal  = loVal
pHiSet  = hiSet
pLoSet  = loSet


IF barindex >= swingLength - 1 THEN
   hiWin = highest[swingLength](high)
   loWin = lowest[swingLength](low)


   IF high = hiWin THEN
      hiIdx = barindex
      hiVal = high
      hiSet = 1
      dirUp = 1
   ENDIF
   // the low test runs AFTER the high test, as in the original: on a bar that
   // is both the N-bar high and the N-bar low, the structure ends up bearish
   IF low = loWin THEN
      loIdx = barindex
      loVal = low
      loSet = 1
      dirUp = 0
   ENDIF


   IF dirUp <> prevDir THEN
      // range and leg length are FROZEN here and drive the size of the spiral
      // until the next flip
      rngLen  = min(500, barindex + 1)
      lockRng = highest[rngLen](high) - lowest[rngLen](low)


      IF dirUp = 1 THEN
         lockLen = barindex - loIdx + 20
         IF loSet = 1 THEN
            swIdx = loIdx
            swPrice = loVal
            swReady = 1
         ENDIF
      ELSE
         lockLen = barindex - hiIdx + 20
         IF hiSet = 1 THEN
            swIdx = hiIdx
            swPrice = hiVal
            swReady = 1
         ENDIF
      ENDIF


      // Leg of the zigzag: from the opposite extreme AS IT STOOD on the
      // previous bar to the pivot just confirmed. Without both extremes really
      // recorded the leg would start on bar 0 at the price of bar 0, which
      // draws a huge segment and stretches the price axis.
      IF pHiSet = 1 AND pLoSet = 1 THEN
         slot = nMk MOD mkRing
         IF dirUp = 1 THEN
            $mkXa[slot] = pHiIdx
            $mkYa[slot] = pHiVal
            $mkXb[slot] = loIdx
            $mkYb[slot] = loVal
         ELSE
            $mkXa[slot] = pLoIdx
            $mkYa[slot] = pLoVal
            $mkXb[slot] = hiIdx
            $mkYb[slot] = hiVal
         ENDIF
         $mkD[slot] = dirUp
         nMk = nMk + 1
      ENDIF
   ENDIF
ENDIF


//---------------------------------------------------------------------------//
//2. TREND TINT
//---------------------------------------------------------------------------//
// Replaces the row of coloured squares the original prints at the bottom of
// the pane: ProBuilder has no location.bottom, and any fixed price level would
// drift with the zoom.
//---------------------------------------------------------------------------//
bgA = 0
IF showRegime = 1 THEN
   bgA = 14
ENDIF
IF dirUp = 1 THEN
   BACKGROUNDCOLOR(bullR, bullG, bullB, bgA)
ELSE
   BACKGROUNDCOLOR(bearR, bearG, bearB, bgA)
ENDIF


//---------------------------------------------------------------------------//
//3. DRAWING
//---------------------------------------------------------------------------//
// Everything is repainted from stored state inside a single islastbarupdate
// block, so the defparam above can only help: the spiral and the panel are not
// stacked bar after bar, and the swing marks are not lost because they are
// replayed from the ring buffer instead of being drawn on their own bar.
//---------------------------------------------------------------------------//
IF islastbarupdate AND swReady = 1 THEN


   //-----3.1 Zigzag of confirmed swings-------------------------------------//
   nShow = min(nMk, maxMarks)
   stMk = (nMk - nShow) MOD mkRing
   FOR k = 0 TO nShow - 1 DO
      sl = (stMk + k) MOD mkRing
      IF $mkD[sl] = 1 THEN
         // bullish flip: the leg that just ended is the bearish one, and the
         // pivot marked is a low
         segR = bearR
         segG = bearG
         segB = bearB
         ptR = bullR
         ptG = bullG
         ptB = bullB
      ELSE
         segR = bullR
         segG = bullG
         segB = bullB
         ptR = bearR
         ptG = bearG
         ptB = bearB
      ENDIF
      DRAWSEGMENT($mkXa[sl], $mkYa[sl], $mkXb[sl], $mkYb[sl]) COLOURED(segR, segG, segB, 255) STYLE(line, 1)
      DRAWPOINT($mkXb[sl], $mkYb[sl], 3) COLOURED(ptR, ptG, ptB, 255)
   NEXT


   //-----3.2 Spiral---------------------------------------------------------//
   resN = max(3, spiralRes)
   // Pine builds it in radians over two arrays. Solving the algebra first
   // removes both: the number of arms cancels out on each axis, so
   //
   //     x(u) = anchorBar   - u * cos(theta) * radius * lockLen
   //     y(u) = anchorPrice + s * u * sin(theta) * lockRange * radius / dY
   //
   // with u from 0 to 1, theta from 0 to arms * 180 degrees, s = -1 when the
   // structure is bullish, and dY the peak-to-peak of u * sin(theta), which
   // only the first pass can know. Consequence worth reading twice: the total
   // height of the spiral is exactly lockRange * radius, so radius is not a
   // cosmetic size, it multiplies the 500-bar range.
   //
   // The angle advances by rotation recurrence instead of evaluating
   // sin/cos of a number of degrees that reaches 1800 with 10 arms.
   dAng = spiralArms * 180 / (resN - 1)
   cD = cos(dAng)
   sD = sin(dAng)


   // pass 1: peak-to-peak of u * sin(theta)
   cA = 1
   sA = 0
   yLo = 0
   yHi = 0
   FOR i = 0 TO resN - 1 DO
      yv = i / (resN - 1) * sA
      IF yv < yLo THEN
         yLo = yv
      ENDIF
      IF yv > yHi THEN
         yHi = yv
      ENDIF
      cN = cA * cD - sA * sD
      sN = sA * cD + cA * sD
      cA = cN
      sA = sN
   NEXT
   dY = yHi - yLo


   IF dY > 0 THEN
      yScale = lockRng * spiralRad / dY
      IF dirUp = 1 THEN
         dirSign = -1
      ELSE
         dirSign = 1
      ENDIF


      // pass 2: draw
      cA = 1
      sA = 0
      xPrev = 0
      yPrev = 0
      FOR i = 0 TO resN - 1 DO
         u = i / (resN - 1)
         xCur = round(swIdx - u * cA * spiralRad * lockLen)
         yCur = swPrice + dirSign * u * sA * yScale


         IF i > 0 THEN
            // the original hides whatever falls more than 500 bars to the
            // right of the last bar, which is where Pine stops drawing
            IF xPrev >= 0 AND xCur >= 0 AND xPrev - barindex < maxFwd THEN
               norm = (i - 1) / (resN - 2)
               gR = round(bearR + (bullR - bearR) * norm)
               gG = round(bearG + (bullG - bearG) * norm)
               gB = round(bearB + (bullB - bearB) * norm)
               DRAWSEGMENT(xPrev, yPrev, xCur, yCur) COLOURED(gR, gG, gB, 255) STYLE(line, 2)
            ENDIF
         ENDIF


         xPrev = xCur
         yPrev = yCur
         cN = cA * cD - sA * sD
         sN = sA * cD + cA * sD
         cA = cN
         sA = sN
      NEXT


      // settings tag on the outer end of the spiral
      IF xPrev >= 0 AND xPrev - barindex < maxFwd THEN
         DRAWTEXT("Spirals: #spiralArms#", xPrev, yPrev, sansserif, standard, 10) COLOURED(txtR, txtG, txtB, 255)
         DRAWTEXT("Radius: #spiralRad#", xPrev, yPrev - lockRng * spiralRad / 22, sansserif, standard, 10) COLOURED(txtR, txtG, txtB, 255)
      ENDIF
   ENDIF


   //-----3.3 Live leg and panel---------------------------------------------//
   IF dirUp = 1 THEN
      actPx = high
   ELSE
      actPx = low
   ENDIF
   DRAWSEGMENT(swIdx, swPrice, barindex, actPx) COLOURED(neuR, neuG, neuB, 255) STYLE(dottedline, 1)


   IF showPanel = 1 THEN
      movePct = round((actPx - swPrice) / swPrice * 10000) / 100


      // decimals from the scale of the instrument: format.mintick has no
      // equivalent, and a fixed round(x, 2) flattens forex
      IF swPrice >= 100 THEN
         shownPx = round(swPrice, 2)
      ELSIF swPrice >= 1 THEN
         shownPx = round(swPrice, 4)
      ELSE
         shownPx = round(swPrice, 6)
      ENDIF


      // DRAWTEXT centres the string on its anchor point, so the frame has to
      // clear half a column on each side
      halfCol = dashCol / 2
      boxL = dashX - halfCol - 10
      boxR = dashX + dashCol + halfCol + 10
      cx1 = dashX
      cx2 = dashX + dashCol


      DRAWRECTANGLE(boxL, dashY + 12, boxR, dashY - 82) ANCHOR(topright, xshift, yshift) COLOURED(neuR, neuG, neuB, 200) FILLCOLOR(250, 250, 250, 190)


      DRAWTEXT("Anchor Price", cx1, dashY - 12, sansserif, standard, 11) ANCHOR(topright, xshift, yshift) COLOURED(neuR, neuG, neuB, 255)
      DRAWTEXT("#shownPx#", cx2, dashY - 12, sansserif, bold, 11) ANCHOR(topright, xshift, yshift) COLOURED(txtR, txtG, txtB, 255)


      DRAWTEXT("Direction", cx1, dashY - 37, sansserif, standard, 11) ANCHOR(topright, xshift, yshift) COLOURED(neuR, neuG, neuB, 255)
      IF dirUp = 1 THEN
         DRAWTEXT("BULLISH ▲", cx2, dashY - 37, sansserif, bold, 11) ANCHOR(topright, xshift, yshift) COLOURED(bullR, bullG, bullB, 255)
      ELSE
         DRAWTEXT("BEARISH ▼", cx2, dashY - 37, sansserif, bold, 11) ANCHOR(topright, xshift, yshift) COLOURED(bearR, bearG, bearB, 255)
      ENDIF


      DRAWTEXT("Swing %", cx1, dashY - 62, sansserif, standard, 11) ANCHOR(topright, xshift, yshift) COLOURED(neuR, neuG, neuB, 255)
      IF movePct >= 0 THEN
         DRAWTEXT("#movePct#%", cx2, dashY - 62, sansserif, bold, 11) ANCHOR(topright, xshift, yshift) COLOURED(bullR, bullG, bullB, 255)
      ELSE
         DRAWTEXT("#movePct#%", cx2, dashY - 62, sansserif, bold, 11) ANCHOR(topright, xshift, yshift) COLOURED(bearR, bearG, bearB, 255)
      ENDIF
   ENDIF
ENDIF


RETURN

Download
Filename: PRC_Swing-Fibonacci.itf
Downloads: 10
Iván González Legend
Code artist, my biography is a blank page waiting to be scripted. Imagine a bio so awesome it hasn't been coded yet.
Author’s Profile

Comments

Logo Logo
Loading...