//@version=6
// ============================================================================
// Smooth Trend Radar [WavesUnchained] — Strategy (standalone)
// Version: 3.4.5
// Author: WavesUnchained
// Build: 2026-10-12
// ============================================================================
// Double-smoothed Supertrend baseline for trend direction, with live baseline
// rejection signals at the baseline, statistical overextension highlighting,
// and automatic SL/TP level visualization on flips.
//
// Features:
// ✓ Supertrend midline double-smoothed with WMA + EMA — fewer whipsaws
// ✓ Auto Timeframe Scaling — parameters self-adjust per chart timeframe
// ✓ Optional volatility-adaptive band width — normalizes factor across instruments
// ✓ Trend direction from ATR-scaled baseline slope with hysteresis — filters micro-noise flips
// ✓ Baseline rejection with slope filter, touch validation,
// optional bar-polarity filter (Body or Pressure), and optional volume filter
// ✓ Rejections fire in both regimes, tagged Trend Pullback / Range Rejection
// ✓ Three SL methods: Flip Candle / Baseline ± ATR / Swing Structure
// ✓ Four TP modes: Pivot (R/S levels), R-Multiple, Fibonacci extension, ATR
// ✓ Pivot TP auto-fallback to ATR when prior session levels are out of range
// ✓ Re-Entry on rejection after SL hit (optional)
// ✓ Trail SL to break-even at TP1 (optional)
// ✓ Statistical overextension via percentile rank — candle coloring
// ✓ Risk/reward fill between entry–SL (red) and entry–TP3 (green)
// ✓ Hover tooltips on all signals — price, direction, timestamp
// ✓ Direction-aware alerts for flips, rejections, and TP/SL hits
//
// Data Contract:
// Price: REQUIRED chart symbol
// Volume: OPTIONAL real trade volume only — pivot-rejection volume filter degrades to pass-through
// OI: NO
// Reference: NO
// Verdict: CFD-degraded
//
// ============================================================================
strategy("Smooth Trend Radar [WavesUnchained] — Strategy", overlay=true,
max_labels_count=500,
default_qty_type=strategy.percent_of_equity, default_qty_value=10,
commission_type=strategy.commission.percent, commission_value=0.02,
slippage=1, max_bars_back=500)
// Synthetische Bar-Preise (Heikin Ashi, Renko …): Orders füllen zu Marktpreisen,
// der Backtest gilt dort nicht. Kurzer Hinweis unten rechts statt auf der Kerze.
var table chartTypeWarn = table.new(position.bottom_right, 1, 1)
if barstate.islast and not chart.is_standard
table.cell(chartTypeWarn, 0, 0, "STR Strategy · backtest invalid on this chart type",
bgcolor = color.new(color.red, 10), text_color = color.white, text_size = size.small)
// ─── User-defined types ─────────────────────────────────────────────────────
type bar
float o = open
float h = high
float l = low
float c = close
bar b = bar.new()
// Display flag for inputs — keeps the chart status line uncluttered
disp = display.all - display.status_line
// ─── Inputs ──────────────────────────────────────────────────────────────────
g_trend = "Trend"
autoScale = input.bool(true, "Auto Timeframe Scaling", group=g_trend, display=disp,
tooltip="Automatically selects Supertrend, WMA, and EMA periods for the active timeframe. Manual inputs below are ignored when enabled.")
stFactor = input.float(12, "Supertrend Factor", minval=1, step=0.5, group=g_trend, display=disp,
tooltip="ATR multiplier for the Supertrend bands. Higher = wider bands, fewer flips.")
stAtrPeriod = input.int(90, "Supertrend ATR Period", minval=1, group=g_trend, display=disp,
tooltip="ATR lookback for the Supertrend. Longer = smoother, more lagging bands.")
wmaLength = input.int(40, "WMA Length", minval=1, group=g_trend, display=disp,
tooltip="Weighted Moving Average applied to the Supertrend midline.")
emaLength = input.int(14, "EMA Length", minval=1, group=g_trend, display=disp,
tooltip="EMA applied to the WMA output — second smoothing pass.")
adxThreshold = input.float(25.0, "ADX Trend/Sideways Threshold", minval=5.0, maxval=50.0, step=1.0, group=g_trend, display=disp,
tooltip="ADX threshold: trend flips only fire above this value. Rejections fire in both regimes — the threshold only tags them as Trend Pullback (above) or Range Rejection (at or below).")
trendSlopeLookback = input.int(3, "Trend Slope Lookback", minval=1, group=g_trend, display=disp,
tooltip="Bars back to measure baseline slope for trend direction.")
trendSlopeThreshold = input.float(0.05, "Trend Slope Threshold (× ATR)", minval=0.0, step=0.01, group=g_trend, display=disp,
tooltip="Minimum baseline movement over the Slope Lookback, in ATR units, required to flip trend direction. Higher = fewer, more decisive flips; 0 = flips on any directional move (old crossover behavior).")
volAdaptive = input.bool(false, "Volatility-Adaptive Band Width", group=g_trend, display=disp,
tooltip="Scales the Supertrend factor by the instrument's own ATR percentile rank (200-bar lookback), so the same factor produces comparable relative band width across instruments. Off by default to preserve existing behavior.")
g_rej = "Rejection"
contFactor = input.int(2, "Min Baseline Touches", minval=1, maxval=5, group=g_rej, display=disp,
tooltip="Minimum number of bars touching the baseline in the pivot window. Filters out single-spike touches — requires sustained contact before the pivot.")
slopeLookback = input.int(5, "Slope Lookback", minval=1, group=g_rej, display=disp,
tooltip="Rejection only fires when the baseline is trending in the signal direction over this many bars. Filters out signals in flat/choppy zones.")
polarityMethod = input.string("Off", "Polarity Filter", options=["Off", "Body", "Pressure"], group=g_rej, display=disp,
tooltip="Extra filter at the pivot bar. Off: no filter. Body: pivot bar must be bullish/bearish (close > open). Pressure: close must be in the upper/lower half of the bar's range — more robust against doji bars and long wicks (e.g. NatGas).")
volumeFilter = input.string("Off", "Volume Filter", options=["Off", "Above Average"], group=g_rej, display=disp,
tooltip="Extra filter at the pivot bar. Off: no filter. Above Average: bar volume must exceed its SMA (Volume MA Length) — confirms rejections with real participation. Degrades to pass-through on instruments without real trade volume (tick volume or no volume in the feed).")
volumeMaLen = input.int(20, "Volume MA Length", minval=1, group=g_rej, display=disp,
tooltip="SMA length for the Volume Filter. Ignored when Volume Filter is Off.")
g_overext = "Overextension"
overextLookback = input.int(100, "Lookback", minval=20, group=g_overext, display=disp,
tooltip="How far back to compare current distance from baseline. Self-adapts to symbol/timeframe characteristics.")
overextThreshold = input.float(85, "Percentile Threshold", minval=50, maxval=99, step=1, group=g_overext, display=disp,
tooltip="Bars where the distance from baseline is in the top X% of the lookback window are flagged as overextended. 85 = top 15%, 95 = top 5%.")
overextBullCol = input.color(#9c27b0, "Bull-Side Overext (price below baseline)", group=g_overext, display=disp,
tooltip="Color used when price is overextended below the baseline — potential reversal up.")
overextBearCol = input.color(#ff6f00, "Bear-Side Overext (price above baseline)", group=g_overext, display=disp,
tooltip="Color used when price is overextended above the baseline — potential reversal down.")
g_tp = "Targets"
showTargets = input.bool(true, "Show SL / TP Levels", group=g_tp, display=disp)
tpMode = input.string("Pivot", "TP Method",
options=["Pivot", "R-Multiple", "Fibonacci", "ATR"], group=g_tp, display=disp,
tooltip="Pivot: nearest R/S pivot levels. R-Multiple: multiples of SL distance. Fibonacci: 0.618 / 1.0 / 1.618 of setup swing. ATR: multiples of long ATR.")
slMode = input.string("Flip Candle", "SL Method",
options=["Flip Candle", "Baseline", "Swing"], group=g_tp, display=disp,
tooltip="Flip Candle: low/high of the flip bar ± ATR. Baseline: smoothed baseline ± ATR. Swing: lowest low / highest high within lookback ± ATR.")
swingLookback = input.int(20, "Swing Lookback (bars)", minval=5, group=g_tp, display=disp,
tooltip="SL Method = Swing: bars to look back for swing low/high. Also used as the setup-swing lookback for TP Method = Fibonacci.")
atrPeriod = input.int(14, "ATR Period", minval=1, group=g_tp, display=disp)
slMultiplier = input.float(2.0, "SL ATR Multiplier", minval=0.1, step=0.1, group=g_tp, display=disp,
tooltip="ATR distance added to the SL reference point.")
pivotPeriod = input.string("D", "Pivot Period",
options=["D", "W", "M"], group=g_tp, display=disp,
tooltip="Timeframe for pivot calculation. Used with TP Method = Pivot.")
pivotFormula = input.string("Camarilla", "Pivot Formula",
options=["Classic", "Camarilla"], group=g_tp, display=disp,
tooltip="Classic (Floor Trader): PP = (H+L+C)/3. Camarilla: tighter R/S bands scaled from close. Used with TP Method = Pivot.")
tp1Multiplier = input.float(3.0, "TP1 Multiplier", minval=0.1, step=0.1, group=g_tp, display=disp,
tooltip="R-Multiple: × SL distance. ATR: × long ATR. Not used for Pivot or Fibonacci.")
tp2Multiplier = input.float(5.0, "TP2 Multiplier", minval=0.1, step=0.1, group=g_tp, display=disp)
tp3Multiplier = input.float(7.0, "TP3 Multiplier", minval=0.1, step=0.1, group=g_tp, display=disp)
atrLongPeriod = input.int(50, "ATR Long Period", minval=1, group=g_tp, display=disp,
tooltip="ATR lookback for TP Method = ATR. Longer period = more stable volatility estimate.")
rejReentry = input.bool(true, "Re-Entry on Rejection after SL Hit", group=g_tp, display=disp,
tooltip="When the SL is hit but the trend direction is unchanged, the next confirmed rejection signal rebuilds the Entry/SL/TP setup at the current price.")
trailBE = input.bool(false, "Trail SL to Break-Even at TP1", group=g_tp, display=disp,
tooltip="When TP1 is reached, the SL moves to the entry price. The trade can no longer result in a loss.")
g_col = "Colors"
bullCol = input.color(#26a69a, "Bullish", group=g_col, display=disp)
bearCol = input.color(#ef5350, "Bearish", group=g_col, display=disp)
flipSizeStr = input.string("Small", "Flip Label Size", options=["Tiny","Small","Normal"], group=g_col, display=disp)
rejSizeStr = input.string("Tiny", "Rejection Label Size", options=["Tiny","Small","Normal"], group=g_col, display=disp)
// String → size helper
f_gTS(string _t) =>
switch _t
"Tiny" => size.tiny
"Small" => size.small
"Normal" => size.normal
=> size.small
flipSize = f_gTS(flipSizeStr)
rejSize = f_gTS(rejSizeStr)
g_alert = "Alerts"
awaitConfirm = input.bool(true, "Await Bar Confirmation", group=g_alert, display=disp)
// ─── Timeframe-scaled parameters ─────────────────────────────────────────────
_tf = timeframe.period
_efFactor = autoScale ? (_tf == "1" ? 4.0 : _tf == "5" ? 5.0 : _tf == "15" ? 6.0 : _tf == "30" ? 7.0 : _tf == "60" ? 5.0 : _tf == "240" ? 10.0 : timeframe.isdaily ? 12.0 : timeframe.isweekly ? 14.0 : stFactor) : stFactor
_efAtrPeriod = autoScale ? (_tf == "1" ? 10 : _tf == "5" ? 14 : _tf == "15" ? 20 : _tf == "30" ? 25 : _tf == "60" ? 14 : _tf == "240" ? 50 : timeframe.isdaily ? 90 : timeframe.isweekly ? 120 : stAtrPeriod) : stAtrPeriod
_efWma = autoScale ? (_tf == "1" ? 8 : _tf == "5" ? 10 : _tf == "15" ? 15 : _tf == "30" ? 18 : _tf == "60" ? 10 : _tf == "240" ? 30 : timeframe.isdaily ? 40 : timeframe.isweekly ? 50 : wmaLength) : wmaLength
_efEma = autoScale ? (_tf == "1" ? 3 : _tf == "5" ? 4 : _tf == "15" ? 5 : _tf == "30" ? 6 : _tf == "60" ? 3 : _tf == "240" ? 10 : timeframe.isdaily ? 14 : timeframe.isweekly ? 18 : emaLength) : emaLength
_efPivBars = autoScale ? (_tf == "1" ? 2 : _tf == "5" ? 2 : _tf == "15" ? 2 : _tf == "30" ? 2 : _tf == "60" ? 2 : _tf == "240" ? 3 : timeframe.isdaily ? 5 : timeframe.isweekly ? 4 : 2) : 2
// ─── Supertrend midline ───────────────────────────────────────────────────────
_supertrend(factor, atrPeriod) =>
atr = ta.atr(atrPeriod)
upper = hl2 + factor * atr
lower = hl2 - factor * atr
prevLower = nz(lower[1])
prevUpper = nz(upper[1])
lower := lower > prevLower or close[1] < prevLower ? lower : prevLower
upper := upper < prevUpper or close[1] > prevUpper ? upper : prevUpper
[lower, upper, atr]
// Volatility-adaptive band width: scale the factor by where current ATR ranks
// within its own recent history, so the same factor yields comparable relative
// band width across instruments. Off by default — preserves prior behavior.
_volPctRank = ta.percentrank(ta.atr(_efAtrPeriod), 200)
_efFactorAdj = volAdaptive ? _efFactor * (0.85 + 0.3 * _volPctRank / 100) : _efFactor
[lwr, upr, _stAtr] = _supertrend(_efFactorAdj, _efAtrPeriod)
tL = ta.ema(ta.wma(math.avg(lwr, upr), _efWma), _efEma)
// ─── Trend direction ─────────────────────────────────────────────────────────
// Baseline slope over trendSlopeLookback bars, scaled to ATR units. Trend only
// flips once the move exceeds trendSlopeThreshold — holds previous value
// otherwise (hysteresis), filtering micro-noise crosses.
_trendSlope = tL - tL[trendSlopeLookback]
var int trend = 0
trend := _trendSlope > _stAtr * trendSlopeThreshold ? 1 : _trendSlope < -_stAtr * trendSlopeThreshold ? -1 : trend
// ADX Trend/Sideways Regime detection
[diPlus, diMinus, adx] = ta.dmi(14, 14)
isTrending = not na(adx) and adx > adxThreshold
isSideways = not na(adx) and adx <= adxThreshold
longSignal = trend == 1 and trend[1] != 1 and isTrending
shortSignal = trend == -1 and trend[1] != -1 and isTrending
// ─── Rejection detection (live baseline touch validation) ───────────────────
// Pivots are intentionally not used for signal timing. Rejection is detected
// from the current bar plus recent baseline contact.
var int _bullTouches = 0
var int _bearTouches = 0
_bullTouches := 0
_bearTouches := 0
for i = 0 to contFactor - 1
if trend[i] == 1 and low[i] <= tL[i]
_bullTouches += 1
if trend[i] == -1 and high[i] >= tL[i]
_bearTouches += 1
bool _polBull = polarityMethod == "Off" or (polarityMethod == "Body" ? close > open : (close - low) > (high - close))
bool _polBear = polarityMethod == "Off" or (polarityMethod == "Body" ? close < open : (close - low) < (high - close))
bool volumeIsReal = (ta.cum(nz(volume)) > 0 and syminfo.volumetype != "tick") and not na(volume)
bool _volOk = volumeFilter == "Off" or not volumeIsReal or volume > ta.sma(volume, volumeMaLen)
// Rejections fire in both ADX regimes — a baseline pullback in an active trend
// is as valid a signal as one in a range. The regime is carried as a label tag
// (Trend Pullback vs Range Rejection) rather than used to gate the signal.
rejSignalBull = low <= tL and close > tL and trend == 1 and tL > tL[slopeLookback] and _bullTouches >= contFactor and _polBull and _volOk
rejSignalBear = high >= tL and close < tL and trend == -1 and tL < tL[slopeLookback] and _bearTouches >= contFactor and _polBear and _volOk
rejRegime = isTrending ? "Trend Pullback" : "Range Rejection"
// ─── SL / TP levels ──────────────────────────────────────────────────────────
volatility = ta.atr(atrPeriod)
atr_long = ta.atr(atrLongPeriod)
// Pivot data — previous completed period, no repainting
[piv_h, piv_l, piv_c] = request.security(syminfo.tickerid, pivotPeriod,
[high[1], low[1], close[1]], lookahead=barmerge.lookahead_on)
piv_range = piv_h - piv_l
// Classic (Floor Trader)
pp_c = (piv_h + piv_l + piv_c) / 3
r1_c = 2 * pp_c - piv_l
r2_c = pp_c + piv_range
r3_c = piv_h + 2 * (pp_c - piv_l)
s1_c = 2 * pp_c - piv_h
s2_c = pp_c - piv_range
s3_c = piv_l - 2 * (piv_h - pp_c)
// Camarilla
r1_ca = piv_c + piv_range * 1.1 / 12
r2_ca = piv_c + piv_range * 1.1 / 6
r3_ca = piv_c + piv_range * 1.1 / 4
s1_ca = piv_c - piv_range * 1.1 / 12
s2_ca = piv_c - piv_range * 1.1 / 6
s3_ca = piv_c - piv_range * 1.1 / 4
var float SL = na
var float TP1_lvl = na
var float TP2_lvl = na
var float TP3_lvl = na
var int tradeDir = 0
var bool tp1Done = false
var bool tp2Done = false
var bool tp3Done = false
var bool slDone = false
// The strategy stop fills on wicks while slDone tracks close-crosses; latch real stop fills
// so a wick stop-out still opens the rejection re-entry
var bool stopFilled = false
if strategy.closedtrades > nz(strategy.closedtrades[1]) and strategy.closedtrades.exit_comment(strategy.closedtrades - 1) == "sl"
stopFilled := true
var line entry_line = na
var line sl_line = na
var line tp1_line = na
var line tp2_line = na
var line tp3_line = na
var label entry_label = na
var label sl_label = na
var label tp1_label = na
var label tp2_label = na
var label tp3_label = na
var linefill lf_risk = na
var linefill lf_reward = na
var float entryPrice = na
bullRejRe = rejReentry and (slDone or stopFilled) and tradeDir == 1 and trend == 1 and rejSignalBull
bearRejRe = rejReentry and (slDone or stopFilled) and tradeDir == -1 and trend == -1 and rejSignalBear
// SL/TP calc always runs — strategy entries below depend on it regardless of
// whether the visual targets (showTargets) are switched on.
if longSignal or shortSignal or bullRejRe or bearRejRe
isBull = longSignal or bullRejRe
col = isBull ? bullCol : bearCol
tradeDir := isBull ? 1 : -1
entryPrice := b.c
tp1Done := false
tp2Done := false
tp3Done := false
slDone := false
stopFilled := false
// SL method selection
float rawSL = na
if slMode == "Baseline"
rawSL := isBull ? tL - volatility * slMultiplier : tL + volatility * slMultiplier
else if slMode == "Swing"
rawSL := isBull ? ta.lowest(low, swingLookback) - volatility * slMultiplier : ta.highest(high, swingLookback) + volatility * slMultiplier
else // "Flip Candle"
rawSL := isBull ? b.l - volatility * slMultiplier : b.h + volatility * slMultiplier
// Clamp SL to the correct side of entry
SL := isBull ? math.min(rawSL, b.c - syminfo.mintick) : math.max(rawSL, b.c + syminfo.mintick)
// TP levels by mode
if tpMode == "Pivot"
TP1_lvl := isBull ? (pivotFormula == "Classic" ? r1_c : r1_ca) : (pivotFormula == "Classic" ? s1_c : s1_ca)
TP2_lvl := isBull ? (pivotFormula == "Classic" ? r2_c : r2_ca) : (pivotFormula == "Classic" ? s2_c : s2_ca)
TP3_lvl := isBull ? (pivotFormula == "Classic" ? r3_c : r3_ca) : (pivotFormula == "Classic" ? s3_c : s3_ca)
// Pivot levels can land on the wrong side of entry when price has moved far from
// the prior session's range (e.g. price below all S levels for a short, or above
// all R levels for a long). Fall back to ATR in that case.
if (isBull and TP1_lvl <= close) or (not isBull and TP1_lvl >= close)
TP1_lvl := isBull ? close + atr_long * tp1Multiplier : close - atr_long * tp1Multiplier
TP2_lvl := isBull ? close + atr_long * tp2Multiplier : close - atr_long * tp2Multiplier
TP3_lvl := isBull ? close + atr_long * tp3Multiplier : close - atr_long * tp3Multiplier
else if tpMode == "R-Multiple"
slDist = math.abs(close - SL)
TP1_lvl := isBull ? close + slDist * tp1Multiplier : close - slDist * tp1Multiplier
TP2_lvl := isBull ? close + slDist * tp2Multiplier : close - slDist * tp2Multiplier
TP3_lvl := isBull ? close + slDist * tp3Multiplier : close - slDist * tp3Multiplier
else if tpMode == "Fibonacci"
swing = isBull ? ta.lowest(low, swingLookback) : ta.highest(high, swingLookback)
fibRange = math.abs(close - swing)
TP1_lvl := isBull ? close + fibRange * 0.618 : close - fibRange * 0.618
TP2_lvl := isBull ? close + fibRange * 1.0 : close - fibRange * 1.0
TP3_lvl := isBull ? close + fibRange * 1.618 : close - fibRange * 1.618
else // ATR
TP1_lvl := isBull ? close + atr_long * tp1Multiplier : close - atr_long * tp1Multiplier
TP2_lvl := isBull ? close + atr_long * tp2Multiplier : close - atr_long * tp2Multiplier
TP3_lvl := isBull ? close + atr_long * tp3Multiplier : close - atr_long * tp3Multiplier
if showTargets
line.delete(entry_line)
line.delete(sl_line)
line.delete(tp1_line)
line.delete(tp2_line)
line.delete(tp3_line)
label.delete(entry_label)
label.delete(sl_label)
label.delete(tp1_label)
label.delete(tp2_label)
label.delete(tp3_label)
linefill.delete(lf_risk)
linefill.delete(lf_reward)
x2 = bar_index + 40
entry_line := line.new(bar_index, close, x2, close, color=col, width=2)
sl_line := line.new(bar_index, SL, x2, SL, color=color.new(bearCol, 50), width=1)
tp1_line := line.new(bar_index, TP1_lvl, x2, TP1_lvl, color=color.new(col, 60), width=1)
tp2_line := line.new(bar_index, TP2_lvl, x2, TP2_lvl, color=color.new(col, 40), width=1)
tp3_line := line.new(bar_index, TP3_lvl, x2, TP3_lvl, color=col, width=1)
entry_label := label.new(x2, close, "Entry " + str.tostring(close, format.mintick), style=label.style_label_left, color=col, textcolor=color.white, size=size.small)
sl_label := label.new(x2, SL, "SL " + str.tostring(SL, format.mintick), style=label.style_label_left, color=color.new(bearCol, 50), textcolor=color.white, size=size.small)
tp1_label := label.new(x2, TP1_lvl, "TP1 " + str.tostring(TP1_lvl, format.mintick), style=label.style_label_left, color=color.new(col, 60), textcolor=color.white, size=size.small)
tp2_label := label.new(x2, TP2_lvl, "TP2 " + str.tostring(TP2_lvl, format.mintick), style=label.style_label_left, color=color.new(col, 40), textcolor=color.white, size=size.small)
tp3_label := label.new(x2, TP3_lvl, "TP3 " + str.tostring(TP3_lvl, format.mintick), style=label.style_label_left, color=col, textcolor=color.white, size=size.small)
lf_risk := linefill.new(entry_line, sl_line, color.new(bearCol, 85))
lf_reward := linefill.new(entry_line, tp3_line, color.new(bullCol, 85))
else if showTargets and not na(entry_line)
x2 = bar_index + 40
line.set_x2(entry_line, x2)
line.set_x2(sl_line, x2)
line.set_x2(tp1_line, x2)
line.set_x2(tp2_line, x2)
line.set_x2(tp3_line, x2)
label.set_x(entry_label, x2)
label.set_x(sl_label, x2)
label.set_x(tp1_label, x2)
label.set_x(tp2_label, x2)
label.set_x(tp3_label, x2)
// ─── Overextension (statistical) ─────────────────────────────────────────────
// Compares the current distance from the baseline against historical distances
// over the lookback window. A bar is "overextended" when its distance is in the
// top X% of all distances seen — self-adapts to symbol/timeframe.
_distSigned = close - tL
_distAbs = math.abs(_distSigned)
_distPctRank = ta.percentrank(_distAbs, overextLookback)
isOverextBull = _distPctRank > overextThreshold and _distSigned < 0
isOverextBear = _distPctRank > overextThreshold and _distSigned > 0
// ─── Plots ───────────────────────────────────────────────────────────────────
plot(tL, "Baseline", color=trend == 1 ? color.new(bullCol, 50) : color.new(bearCol, 50), linewidth=2)
if longSignal
label.new(bar_index, tL, "▲",
style=label.style_label_up, color=bullCol, textcolor=color.white, size=flipSize,
tooltip="Bull Flip\nPrice: " + str.tostring(close, format.mintick) + "\n" + str.format_time(time, "yyyy-MM-dd HH:mm", syminfo.timezone))
if shortSignal
label.new(bar_index, tL, "▼",
style=label.style_label_down, color=bearCol, textcolor=color.white, size=flipSize,
tooltip="Bear Flip\nPrice: " + str.tostring(close, format.mintick) + "\n" + str.format_time(time, "yyyy-MM-dd HH:mm", syminfo.timezone))
if rejSignalBull
label.new(bar_index, low, "",
style=label.style_triangleup, color=color.new(bullCol, 30), size=rejSize,
tooltip="Bull Rejection (" + rejRegime + ")\nLow: " + str.tostring(low, format.mintick) + "\n" + str.format_time(time, "yyyy-MM-dd HH:mm", syminfo.timezone))
if rejSignalBear
label.new(bar_index, high, "",
style=label.style_triangledown, color=color.new(bearCol, 30), size=rejSize,
tooltip="Bear Rejection (" + rejRegime + ")\nHigh: " + str.tostring(high, format.mintick) + "\n" + str.format_time(time, "yyyy-MM-dd HH:mm", syminfo.timezone))
// Gradient-based overextension intensity: from threshold (faded) to 100% (full)
_overextBullGrad = color.from_gradient(_distPctRank, overextThreshold, 100.0, color.new(overextBullCol, 60), color.new(overextBullCol, 0))
_overextBearGrad = color.from_gradient(_distPctRank, overextThreshold, 100.0, color.new(overextBearCol, 60), color.new(overextBearCol, 0))
_barCol = isOverextBull ? _overextBullGrad : isOverextBear ? _overextBearGrad : trend == 1 ? color.new(bullCol, 60) : color.new(bearCol, 60)
barcolor(_barCol)
// ─── Alerts ──────────────────────────────────────────────────────────────────
await = awaitConfirm ? barstate.isconfirmed : true
tp1Hit = not tp1Done and tradeDir != 0 and (tradeDir == 1 ? ta.crossover(close, TP1_lvl) : ta.crossunder(close, TP1_lvl))
tp2Hit = not tp2Done and tradeDir != 0 and (tradeDir == 1 ? ta.crossover(close, TP2_lvl) : ta.crossunder(close, TP2_lvl))
tp3Hit = not tp3Done and tradeDir != 0 and (tradeDir == 1 ? ta.crossover(close, TP3_lvl) : ta.crossunder(close, TP3_lvl))
slHit = not slDone and tradeDir != 0 and (tradeDir == 1 ? ta.crossunder(close, SL) : ta.crossover(close, SL))
if tp1Hit
tp1Done := true
if trailBE and not na(sl_line)
SL := entryPrice
line.set_y1(sl_line, SL)
line.set_y2(sl_line, SL)
label.set_y(sl_label, SL)
label.set_text(sl_label, "BE " + str.tostring(SL, format.mintick))
linefill.delete(lf_risk)
lf_risk := na
if tp2Hit
tp2Done := true
if tp3Hit
tp3Done := true
if slHit
slDone := true
tp1Done := true
tp2Done := true
tp3Done := true
line.delete(entry_line)
line.delete(sl_line)
line.delete(tp1_line)
line.delete(tp2_line)
line.delete(tp3_line)
label.delete(entry_label)
label.delete(sl_label)
label.delete(tp1_label)
label.delete(tp2_label)
label.delete(tp3_label)
linefill.delete(lf_risk)
linefill.delete(lf_reward)
entry_line := na
_ticker = syminfo.tickerid + " (" + timeframe.period + ")"
if longSignal and await
alert("Smooth Trend Radar Buy — " + _ticker, alert.freq_once_per_bar_close)
if shortSignal and await
alert("Smooth Trend Radar Sell — " + _ticker, alert.freq_once_per_bar_close)
// ─── Strategy inputs ─────────────────────────────────────────────────────────
g_strat = "Strategy"
tradeDirInput = input.string("Both", "Trade Direction",
options=["Both", "Long Only", "Short Only"], group=g_strat,
tooltip="Filter entries by direction. Useful for asymmetric instruments.")
confirmClose = input.bool(true, "Entries on Confirmed Bar Only", group=g_strat,
tooltip="Only trigger entries on closed bars. Prevents repainting in live trading.")
cooldownBars = input.int(0, "Cooldown Bars After Exit", minval=0, group=g_strat,
tooltip="Block new entries for N bars after a position closes. Set 0 to disable.")
enableBE = input.bool(false, "Break-Even Stop", group=g_strat,
tooltip="Move SL to entry price once the trade is N×ATR in profit.")
beATRInput = input.float(1.0, "Break-Even Trigger (ATR×)", minval=0.1, step=0.1, group=g_strat,
tooltip="ATR distance from entry that triggers the break-even move.")
g_filters = "Filters"
useSession = input.bool(false, "Session Filter", group=g_filters)
sessionInput = input.session("0000-2345", "Session", group=g_filters)
useDateRange = input.bool(false, "Date Range", group=g_filters)
dateFrom = input.time(timestamp("01 Jan 2020 00:00 +0000"), "From", group=g_filters)
dateTo = input.time(timestamp("31 Dec 2030 23:59 +0000"), "To", group=g_filters)
useMaxDD = input.bool(false, "Max Drawdown", group=g_filters)
maxDD = input.float(20.0, "Max Drawdown (%)", minval=1, step=1, group=g_filters)
useMaxIntraDD = input.bool(false, "Max Intraday Loss", group=g_filters)
maxIntraDD = input.float(5.0, "Max Intraday Loss (%)", minval=1, step=1, group=g_filters)
useLossStreak = input.bool(false, "Losing Streak Limit", group=g_filters)
maxLossStreak = input.int(5, "Max Consecutive Losses", minval=1, group=g_filters)
strategy.risk.max_drawdown(useMaxDD ? maxDD : 100, strategy.percent_of_equity)
strategy.risk.max_intraday_loss(useMaxIntraDD ? maxIntraDD : 100, strategy.percent_of_equity)
var int _genLossStreak = 0
if strategy.losstrades > strategy.losstrades[1]
_genLossStreak := _genLossStreak + 1
if strategy.wintrades > strategy.wintrades[1]
_genLossStreak := 0
_genFilterOk = (not useSession or not na(time(timeframe.period, sessionInput))) and
(not useDateRange or (time >= dateFrom and time <= dateTo)) and
(not useLossStreak or _genLossStreak < maxLossStreak)
var int _lastExitBar = na
_justClosed = strategy.closedtrades > 0 and strategy.closedtrades.exit_bar_index(strategy.closedtrades - 1) == bar_index
if _justClosed
_lastExitBar := bar_index
_inCooldown = cooldownBars > 0 and not na(_lastExitBar) and (bar_index - _lastExitBar) < cooldownBars
_canEntry = not _inCooldown and (not confirmClose or barstate.isconfirmed) and _genFilterOk
exitLvlInput = input.string("TP1", "Exit at TP",
options=["TP1", "TP2", "TP3", "None (next signal)"], group=g_strat,
tooltip="TP level for the exit limit order. 'None' holds until the next opposing signal.")
// ─── Strategy execution ───────────────────────────────────────────────────────
_canLong = tradeDirInput != "Short Only"
_canShort = tradeDirInput != "Long Only"
var float _exitTP = na
_doLong = (longSignal or bullRejRe) and _canLong and _canEntry
_doShort = (shortSignal or bearRejRe) and _canShort and _canEntry
if _doLong
_exitTP := exitLvlInput == "TP1" ? TP1_lvl :
exitLvlInput == "TP2" ? TP2_lvl :
exitLvlInput == "TP3" ? TP3_lvl : na
strategy.entry("Long", strategy.long)
if _doShort
_exitTP := exitLvlInput == "TP1" ? TP1_lvl :
exitLvlInput == "TP2" ? TP2_lvl :
exitLvlInput == "TP3" ? TP3_lvl : na
strategy.entry("Short", strategy.short)
// ─── Break-even management (merged into the single exit stop below — a second
// strategy.exit() call on the same entry would otherwise compete for the
// already-reserved exit quantity) ────────────────────────────────────────────
if enableBE
_beAtr = ta.atr(14) * beATRInput
if strategy.position_size > 0 and close >= strategy.position_avg_price + _beAtr
SL := math.max(SL, strategy.position_avg_price)
if strategy.position_size < 0 and close <= strategy.position_avg_price - _beAtr
SL := math.min(SL, strategy.position_avg_price)
if strategy.position_size > 0
strategy.exit("Long Exit", "Long", stop=SL, limit=_exitTP, comment_loss="sl")
if strategy.position_size < 0
strategy.exit("Short Exit", "Short", stop=SL, limit=_exitTP, comment_loss="sl")
Overview
Smooth Trend Radar Strategy plots baseline rejection signals on flips, trend slope filters, and statistical overextension highlights with candle coloring. The strategy enters on confirmed rejections, exits using selected SL or TP modes including pivot levels, R-multiple, Fibonacci, or ATR-based stops, and applies position sizing with optional re-entry and trail stops. Auto Timeframe Scaling and volatility-adaptive bands adjust parameters. It draws all signals and levels on the price chart. Notable limits include invalid backtesting on non-standard chart types.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Auto Timeframe Scaling
bool
true
autoScale
Supertrend Factor
float
12
stFactor
Supertrend ATR Period
int
90
stAtrPeriod
WMA Length
int
40
wmaLength
EMA Length
int
14
emaLength
ADX Trend/Sideways Threshold
float
25.0
adxThreshold
Trend Slope Lookback
int
3
trendSlopeLookback
Trend Slope Threshold (× ATR)
float
0.05
trendSlopeThreshold
Volatility-Adaptive Band Width
bool
false
volAdaptive
Min Baseline Touches
int
2
contFactor
Slope Lookback
int
5
slopeLookback
Polarity Filter
string
Off
polarityMethod
Show all 40 settings
Setting
Type
Default
Variable
Volume Filter
string
Off
volumeFilter
Volume MA Length
int
20
volumeMaLen
Lookback
int
100
overextLookback
Percentile Threshold
float
85
overextThreshold
Bull-Side Overext (price below baseline)
color
#9c27b0
overextBullCol
Bear-Side Overext (price above baseline)
color
#ff6f00
overextBearCol
Show SL / TP Levels
bool
true
showTargets
TP Method
string
Pivot
tpMode
SL Method
string
Flip Candle
slMode
Swing Lookback (bars)
int
20
swingLookback
ATR Period
int
14
atrPeriod
SL ATR Multiplier
float
2.0
slMultiplier
Pivot Period
string
D
pivotPeriod
Pivot Formula
string
Camarilla
pivotFormula
TP1 Multiplier
float
3.0
tp1Multiplier
TP2 Multiplier
float
5.0
tp2Multiplier
TP3 Multiplier
float
7.0
tp3Multiplier
ATR Long Period
int
50
atrLongPeriod
Re-Entry on Rejection after SL Hit
bool
true
rejReentry
Trail SL to Break-Even at TP1
bool
false
trailBE
Bullish
color
#26a69a
bullCol
Bearish
color
#ef5350
bearCol
Flip Label Size
string
Small
flipSizeStr
Rejection Label Size
string
Tiny
rejSizeStr
Await Bar Confirmation
bool
true
awaitConfirm
Trade Direction
string
Both
tradeDirInput
Entries on Confirmed Bar Only
bool
true
confirmClose
Cooldown Bars After Exit
int
0
cooldownBars
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
This strategy combines moving averages, ATR volatility bands and ADX trend strength on the price chart. It calculates ADX from price data and adjusts the ATR multiplier dynamically based on whether ADX is rising or falling. Long and short entries are controlle
The AI SuperTrend x Pivot Percentile strategy combines SuperTrend trend following with KNN-based AI predictions, Pivot Percentile levels, and ADX/DMI filtering. SuperTrend indicators run on multiple higher timeframes with configurable lengths and factors, whil
ALGOX is a Pine Script v5 strategy that overlays SuperTrend, ATR bands, Heikin Ashi candles, EMA ribbons and an RSI table on the price chart. It generates long and short entries based on signal sensitivity settings with optional lower-timeframe or higher-timef
This strategy calculates the Supertrend using ATR period and factor inputs, then plots green lines for up direction and red lines for down direction on the price chart. It signals long entries when Supertrend changes to positive and short entries when changing
This strategy plots the Trend EMA and Deep Terrace levels on the price chart to identify rising trends. It signals long pyramid entries in staged terraces when price crosses above the first, second, or third ATR-based levels during an upward trend, with up to
This Pine Script strategy plots pivot points and a SuperTrend-style trailing line based on ATR and center line derived from pivots. It calculates trend direction and generates buy and sell signals on trend reversals. Signals are filtered by a user-selected mov