Iván González

Trend Target Ribbon: an ALMA Trend Filter With a Stop and R-Multiple Targets

Category: Indicators By: Iván González Created: November 12, 2026, 10:56 AM
November 12, 2026, 10:56 AM
Indicators
0 Comments
Trend Target Ribbon: an ALMA Trend Filter With a Stop and R-Multiple Targets

Introduction

 

Most trend indicators stop at the colour change. Trend Target Ribbon goes one step further: every time the trend flips, it lays out a complete trade plan on the chart, with an entry, a structural stop and up to four profit targets measured in multiples of the risk. As price moves, the targets light up when price gets close and are ticked off when they are reached. A stop hit closes the plan.

The trend itself comes from an ALMA with two filters, slope and distance, so a flip needs both direction and conviction.

Theory Behind the Indicator

 

The trend filter

 

The core line is an Arnaud Legoux moving average (34 bars, offset 0.85, sigma 6). Two conditions must be true at the same time to confirm a bullish trend:

  • Slope: the change of the ALMA over the last 3 bars, divided by the ATR(14), is above 0.08.
  • Distance: the close is above the ALMA plus 0.65 standard deviations of price (34 bars).

The bearish trend is the mirror image. Once a trend is confirmed, it stays until the opposite setup appears.

The ribbon

 

The ribbon runs from the ALMA to 0.55 standard deviations on the trend side: below the ALMA in an uptrend, above it in a downtrend. Its opacity follows a conviction score that combines the slope and the distance between close and ALMA, both in ATR units. A strong, extended trend gives a solid ribbon; a weak one fades. The ribbon breaks at each flip so that every trend starts as a new segment.

The trade plan

 

At each flip, a position is opened on the close:

  • Stop: the lowest low (long) or highest high (short) of the previous 12 bars, with the distance kept between 0.75 and 3 ATR.
  • Risk (1R): the distance from entry to stop.
  • Targets: at 1R, 2R, 3R and 4R (2 to 4 targets), each with a small zone around it and a shaded band between consecutive levels.

While the plan is open, the levels are projected 30 bars ahead. A target gets brighter as price approaches it (within 0.65R) and receives a tick when it is touched. If the stop is hit, the plan freezes at that bar with the stop marked. A new flip closes the open plan and starts the next one. The last four plans stay on the chart.

Key Features at a Glance

 

  • ALMA trend with slope and deviation confirmation, and a diamond marker at each flip
  • Ribbon with opacity driven by trend conviction
  • Automatic trade plan at every flip: entry, structural stop clamped by ATR, 2 to 4 R-multiple targets
  • Targets that light up on approach and are ticked when reached
  • Three looks for a plan: open, closed by a new trend, and stopped out
  • Up to five past plans kept on the chart

How to Read the Indicator

 

  • Ribbon colour: green for an uptrend, magenta for a downtrend, grey before the first trend.
  • Ribbon opacity: the stronger the slope and the farther the close from the ALMA, the more solid it gets.
  • Diamonds: a new trend, below the bar for bullish and above it for bearish.
  • Plan labels: LONG or SHORT at the entry, “SL -1R” at the stop and “T1 1R” to “T4 4R” at the targets. A tick (✓) means the level was reached.
  • Faded plan: closed by a new trend. Stop line highlighted with a tick: stopped out.

Practical Applications

 

  • A visual trade journal: every flip shows how far the move went in R before the stop or the next flip.
  • Exit planning: the targets give natural places to scale out, and the ticks show how often the move reached 2R or 3R on the instrument you trade.
  • Trend context: the ribbon opacity separates strong trends from weak ones without adding another indicator.

The plans are a visual model of the signals, not a backtest: they ignore costs and take the entry at the close of the flip bar.

Indicator Configuration

 

  • almaLen, almaOffset, almaSigma (34, 0.85, 6): the ALMA settings.
  • devLen, devMult (34, 0.65): standard deviation length and the multiplier the close must clear to confirm a trend.
  • slopeLen, slopeMin (3, 0.08): bars used for the slope and the minimum slope, in ATR.
  • atrLen (14): ATR length, used for the slope, the conviction and the stop limits.
  • stopLookback (12): bars used to find the swing for the stop.
  • minStopAtr, maxStopAtr (0.75, 3): minimum and maximum stop distance, in ATR.
  • targetCount (4): number of targets, from 2 to 4.
  • zonePct (0.06): size of the zone around each target, as a fraction of the risk.
  • approachR (0.65): distance, in R, at which a target starts to light up.
  • extendBars (30): how far the levels are projected.
  • keepPositions (4): number of plans kept on the chart, 1 to 5.
  • showRibbon, showLabels (1, 1): ribbon and labels on or off.
  • labelSize (2): 0 tiny, 1 small, 2 normal, 3 large, 4 huge.
  • bullR/G/B, bearR/G/B, tpR/G/B, slR/G/B, coreR/G/B, neuR/G/B: colours of the bullish trend, bearish trend, targets, stop, core line and neutral state.

The source price is chosen in the indicator settings (by default the close).

The Code

 

//---------------------------------------------------------------
// PRC_Trend Target Ribbon
// version = 0
// 12.11.2026
// Iván González @ www.prorealcode.com
// Author: BOSWaves
// Sharing ProRealTime knowledge
//--------------------------------------------------------------------//
defparam drawonlastbaronly = true
//--------------------------------------------------------------------//
// TREND
//--------------------------------------------------------------------//
almaLen = 34         // ALMA length
almaOffset = 0.85    // ALMA offset
almaSigma = 6        // ALMA sigma
devLen = 34          // deviation length
devMult = 0.65       // trend confirmation (deviation multiplier)
slopeLen = 3         // slope length (bars)
slopeMin = 0.08      // minimum slope, in ATR
atrLen = 14          // ATR length
//--------------------------------------------------------------------//
// POSITION
//--------------------------------------------------------------------//
stopLookback = 12    // bars used to find the swing for the stop
minStopAtr = 0.75    // minimum stop distance (ATR)
maxStopAtr = 3       // maximum stop distance (ATR)
targetCount = 4      // profit targets (2 to 4)
zonePct = 0.06       // target zone size, as a fraction of the risk distance
approachR = 0.65     // distance in R within which a target starts to light up
extendBars = 30      // bars the levels are projected forward
keepPositions = 4    // positions kept on the chart (1 to 5)
//--------------------------------------------------------------------//
// DISPLAY
//--------------------------------------------------------------------//
showRibbon = 1
showLabels = 1
labelSize = 2        // 0 Tiny, 1 Small, 2 Normal, 3 Large, 4 Huge
bullR = 0            // bull colour
bullG = 255
bullB = 0
bearR = 255          // bear colour
bearG = 0
bearB = 102
tpR = 0              // take profit colour
tpG = 255
tpB = 0
slR = 255            // stop loss colour
slG = 0
slB = 102
coreR = 20           // core colour (ALMA line, entry line)
coreG = 20
coreB = 20
neuR = 85            // neutral colour (no trend yet)
neuG = 85
neuB = 85
//--------------------------------------------------------------------//
// TREND ENGINE
//--------------------------------------------------------------------//
src = customclose
atrV = averagetruerange[atrLen](close)
isReady = barindex >= max(almaLen - 1 + slopeLen, max(devLen - 1, atrLen))
// ALMA
almaV = undefined
IF barindex >= almaLen - 1 THEN
   almaM = almaOffset * (almaLen - 1)
   almaS = almaLen / almaSigma
   wSum = 0
   cWt = 0
   FOR k = 0 TO almaLen - 1 DO
      wt = exp(0 - ((k - almaM) * (k - almaM)) / (2 * almaS * almaS))
      wSum = wSum + wt * src[almaLen - 1 - k]
      cWt = cWt + wt
   NEXT
   almaV = wSum / cWt
ENDIF
devV = std[devLen](src)
ONCE trend = 0
bullFlip = 0
bearFlip = 0
slopeScore = 0
conviction = 0
IF isReady THEN
   IF atrV > 0 THEN
      slopeScore = (almaV - almaV[slopeLen]) / atrV
      distScore = abs(close - almaV) / atrV
   ELSE
      slopeScore = 0
      distScore = 0
   ENDIF
   upperC = almaV + devV * devMult
   lowerC = almaV - devV * devMult
   bullSetup = slopeScore > slopeMin AND close > upperC
   bearSetup = slopeScore < 0 - slopeMin AND close < lowerC
   IF trend <> 1 AND bullSetup THEN
      trend = 1
      bullFlip = 1
   ELSIF trend <> -1 AND bearSetup THEN
      trend = -1
      bearFlip = 1
   ENDIF
   conviction = max(0, min(1, abs(slopeScore) * 2 + distScore * 0.35))
ENDIF
isFlip = bullFlip OR bearFlip
//--------------------------------------------------------------------//
// RIBBON
//--------------------------------------------------------------------//
IF trend = 1 THEN
   trR = bullR
   trG = bullG
   trB = bullB
ELSIF trend = -1 THEN
   trR = bearR
   trG = bearG
   trB = bearB
ELSE
   trR = neuR
   trG = neuG
   trB = neuB
ENDIF
IF isReady THEN
   IF trend = 1 THEN
      ribEdge = almaV - devV * 0.55
   ELSIF trend = -1 THEN
      ribEdge = almaV + devV * 0.55
   ELSE
      ribEdge = almaV
   ENDIF
   ribMid = (almaV + ribEdge) * 0.5
   ribAlma = almaV
ELSE
   ribEdge = undefined
   ribMid = undefined
   ribAlma = undefined
ENDIF
// the ribbon breaks at every new trend: hidden on the flip bar and the bar after it
// (a segment takes the colour of the bar it reaches)
IF showRibbon = 1 AND isReady AND (NOT isFlip) AND (NOT isFlip[1]) THEN
   ribVis = 1
ELSE
   ribVis = 0
ENDIF
edgeT = round(82 - conviction * 16)
coreT = round(50 - conviction * 35)
glowA = 36 * ribVis
edgeA = round((100 - edgeT) * 2.55) * ribVis
almaA = round((100 - coreT) * 2.55) * ribVis
fillA1 = 41 * ribVis
fillA2 = 18 * ribVis
COLORBETWEEN(ribEdge, ribMid, trR, trG, trB, fillA1)
COLORBETWEEN(ribMid, ribAlma, trR, trG, trB, fillA2)
//--------------------------------------------------------------------//
// TREND MARKERS (diamonds), stored to be redrawn on the last bar
//--------------------------------------------------------------------//
ONCE nFl = 0
IF isFlip THEN
   nFl = nFl + 1
   $flX[nFl] = barindex
   $flD[nFl] = trend
   IF trend = 1 THEN
      $flY[nFl] = low - atrV * 0.4
   ELSE
      $flY[nFl] = high + atrV * 0.4
   ENDIF
ENDIF
//--------------------------------------------------------------------//
// POSITIONS
//--------------------------------------------------------------------//
ONCE posN = 0
ONCE pEnded = 1
ONCE pStopped = 0
// freeze the current position into its slot when a new trend starts
IF isFlip AND posN > 0 THEN
   $sEB[posN] = pEB
   $sEP[posN] = pEP
   $sSP[posN] = pSP
   $sRD[posN] = pRD
   $sDir[posN] = pDir
   IF pEnded = 0 THEN
      $sEnd[posN] = barindex
      $sStp[posN] = 0
   ELSE
      $sEnd[posN] = pEndB
      $sStp[posN] = pStopped
   ENDIF
   $sH1[posN] = h1
   $sH2[posN] = h2
   $sH3[posN] = h3
   $sH4[posN] = h4
   pEnded = 1
ENDIF
// new position
IF isFlip THEN
   posN = posN + 1
   h1 = 0
   h2 = 0
   h3 = 0
   h4 = 0
   pEP = close
   pEB = barindex
   IF trend = 1 THEN
      pDir = 1
      structStop = lowest[stopLookback](low)[1]
      rawRisk = pEP - structStop
   ELSE
      pDir = -1
      structStop = highest[stopLookback](high)[1]
      rawRisk = structStop - pEP
   ENDIF
   pEnded = 0
   pStopped = 0
   minRisk = atrV * minStopAtr
   maxRisk = atrV * maxStopAtr
   pRD = min(max(rawRisk, minRisk), maxRisk)
   pSP = pEP - pDir * pRD
ENDIF
// active position: stop check, otherwise update the targets reached
IF posN > 0 AND pEnded = 0 THEN
   IF pDir = 1 THEN
      stoppedNow = barindex > pEB AND low <= pSP
   ELSE
      stoppedNow = barindex > pEB AND high >= pSP
   ENDIF
   IF stoppedNow THEN
      pEnded = 1
      pStopped = 1
      pEndB = barindex
   ELSE
      tg1 = pEP + pDir * pRD
      tg2 = pEP + pDir * pRD * 2
      tg3 = pEP + pDir * pRD * 3
      tg4 = pEP + pDir * pRD * 4
      IF pDir = 1 THEN
         IF high >= tg1 THEN
            h1 = 1
         ENDIF
         IF high >= tg2 THEN
            h2 = 1
         ENDIF
         IF high >= tg3 AND targetCount >= 3 THEN
            h3 = 1
         ENDIF
         IF high >= tg4 AND targetCount >= 4 THEN
            h4 = 1
         ENDIF
      ELSE
         IF low <= tg1 THEN
            h1 = 1
         ENDIF
         IF low <= tg2 THEN
            h2 = 1
         ENDIF
         IF low <= tg3 AND targetCount >= 3 THEN
            h3 = 1
         ENDIF
         IF low <= tg4 AND targetCount >= 4 THEN
            h4 = 1
         ENDIF
      ENDIF
   ENDIF
ENDIF
//--------------------------------------------------------------------//
// DRAWING (last bar)
//--------------------------------------------------------------------//
IF islastbarupdate THEN
   IF labelSize = 0 THEN
      lblSz = 8
   ELSIF labelSize = 1 THEN
      lblSz = 10
   ELSIF labelSize = 3 THEN
      lblSz = 15
   ELSIF labelSize = 4 THEN
      lblSz = 19
   ELSE
      lblSz = 12
   ENDIF
   // diamonds: the 500 most recent trend starts
   IF nFl > 0 THEN
      FOR kf = max(1, nFl - 499) TO nFl DO
         IF $flD[kf] = 1 THEN
            DRAWTEXT("♦", $flX[kf], $flY[kf], sansserif, bold, 12) COLOURED(bullR, bullG, bullB, 255)
         ELSE
            DRAWTEXT("♦", $flX[kf], $flY[kf], sansserif, bold, 12) COLOURED(bearR, bearG, bearB, 255)
         ENDIF
      NEXT
   ENDIF
   // positions: the last keepPositions, the current one from its live state
   IF posN > 0 THEN
      FOR kp = max(1, posN - keepPositions + 1) TO posN DO
         IF kp = posN THEN
            eB = pEB
            eP = pEP
            sP = pSP
            rD = pRD
            dirP = pDir
            q1 = h1
            q2 = h2
            q3 = h3
            q4 = h4
            IF pEnded = 0 THEN
               stP = 0
               xEnd = barindex + extendBars
               xLbl = barindex + extendBars
            ELSIF pStopped = 1 THEN
               stP = 2
               xEnd = pEndB
               xLbl = pEndB + 1
            ELSE
               stP = 1
               xEnd = barindex
               xLbl = barindex + 1
            ENDIF
         ELSE
            eB = $sEB[kp]
            eP = $sEP[kp]
            sP = $sSP[kp]
            rD = $sRD[kp]
            dirP = $sDir[kp]
            q1 = $sH1[kp]
            q2 = $sH2[kp]
            q3 = $sH3[kp]
            q4 = $sH4[kp]
            xEnd = $sEnd[kp]
            xLbl = $sEnd[kp] + 1
            IF $sStp[kp] = 1 THEN
               stP = 2
            ELSE
               stP = 1
            ENDIF
         ENDIF
         IF dirP = 1 THEN
            acR = bullR
            acG = bullG
            acB = bullB
         ELSE
            acR = bearR
            acG = bearG
            acB = bearB
         ENDIF
         // colours by state: 0 active, 1 closed by a new trend, 2 stopped
         IF stP = 0 THEN
            enGA = 61
            enCA = 230
            slGA = 66
            slCA = 250
            rbA = 15
            enTA = 214
            slTA = 199
         ELSIF stP = 1 THEN
            enGA = 31
            enCA = 128
            slGA = 31
            slCA = 133
            rbA = 8
            enTA = 107
            slTA = 89
         ELSE
            enGA = 31
            enCA = 128
            slGA = 133
            slCA = 242
            rbA = 33
            enTA = 107
            slTA = 224
         ENDIF
         // risk box, entry and stop lines
         DRAWRECTANGLE(eB, max(eP, sP), xEnd, min(eP, sP)) COLOURED(slR, slG, slB, 0) FILLCOLOR(slR, slG, slB, rbA)
         DRAWSEGMENT(eB, eP, xEnd, eP) COLOURED(acR, acG, acB, enGA) STYLE(line, 5)
         DRAWSEGMENT(eB, eP, xEnd, eP) COLOURED(coreR, coreG, coreB, enCA) STYLE(line, 1)
         DRAWSEGMENT(eB, sP, xEnd, sP) COLOURED(slR, slG, slB, slGA) STYLE(line, 5)
         IF stP = 2 THEN
            DRAWSEGMENT(eB, sP, xEnd, sP) COLOURED(coreR, coreG, coreB, slCA) STYLE(line, 2)
         ELSE
            DRAWSEGMENT(eB, sP, xEnd, sP) COLOURED(slR, slG, slB, slCA) STYLE(line, 2)
         ENDIF
         IF showLabels = 1 THEN
            IF dirP = 1 THEN
               DRAWTEXT("LONG", xLbl + 3, eP, sansserif, bold, lblSz) COLOURED(acR, acG, acB, enTA)
            ELSE
               DRAWTEXT("SHORT", xLbl + 3, eP, sansserif, bold, lblSz) COLOURED(acR, acG, acB, enTA)
            ENDIF
            IF stP = 2 THEN
               DRAWTEXT("SL  -1R  ✓", xLbl + 4, sP, sansserif, bold, lblSz) COLOURED(slR, slG, slB, slTA)
            ELSE
               DRAWTEXT("SL  -1R", xLbl + 3, sP, sansserif, bold, lblSz) COLOURED(slR, slG, slB, slTA)
            ENDIF
         ENDIF
         // targets
         prevLvl = eP
         zHalf = rD * zonePct
         FOR it = 1 TO targetCount DO
            tgP = eP + dirP * rD * it
            IF it = 1 THEN
               hitT = q1
            ELSIF it = 2 THEN
               hitT = q2
            ELSIF it = 3 THEN
               hitT = q3
            ELSE
               hitT = q4
            ENDIF
            IF stP = 0 THEN
               distT = abs(close - tgP)
               radT = max(rD * approachR, ticksize)
               proxT = max(0, min(1, 1 - distT / radT))
               IF hitT = 1 THEN
                  glowT = 60
                  tCoreT = 4
                  zoneT = 84
                  bandT = max(85, 90 - (it - 1) * 2)
               ELSE
                  glowT = round(95 - proxT * 30)
                  tCoreT = round(88 - proxT * 72)
                  zoneT = round(98 - proxT * 10)
                  bandT = round(97 - proxT * 5)
               ENDIF
               zBordT = max(25, zoneT - 12)
               IF hitT = 1 THEN
                  tagT = 14
               ELSE
                  tagT = max(38, tCoreT)
               ENDIF
            ELSIF stP = 1 THEN
               IF hitT = 1 THEN
                  glowT = 80
                  tCoreT = 38
                  zoneT = 93
                  bandT = 95
                  tagT = 52
                  zBordT = 72
               ELSE
                  glowT = 94
                  tCoreT = 72
                  zoneT = 98
                  bandT = 99
                  tagT = 76
                  zBordT = 92
               ENDIF
            ELSE
               IF hitT = 1 THEN
                  glowT = 80
                  tCoreT = 38
                  zoneT = 93
                  bandT = 95
                  tagT = 52
                  zBordT = 72
               ELSE
                  glowT = 95
                  tCoreT = 82
                  zoneT = 99
                  bandT = 99
                  tagT = 82
                  zBordT = 92
               ENDIF
            ENDIF
            glowA2 = round((100 - glowT) * 2.55)
            tCoreA = round((100 - tCoreT) * 2.55)
            zoneA = round((100 - zoneT) * 2.55)
            bandA = round((100 - bandT) * 2.55)
            zBordA = round((100 - zBordT) * 2.55)
            tagA = round((100 - tagT) * 2.55)
            DRAWRECTANGLE(eB, max(prevLvl, tgP), xEnd, min(prevLvl, tgP)) COLOURED(tpR, tpG, tpB, 0) FILLCOLOR(tpR, tpG, tpB, bandA)
            DRAWRECTANGLE(eB, tgP + zHalf, xEnd, tgP - zHalf) COLOURED(tpR, tpG, tpB, zBordA) FILLCOLOR(tpR, tpG, tpB, zoneA)
            DRAWSEGMENT(eB, tgP, xEnd, tgP) COLOURED(tpR, tpG, tpB, glowA2) STYLE(line, 5)
            IF hitT = 1 THEN
               DRAWSEGMENT(eB, tgP, xEnd, tgP) COLOURED(coreR, coreG, coreB, tCoreA) STYLE(line, 1)
            ELSE
               DRAWSEGMENT(eB, tgP, xEnd, tgP) COLOURED(tpR, tpG, tpB, tCoreA) STYLE(line, 1)
            ENDIF
            IF showLabels = 1 THEN
               IF it = 1 AND hitT = 1 THEN
                  DRAWTEXT("T1  1R  ✓", xLbl + 4, tgP, sansserif, bold, lblSz) COLOURED(tpR, tpG, tpB, tagA)
               ELSIF it = 1 THEN
                  DRAWTEXT("T1  1R", xLbl + 3, tgP, sansserif, bold, lblSz) COLOURED(tpR, tpG, tpB, tagA)
               ELSIF it = 2 AND hitT = 1 THEN
                  DRAWTEXT("T2  2R  ✓", xLbl + 4, tgP, sansserif, bold, lblSz) COLOURED(tpR, tpG, tpB, tagA)
               ELSIF it = 2 THEN
                  DRAWTEXT("T2  2R", xLbl + 3, tgP, sansserif, bold, lblSz) COLOURED(tpR, tpG, tpB, tagA)
               ELSIF it = 3 AND hitT = 1 THEN
                  DRAWTEXT("T3  3R  ✓", xLbl + 4, tgP, sansserif, bold, lblSz) COLOURED(tpR, tpG, tpB, tagA)
               ELSIF it = 3 THEN
                  DRAWTEXT("T3  3R", xLbl + 3, tgP, sansserif, bold, lblSz) COLOURED(tpR, tpG, tpB, tagA)
               ELSIF hitT = 1 THEN
                  DRAWTEXT("T4  4R  ✓", xLbl + 4, tgP, sansserif, bold, lblSz) COLOURED(tpR, tpG, tpB, tagA)
               ELSE
                  DRAWTEXT("T4  4R", xLbl + 3, tgP, sansserif, bold, lblSz) COLOURED(tpR, tpG, tpB, tagA)
               ENDIF
            ENDIF
            prevLvl = tgP
         NEXT
      NEXT
   ENDIF
ENDIF
//--------------------------------------------------------------------//
RETURN ribEdge COLOURED(trR, trG, trB, glowA) STYLE(line, 5) AS "Trend Glow", ribEdge COLOURED(trR, trG, trB, edgeA) STYLE(line, 3) AS "Trend Edge", ribMid COLOURED(trR, trG, trB, 0) STYLE(line, 1) AS "Trend Mid", ribAlma COLOURED(coreR, coreG, coreB, almaA) STYLE(line, 1) AS "ALMA"

Download
Filename: PRC_Trend-Target-Ribbon.itf
Downloads: 10
Iván González
Iván González Legend
Currently debugging life, so my bio is on hold. Check back after the next commit for an update.
Author’s Profile

Comments

ProRealCode ProRealCode
Loading...