Strategy · Pine Script v6 · On the chart

Smooth Trend Radar Strategy

by casoon

Smooth Trend Radar Strategy calculates a double-smoothed Supertrend baseline using WMA and EMA to identify trend direction and sideway regimes.

View on GitHub

GitHub stars9on the repository
Pine versionv6current
Lines of code543
Settings40inputs
LicenceMIT
Last updateyesterday27 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

543 lines of Pine Script v6 from strategies/smooth_trend_radar/smooth_trend_radar_strategy.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

smooth_trend_radar_strategy.pine
//@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.

Details

Published by

casoon on GitHub

Type

Strategy, Pine Script v6

Draws

On the price chart

Category

Trend, Moving averages, Volatility & bands, Support & resistance

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Topics

pine-scriptpinescripttechnical-analysistrading-indicatorstrading-strategiestradingview

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Auto Timeframe ScalingbooltrueautoScale
Supertrend Factorfloat12stFactor
Supertrend ATR Periodint90stAtrPeriod
WMA Lengthint40wmaLength
EMA Lengthint14emaLength
ADX Trend/Sideways Thresholdfloat25.0adxThreshold
Trend Slope Lookbackint3trendSlopeLookback
Trend Slope Threshold (× ATR)float0.05trendSlopeThreshold
Volatility-Adaptive Band WidthboolfalsevolAdaptive
Min Baseline Touchesint2contFactor
Slope Lookbackint5slopeLookback
Polarity FilterstringOffpolarityMethod
Show all 40 settings
SettingTypeDefaultVariable
Volume FilterstringOffvolumeFilter
Volume MA Lengthint20volumeMaLen
Lookbackint100overextLookback
Percentile Thresholdfloat85overextThreshold
Bull-Side Overext (price below baseline)color#9c27b0overextBullCol
Bear-Side Overext (price above baseline)color#ff6f00overextBearCol
Show SL / TP LevelsbooltrueshowTargets
TP MethodstringPivottpMode
SL MethodstringFlip CandleslMode
Swing Lookback (bars)int20swingLookback
ATR Periodint14atrPeriod
SL ATR Multiplierfloat2.0slMultiplier
Pivot PeriodstringDpivotPeriod
Pivot FormulastringCamarillapivotFormula
TP1 Multiplierfloat3.0tp1Multiplier
TP2 Multiplierfloat5.0tp2Multiplier
TP3 Multiplierfloat7.0tp3Multiplier
ATR Long Periodint50atrLongPeriod
Re-Entry on Rejection after SL HitbooltruerejReentry
Trail SL to Break-Even at TP1boolfalsetrailBE
Bullishcolor#26a69abullCol
Bearishcolor#ef5350bearCol
Flip Label SizestringSmallflipSizeStr
Rejection Label SizestringTinyrejSizeStr
Await Bar ConfirmationbooltrueawaitConfirm
Trade DirectionstringBothtradeDirInput
Entries on Confirmed Bar OnlybooltrueconfirmClose
Cooldown Bars After Exitint0cooldownBars
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.

Similar Pine Scripts

8 stars

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

Strategy v4 TrendMoving averages
Updated 4 years ago · None stated GitHub repository of MA+ATR+ADX+PP Strategy

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

Strategy v5 TrendMoving averages
Updated 1 year ago · None stated GitHub repository of AI SuperTrend x Pivot Percentile - Strategy

ALGOX

by thino-dev
2 stars

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

Strategy v5 TrendVolatility & bands Repaints
Updated 7 months ago · None stated GitHub repository of ALGOX
1 stars

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

Strategy v5 TrendMoving averages
Updated 2 years ago · MPL-2.0 GitHub repository of Hello World, Supertrend

PineForge — Terrace Pyramid

by pineforge-4pass
1 stars

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

Strategy v6 TrendMoving averages
Updated 5 days ago · Apache-2.0 GitHub repository of PineForge — Terrace Pyramid

Pivot Point SuperTrend

by kanylbullen
1 stars

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

Strategy v5 TrendVolatility & bands Repaints
Updated 6 days ago · None stated GitHub repository of Pivot Point SuperTrend

More strategys

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers