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.
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:
The bearish trend is the mirror image. Once a trend is confirmed, it stays until the opposite setup appears.
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.
At each flip, a position is opened on the close:
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.
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.
The source price is chosen in the indicator settings (by default the close).
//---------------------------------------------------------------
// 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"