Strategy · Pine Script v6 · On the chart

Reversal Engine Score [WavesUnchained] Strategy

by casoon

This strategy identifies reversal opportunities based on liquidity sweeps and multi-gate confluence scoring.

View on GitHub

GitHub stars9on the repository
Pine versionv6current
Lines of code585
Settings40inputs
LicenceMIT
Last update3 days ago27 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

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

reversal_engine_score_strategy.pine
//@version=6
// ============================================================================
// Reversal Engine Score [WavesUnchained] — Strategy (standalone)
// Version: 1.6.3
// Author: WavesUnchained
// Build: 2026-10-12 00:00:00
// ============================================================================
// Score-based liquidity sweep reversal detector with HTF trend confluence.
//
// Features:
//   ✓ Mandatory gate model — sweep + close position + body + ATR corridor + HTF
//   ✓ Quality score (0–2 optional conditions) replaces additive sum
//   ✓ Per-direction quality thresholds (Long/Short configurable separately)
//   ✓ Short optionals: RSI > 50 (elevated), no HH
//   ✓ Structural SL — sweep low/high as base + configurable ATR noise buffer
//   ✓ R-multiple TP — adapts to actual risk distance (TP = entry ± risk × factor)
//   ✓ ATR corridor filter — rejects both low-volatility noise and spike regimes
//   ✓ Liquidity sweep detection (swing violation + recovery close)
//   ✓ HTF trend filter via dual EMA on configurable timeframe
//   ✓ Spike filter — blocks sweeps deeper than N×ATR (real breakdowns, not reversals)
//   ✓ Break-even stop triggered at configurable R-multiple of initial risk
//   ✓ Structural exit — close on new LL (long) or new HH (short)
//   ✓ Price structure labels (HH / HL / LH / LL) at confirmed pivots
//   ✓ Outcome-based label coloring (blue = no follow-through within N bars)
//   ✓ Alert conditions for BUY / SELL / either
//
// ============================================================================

strategy("Reversal Engine Score [WavesUnchained] — Strategy", overlay=true,
     max_lines_count=500,
     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, "RES Strategy · backtest invalid on this chart type",
         bgcolor = color.new(color.red, 10), text_color = color.white, text_size = size.small)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Groups
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
g_display = "Anzeige"
g_quality = "Signal-Qualität"
g_gates   = "Pflicht-Gates"
g_ma      = "Reclaim MA"
g_sltp    = "SL / TP"
gStruct   = "Marktstruktur"
gOutcome  = "Outcome"
g_strat   = "Strategy"
g_filters = "Filters"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Inputs
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// — Anzeige ———————————————————————————
only15m             = input.bool(true,  "Nur auf 15M aktiv",        group = g_display)
showLevels          = input.bool(true,  "SL/TP-Level anzeigen",     group = g_display)
showTrendBackground = input.bool(false, "Trend-Hintergrund",        group = g_display)
showScoreLabel      = input.bool(true,  "Score am Signal anzeigen", group = g_display)

// — Signal-Qualität ———————————————————
minQualityLong  = input.int(2, "Min. Qualität Long (0–2)",  minval = 0, maxval = 2, group = g_quality,
     tooltip = "Mindestanzahl optionaler Bedingungen für Longs. 2 = alle Optionals erforderlich (empfohlen).")
minQualityShort = input.int(1, "Min. Qualität Short (0–2)", minval = 0, maxval = 2, group = g_quality,
     tooltip = "Mindestanzahl optionaler Bedingungen für Shorts. Short-Optionals sind reversal-spezifisch (RSI > 50, kein HH).")
rsiLen = input.int(14, "RSI Länge", minval = 1, group = g_quality)

// — Pflicht-Gates —————————————————————
// Gate A — Liquidity Sweep
useGateA = input.bool(true, "Gate A — Liquidity Sweep", group = g_gates,
     tooltip = "Long: Low verletzt Swing-Low und schließt darüber zurück. Short: High verletzt Swing-High und schließt darunter zurück.")
swingLen = input.int(10, "  [A] Sweep Lookback", minval = 2, group = g_gates)

// Gate B — Close-Position (⅓-Regel)
useGateB = input.bool(false, "Gate B — Close-Position (⅓-Regel)", group = g_gates,
     tooltip = "Long: Close im oberen Drittel der Candle. Short: Close im unteren Drittel. Bestätigt Rejection-Charakter.")

// Gate C — Body-Größe
useGateC = input.bool(false, "Gate C — Body-Größe", group = g_gates,
     tooltip = "Candle-Body muss Mindestgröße relativ zur ATR erreichen. Filtert Doji/Spinning-Top Entries.")
minBodyAtr = input.float(0.25, "  [C] Min. Body / ATR", step = 0.05, group = g_gates)

// Gate D — ATR Minimum
useGateD     = input.bool(true, "Gate D — ATR Minimum", group = g_gates,
     tooltip = "ATR muss über dem Minimalwert × ATR-Basis liegen. Filtert Low-Vola-Noise-Phasen. Kein oberes Cap — hohe ATR ist bei Liquidity-Sweeps erwünscht.")
atrLen       = input.int(14,    "  [D] ATR Länge",      minval = 1, group = g_gates)
atrBaseLen   = input.int(50,    "  [D] ATR Basislänge", minval = 5, group = g_gates)
atrLowFactor = input.float(0.60, "  [D] Min (× Basis)", step = 0.05, group = g_gates,
     tooltip = "ATR muss über diesem Faktor × 50-Bar-ATR-Basis liegen. Werte 0.5–0.8 sinnvoll.")

// Gate E — Spike-Filter
useGateE     = input.bool(false, "Gate E — Spike-Filter", group = g_gates,
     tooltip = "Sweeps tiefer als N×ATR werden blockiert — echte Breakdowns, keine Reversals.")
spikeAtrMult = input.float(0.80, "  [E] Max Sweep-Tiefe (ATR×)", step = 0.05, group = g_gates,
     tooltip = "Maximale erlaubte Sweep-Tiefe in ATR-Vielfachen.")

// Gate F — HTF Trendfilter
useGateF   = input.bool(true,  "Gate F — HTF Trendfilter", group = g_gates,
     tooltip = "Blockiert Longs wenn HTF bearish (FastEMA < SlowEMA), Shorts wenn HTF bullish. Verhindert Counter-Trend-Entries.")
htf        = input.timeframe("60", "  [F] Timeframe",   group = g_gates)
htfFastLen = input.int(50,         "  [F] EMA schnell", minval = 1, group = g_gates)
htfSlowLen = input.int(200,        "  [F] EMA langsam", minval = 1, group = g_gates)

// Gate G — 1H WT Confluence
useGateG  = input.bool(false, "Gate G — 1H WT Confluence", group = g_gates,
     tooltip = "Long nur wenn 1H WT1 ≤ OS-Schwelle. Short nur wenn 1H WT1 ≥ OB-Schwelle. Stellt sicher, dass der 1H WaveTrend im Extrembereich ist.")
wtChLen   = input.int(10,    "  [G/H] WT Channel Length", minval = 1,  group = g_gates)
wtAvgLen  = input.int(21,    "  [G/H] WT Average Length", minval = 1,  group = g_gates)
gateG_os  = input.float(-30.0, "  [G] 1H OS Threshold (long)",  step = 1.0, group = g_gates,
     tooltip = "1H WT1 muss ≤ diesem Wert liegen für Long-Entries. Typisch: −20 bis −40.")
gateG_ob  = input.float( 30.0, "  [G] 1H OB Threshold (short)", step = 1.0, group = g_gates,
     tooltip = "1H WT1 muss ≥ diesem Wert liegen für Short-Entries. Typisch: +20 bis +40.")

// Gate H — 4H WT Confluence
useGateH  = input.bool(false, "Gate H — 4H WT Confluence", group = g_gates,
     tooltip = "Long nur wenn 4H WT1 ≤ OS-Schwelle. Short nur wenn 4H WT1 ≥ OB-Schwelle. Stellt sicher, dass der 4H WaveTrend im Extrembereich ist.")
gateH_os  = input.float(-30.0, "  [H] 4H OS Threshold (long)",  step = 1.0, group = g_gates,
     tooltip = "4H WT1 muss ≤ diesem Wert liegen für Long-Entries. Typisch: −20 bis −40.")
gateH_ob  = input.float( 30.0, "  [H] 4H OB Threshold (short)", step = 1.0, group = g_gates,
     tooltip = "4H WT1 muss ≥ diesem Wert liegen für Short-Entries. Typisch: +20 bis +40.")

// — Reclaim MA ————————————————————————
emaLen     = input.int(21,       "Länge", minval = 1, group = g_ma)
smoothType = input.string("EMA", "Typ",   group = g_ma,
     options = ["EMA", "RMA", "SMA", "WMA", "HMA", "JMA"],
     tooltip = "Glättungstyp für die Reclaim-MA. EMA = Standard. JMA = reaktionsschnell mit weniger Lag.")
jmaPhase   = input.int(50,       "  JMA Phase", minval = -100, maxval = 100, group = g_ma,
     tooltip = "Nur relevant wenn Typ = JMA. Steuert Phasenverschiebung (−100 bis +100).")
jmaPower   = input.float(2.0,    "  JMA Power", minval = 1.0,  maxval = 4.0, step = 0.25, group = g_ma,
     tooltip = "Nur relevant wenn Typ = JMA. Stärke der Glättung (1 = leicht, 4 = stark).")

// — SL / TP ———————————————————————————
slAtrBuffer = input.float(0.30, "SL Puffer (ATR×)",  step = 0.05, group = g_sltp,
     tooltip = "ATR-Puffer unterhalb des Sweep-Tiefs (Long) / oberhalb des Sweep-Hochs (Short). Struktur ist der primäre SL.")
tpRMult     = input.float(1.50, "TP Risk-Multiple",  step = 0.1,  group = g_sltp,
     tooltip = "TP = Entry ± Risk × Multiple. Risk = Abstand Entry zu strukturellem SL.")
sigCooldown = input.int(5,      "Cooldown nach Signal", minval = 0, group = g_sltp)

pivLen     = input.int(5,     "Pivot Length",                          minval = 2, maxval = 20, group = gStruct)
showPivots = input.bool(false, "Struktur-Labels (HH/HL/LH/LL) anzeigen",                       group = gStruct)

outcomeWindow = input.int(20, "Outcome-Fenster (Bars)", minval = 5, maxval = 500, group = gOutcome,
     tooltip = "Nach dieser Anzahl Bars ohne neues Hoch (Long) / neues Tief (Short) wird das Signal-Label blau.")

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 15M Check
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
is15m    = timeframe.period == "15"
isActive = only15m ? is15m : true

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Smoothing helpers
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
f_jma(src, length, phase, power) =>
    var float jma = na
    var float e0  = na
    var float e1  = na
    var float e2  = na
    _pr    = phase < -100 ? 0.5 : phase > 100 ? 2.5 : phase / 100.0 + 1.5
    _beta  = 0.45 * (length - 1) / (0.45 * (length - 1) + 2)
    _alpha = math.pow(_beta, power)
    e0    := (1 - _alpha) * src + _alpha * nz(e0[1], src)
    e1    := (src - e0) * (1 - _beta) + _beta * nz(e1[1], 0)
    e2    := (e0 + _pr * e1 - nz(jma[1], src)) * math.pow(1 - _alpha, 2) + math.pow(_alpha, 2) * nz(e2[1], 0)
    jma   := nz(jma[1], src) + e2
    jma

f_smooth(src, length, mode) =>
    switch mode
        'SMA' => ta.sma(src, length)
        'RMA' => ta.rma(src, length)
        'HMA' => ta.hma(src, length)
        'WMA' => ta.wma(src, length)
        'JMA' => f_jma(src, length, jmaPhase, jmaPower)
        =>       ta.ema(src, length)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Core
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
emaReclaim = f_smooth(close, emaLen, smoothType)
rsiValue   = ta.rsi(close, rsiLen)
atrValue   = ta.atr(atrLen)
atrBase    = ta.sma(atrValue, atrBaseLen)
htfEmaFast = request.security(syminfo.tickerid, htf, ta.ema(close, htfFastLen), barmerge.gaps_off, barmerge.lookahead_off)
htfEmaSlow = request.security(syminfo.tickerid, htf, ta.ema(close, htfSlowLen), barmerge.gaps_off, barmerge.lookahead_off)
bullTrend  = htfEmaFast > htfEmaSlow
bearTrend  = htfEmaFast < htfEmaSlow

f_wt1(src, chLen, avgLen) =>
    _esa = ta.ema(src, chLen)
    _d   = ta.ema(math.abs(src - _esa), chLen)
    _ci  = (src - _esa) / (0.015 * _d)
    ta.ema(_ci, avgLen)

wt1_1H = request.security(syminfo.tickerid, "60",  f_wt1(hlc3, wtChLen, wtAvgLen), barmerge.gaps_off, barmerge.lookahead_off)
wt1_4H = request.security(syminfo.tickerid, "240", f_wt1(hlc3, wtChLen, wtAvgLen), barmerge.gaps_off, barmerge.lookahead_off)

prevSwingLow  = ta.lowest(low[1],  swingLen)
prevSwingHigh = ta.highest(high[1], swingLen)

rangeValue = high - low
bodyValue  = math.abs(close - open)

closeInUpperThird = rangeValue > 0 and close > low  + rangeValue * 0.65
closeInLowerThird = rangeValue > 0 and close < high - rangeValue * 0.65

liquiditySweepLong  = low  < prevSwingLow  and close > prevSwingLow
liquiditySweepShort = high > prevSwingHigh and close < prevSwingHigh

atrOk  = atrValue > atrBase * atrLowFactor
bodyOk = bodyValue > atrValue * minBodyAtr

sweepDepthLong       = prevSwingLow  - low
sweepDepthShort      = high - prevSwingHigh
sweepNotTooDeepLong  = not useGateE or sweepDepthLong  <= atrValue * spikeAtrMult
sweepNotTooDeepShort = not useGateE or sweepDepthShort <= atrValue * spikeAtrMult

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Price structure (HH / HL / LH / LL)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
_phFound = not na(ta.pivothigh(high, pivLen, pivLen))
_plFound = not na(ta.pivotlow(low,   pivLen, pivLen))

var float _lastSwingHigh1 = na
var float _lastSwingHigh2 = na
var float _lastSwingLow1  = na
var float _lastSwingLow2  = na

if _phFound
    _lastSwingHigh2 := _lastSwingHigh1
    _lastSwingHigh1 := high[pivLen]
if _plFound
    _lastSwingLow2 := _lastSwingLow1
    _lastSwingLow1 := low[pivLen]

hasHighStructure = not na(_lastSwingHigh1) and not na(_lastSwingHigh2)
hasLowStructure  = not na(_lastSwingLow1)  and not na(_lastSwingLow2)

hhDetected = hasHighStructure and _lastSwingHigh1 > _lastSwingHigh2
lhDetected = hasHighStructure and _lastSwingHigh1 < _lastSwingHigh2
hlDetected = hasLowStructure  and _lastSwingLow1  > _lastSwingLow2
llDetected = hasLowStructure  and _lastSwingLow1  < _lastSwingLow2

longPivotFail  = hasLowStructure  and llDetected
shortPivotFail = hasHighStructure and hhDetected

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Signal logic — Pflicht-Gates + optionale Qualität
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// Mandatory gates — each can be toggled off individually
longGateOk  = (not useGateA or liquiditySweepLong)       and
              (not useGateB or closeInUpperThird)         and
              (not useGateC or bodyOk)                    and
              (not useGateD or atrOk)                     and
              sweepNotTooDeepLong                         and
              (not useGateF or not bearTrend)             and
              (not useGateG or wt1_1H <= gateG_os)        and
              (not useGateH or wt1_4H <= gateH_os)
shortGateOk = (not useGateA or liquiditySweepShort)      and
              (not useGateB or closeInLowerThird)         and
              (not useGateC or bodyOk)                    and
              (not useGateD or atrOk)                     and
              sweepNotTooDeepShort                        and
              (not useGateF or not bullTrend)             and
              (not useGateG or wt1_1H >= gateG_ob)        and
              (not useGateH or wt1_4H >= gateH_ob)

// Optional quality points (0–2)
longQuality  = 0
longQuality  += rsiValue > 50  ? 1 : 0
longQuality  += not llDetected ? 1 : 0

// Short optionals use reversal-appropriate conditions (inverted vs. longs):
// RSI > 50 = RSI still elevated → shorting from overbought territory
shortQuality  = 0
shortQuality  += rsiValue > 50  ? 1 : 0   // RSI above midline = reversal from overbought
shortQuality  += not hhDetected ? 1 : 0   // no higher-high structure

f_tier(q) =>
    q >= 2 ? "A" : q >= 1 ? "B" : "C"

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Cooldown + final signals
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var int lastSignalBar = na
cooldownOk = na(lastSignalBar) or bar_index - lastSignalBar > sigCooldown

rawLongSignal  = isActive and longGateOk  and longQuality  >= minQualityLong
rawShortSignal = isActive and shortGateOk and shortQuality >= minQualityShort
longSignal  = rawLongSignal  and cooldownOk and barstate.isconfirmed
shortSignal = rawShortSignal and cooldownOk and barstate.isconfirmed

if longSignal or shortSignal
    lastSignalBar := bar_index

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Strategy-ready values
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
entryLong  = longSignal  ? close : na
entryShort = shortSignal ? close : na
_lStructSL = math.min(low, prevSwingLow)  - atrValue * slAtrBuffer
_sStructSL = math.max(high, prevSwingHigh) + atrValue * slAtrBuffer
longStop   = longSignal  ? _lStructSL : na
longTake   = longSignal  ? close + (close - _lStructSL) * tpRMult : na
shortStop  = shortSignal ? _sStructSL : na
shortTake  = shortSignal ? close - (_sStructSL - close) * tpRMult : na

signalDirection = longSignal ? 1 : shortSignal ? -1 : 0
signalScore     = longSignal ? longQuality : shortSignal ? shortQuality : na

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Plots
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
plot(emaReclaim, "Reclaim MA", color = color.new(color.blue, 0), linewidth = 1)

plotshape(longSignal,  title = "BUY Signal",  style = shape.triangleup,   location = location.belowbar, size = size.small, color = color.lime, text = "BUY")
plotshape(shortSignal, title = "SELL Signal", style = shape.triangledown, location = location.abovebar, size = size.small, color = color.red,  text = "SELL")

plot(showLevels ? longStop  : na, "Long SL",  color = color.new(color.red,   0), style = plot.style_linebr)
plot(showLevels ? longTake  : na, "Long TP",  color = color.new(color.green, 0), style = plot.style_linebr)
plot(showLevels ? shortStop : na, "Short SL", color = color.new(color.red,   0), style = plot.style_linebr)
plot(showLevels ? shortTake : na, "Short TP", color = color.new(color.green, 0), style = plot.style_linebr)

plot(signalDirection, "Signal Direction", display = display.none)
plot(signalScore,     "Signal Score",     display = display.none)
plot(entryLong,       "Entry Long",       display = display.none)
plot(entryShort,      "Entry Short",      display = display.none)
plot(longStop,        "Long Stop",        display = display.none)
plot(longTake,        "Long Take",        display = display.none)
plot(shortStop,       "Short Stop",       display = display.none)
plot(shortTake,       "Short Take",       display = display.none)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Structure labels (HH / HL / LH / LL)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
colorBull = #16a34a
colorBear = #dc2626
colorWarn = #1565c0

if showPivots and _phFound and lhDetected
    label.new(bar_index - pivLen, high[pivLen], "LH",
         style = label.style_label_down, color = color.new(colorBear, 30),
         textcolor = color.white, size = size.tiny, force_overlay = true)
if showPivots and _phFound and hhDetected
    label.new(bar_index - pivLen, high[pivLen], "HH",
         style = label.style_label_down, color = color.new(colorBull, 30),
         textcolor = color.white, size = size.tiny, force_overlay = true)
if showPivots and _plFound and hlDetected
    label.new(bar_index - pivLen, low[pivLen], "HL",
         style = label.style_label_up, color = color.new(colorBull, 30),
         textcolor = color.white, size = size.tiny, force_overlay = true)
if showPivots and _plFound and llDetected
    label.new(bar_index - pivLen, low[pivLen], "LL",
         style = label.style_label_up, color = color.new(colorBear, 30),
         textcolor = color.white, size = size.tiny, force_overlay = true)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Signal labels + Outcome tracking
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var label[] _pendingLbl  = array.new_label()
var int[]   _pendingBar  = array.new_int()
var float[] _pendingRef  = array.new_float()
var bool[]  _pendingLong = array.new_bool()

if showScoreLabel and longSignal
    string structCtx = longPivotFail ? "⚠ Struktur: LL (gegenläufig)" : "✓ Struktur ok"
    string tip = "Qualität: " + f_tier(longQuality) + " (" + str.tostring(longQuality) + "/2)\n"
    tip += "── Pflicht-Gates ──\n"
    tip += liquiditySweepLong  ? "✓ Liquidity Sweep\n"      : "✗ Liquidity Sweep\n"
    tip += closeInUpperThird   ? "✓ Close oberes Drittel\n"  : "✗ Close oberes Drittel\n"
    tip += bodyOk              ? "✓ Body size\n"             : "✗ Body size\n"
    tip += atrOk               ? "✓ ATR Korridor\n"          : "✗ ATR Korridor\n"
    tip += not bearTrend       ? "✓ HTF nicht bearish\n"     : "✗ HTF nicht bearish\n"
    tip += "── Optional ──\n"
    tip += rsiValue > 50       ? "✓ RSI > 50 (+1)\n"         : "✗ RSI > 50\n"
    tip += not llDetected      ? "✓ Kein LL (+1)\n"          : "✗ Kein LL\n"
    tip += structCtx
    lbl = label.new(bar_index, low,
         "BUY — " + f_tier(longQuality),
         style     = label.style_label_up,
         textcolor = color.white,
         color     = colorBull,
         tooltip   = tip)
    array.push(_pendingLbl,  lbl)
    array.push(_pendingBar,  bar_index)
    array.push(_pendingRef,  high)
    array.push(_pendingLong, true)

if showScoreLabel and shortSignal
    string structCtx = shortPivotFail ? "⚠ Struktur: HH (gegenläufig)" : "✓ Struktur ok"
    string tip = "Qualität: " + f_tier(shortQuality) + " (" + str.tostring(shortQuality) + "/2)\n"
    tip += "── Pflicht-Gates ──\n"
    tip += liquiditySweepShort ? "✓ Liquidity Sweep\n"       : "✗ Liquidity Sweep\n"
    tip += closeInLowerThird   ? "✓ Close unteres Drittel\n"  : "✗ Close unteres Drittel\n"
    tip += bodyOk              ? "✓ Body size\n"              : "✗ Body size\n"
    tip += atrOk               ? "✓ ATR Korridor\n"           : "✗ ATR Korridor\n"
    tip += not bullTrend       ? "✓ HTF nicht bullish\n"      : "✗ HTF nicht bullish\n"
    tip += "── Optional ──\n"
    tip += rsiValue > 50       ? "✓ RSI > 50 (+1)\n"          : "✗ RSI > 50\n"
    tip += not hhDetected      ? "✓ Kein HH (+1)\n"           : "✗ Kein HH\n"
    tip += structCtx
    lbl = label.new(bar_index, high,
         "SELL — " + f_tier(shortQuality),
         style     = label.style_label_down,
         textcolor = color.white,
         color     = colorBear,
         tooltip   = tip)
    array.push(_pendingLbl,  lbl)
    array.push(_pendingBar,  bar_index)
    array.push(_pendingRef,  low)
    array.push(_pendingLong, false)

// Outcome: kein neues Hoch/Tief im Fenster → Label blau
if array.size(_pendingLbl) > 0
    for i = array.size(_pendingLbl) - 1 to 0
        _lbl    = array.get(_pendingLbl,  i)
        _sigBar = array.get(_pendingBar,  i)
        _ref    = array.get(_pendingRef,  i)
        _isLong = array.get(_pendingLong, i)
        if bar_index > _sigBar
            _win     = _isLong ? high > _ref : low < _ref
            _expired = bar_index - _sigBar >= outcomeWindow
            if _win or _expired
                if not _win
                    label.set_color(_lbl, colorWarn)
                array.remove(_pendingLbl,  i)
                array.remove(_pendingBar,  i)
                array.remove(_pendingRef,  i)
                array.remove(_pendingLong, i)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Backgrounds
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
bgcolor(showTrendBackground and bullTrend ? color.new(color.green,  92) : na)
bgcolor(showTrendBackground and bearTrend ? color.new(color.red,    92) : na)
bgcolor(only15m             and not is15m ? color.new(color.orange, 85) : na)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Alerts
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ─── Strategy inputs ─────────────────────────────────────────────────────────
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×R in profit.")
beTriggerR    = input.float(1.0,  "Break-Even Trigger (R×)", minval=0.1, step=0.1, group=g_strat,
     tooltip="R-multiple of initial risk that triggers break-even. 1.0 = move SL to entry when trade is 1R in profit.")
enableStructExit = input.bool(false, "Struktureller Exit (LL/HH)", group=g_strat,
     tooltip="Close Long bei neuem LL; Close Short bei neuem HH. Schützt offene Gewinne wenn Gegenstruktur bricht.")

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

// ─── Strategy execution (standalone, directional fixed SL/TP) ─────────────────
_canLong  = tradeDirInput != "Short Only"
_canShort = tradeDirInput != "Long Only"

var float _longSL    = na
var float _longTP    = na
var float _shortSL   = na
var float _shortTP   = na
var float _longRisk0 = na
var float _shortRisk0 = na

if (longSignal) and _canLong and _canEntry
    _longSL    := longStop
    _longTP    := longTake
    _longRisk0 := close - longStop
    strategy.entry("Long", strategy.long)
    _longRisk = close - _longSL
    log.info("RES ENTRY | dir=long | quality={0}/2 | tier={1} | gates=sweep:{2},closeUpper:{3},body:{4},atr:{5},htf:{6},spike:{7} | opt=rsi:{8},noLL:{9} | wt1h={10} | wt4h={11} | sl={12} | tp={13} | risk={14}",
         str.tostring(longQuality),
         f_tier(longQuality),
         str.tostring(liquiditySweepLong),
         str.tostring(closeInUpperThird),
         str.tostring(bodyOk),
         str.tostring(atrOk),
         str.tostring(not bearTrend),
         str.tostring(sweepNotTooDeepLong),
         str.tostring(rsiValue > 50),
         str.tostring(not llDetected),
         str.tostring(wt1_1H, "#.#"),
         str.tostring(wt1_4H, "#.#"),
         str.tostring(_longSL, format.mintick),
         str.tostring(_longTP, format.mintick),
         str.tostring(_longRisk, format.mintick))

if (shortSignal) and _canShort and _canEntry
    _shortSL    := shortStop
    _shortTP    := shortTake
    _shortRisk0 := shortStop - close
    strategy.entry("Short", strategy.short)
    _shortRisk = _shortSL - close
    log.info("RES ENTRY | dir=short | quality={0}/2 | tier={1} | gates=sweep:{2},closeLower:{3},body:{4},atr:{5},htf:{6},spike:{7} | opt=rsiAbove50:{8},noHH:{9} | wt1h={10} | wt4h={11} | sl={12} | tp={13} | risk={14}",
         str.tostring(shortQuality),
         f_tier(shortQuality),
         str.tostring(liquiditySweepShort),
         str.tostring(closeInLowerThird),
         str.tostring(bodyOk),
         str.tostring(atrOk),
         str.tostring(not bullTrend),
         str.tostring(sweepNotTooDeepShort),
         str.tostring(rsiValue > 50),
         str.tostring(not hhDetected),
         str.tostring(wt1_1H, "#.#"),
         str.tostring(wt1_4H, "#.#"),
         str.tostring(_shortSL, format.mintick),
         str.tostring(_shortTP, format.mintick),
         str.tostring(_shortRisk, format.mintick))

// ─── Exit logging ─────────────────────────────────────────────────────────────
if _justClosed
    _idx   = strategy.closedtrades - 1
    _dir   = strategy.closedtrades.size(_idx) > 0 ? "long" : "short"
    _entPx = strategy.closedtrades.entry_price(_idx)
    _extPx = strategy.closedtrades.exit_price(_idx)
    _pnl   = strategy.closedtrades.profit(_idx)
    log.info("RES EXIT | dir={0} | entryPx={1} | exitPx={2} | pnl={3} | reason={4}",
         _dir,
         str.tostring(_entPx, format.mintick),
         str.tostring(_extPx, format.mintick),
         str.tostring(_pnl, "#.##"),
         strategy.closedtrades.exit_id(_idx))

// ─── Break-even management (R-based) ─────────────────────────────────────────
if enableBE and not na(_longRisk0) and strategy.position_size > 0
    if close - strategy.position_avg_price >= beTriggerR * _longRisk0
        _longSL := math.max(_longSL, strategy.position_avg_price)
if enableBE and not na(_shortRisk0) and strategy.position_size < 0
    if strategy.position_avg_price - close >= beTriggerR * _shortRisk0
        _shortSL := math.min(_shortSL, strategy.position_avg_price)

// ─── Bracket exits (uses updated SL after BE) ────────────────────────────────
if strategy.position_size > 0
    strategy.exit("Long Exit", "Long", stop=_longSL, limit=_longTP)
if strategy.position_size < 0
    strategy.exit("Short Exit", "Short", stop=_shortSL, limit=_shortTP)

// ─── Structural exit (pivot-based) ───────────────────────────────────────────
// Fires on the bar a NEW LL/HH pivot confirms — llDetected/hhDetected are persistent states
// and would close a short that was entered while an older HH was still on record.
if enableStructExit
    if strategy.position_size > 0 and _plFound and llDetected
        strategy.close("Long", comment="ll-struct")
    if strategy.position_size < 0 and _phFound and hhDetected
        strategy.close("Short", comment="hh-struct")

Overview

Reversal Engine Score [WavesUnchained] Strategy evaluates setups using mandatory conditions such as swing sweeps, body and ATR filters, and dual EMA higher-timeframe trend alignment, alongside optional WaveTrend momentum filters. Positions enter when calculated quality scores meet user-defined thresholds. Risk management uses structural stop losses anchored to sweep extremes with ATR buffers, R-multiple take profits, and optional break-even adjustments. All trade signals, score labels, and stop or target levels plot directly on the price chart.

Details

Published by

casoon on GitHub

Draws

On the price chart

Markets

Any symbol

Timeframes

M15-H1

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
Nur auf 15M aktivbooltrueonly15m
SL/TP-Level anzeigenbooltrueshowLevels
Trend-HintergrundboolfalseshowTrendBackground
Score am Signal anzeigenbooltrueshowScoreLabel
Min. Qualität Long (0–2)int2minQualityLong
Min. Qualität Short (0–2)int1minQualityShort
RSI Längeint14rsiLen
Gate A — Liquidity SweepbooltrueuseGateA
[A] Sweep Lookbackint10swingLen
Gate B — Close-Position (⅓-Regel)boolfalseuseGateB
Gate C — Body-GrößeboolfalseuseGateC
[C] Min. Body / ATRfloat0.25minBodyAtr
Show all 40 settings
SettingTypeDefaultVariable
Gate D — ATR MinimumbooltrueuseGateD
[D] ATR Längeint14atrLen
[D] ATR Basislängeint50atrBaseLen
[D] Min (× Basis)float0.60atrLowFactor
Gate E — Spike-FilterboolfalseuseGateE
[E] Max Sweep-Tiefe (ATR×)float0.80spikeAtrMult
Gate F — HTF TrendfilterbooltrueuseGateF
[F] Timeframetimeframe60htf
[F] EMA schnellint50htfFastLen
[F] EMA langsamint200htfSlowLen
Gate G — 1H WT ConfluenceboolfalseuseGateG
[G/H] WT Channel Lengthint10wtChLen
[G/H] WT Average Lengthint21wtAvgLen
[G] 1H OS Threshold (long)float-30.0gateG_os
[G] 1H OB Threshold (short)float30.0gateG_ob
Gate H — 4H WT ConfluenceboolfalseuseGateH
[H] 4H OS Threshold (long)float-30.0gateH_os
[H] 4H OB Threshold (short)float30.0gateH_ob
Längeint21emaLen
TypstringEMAsmoothType
JMA Phaseint50jmaPhase
JMA Powerfloat2.0jmaPower
SL Puffer (ATR×)float0.30slAtrBuffer
TP Risk-Multiplefloat1.50tpRMult
Cooldown nach Signalint5sigCooldown
Pivot Lengthint5pivLen
Struktur-Labels (HH/HL/LH/LL) anzeigenboolfalseshowPivots
Outcome-Fenster (Bars)int20outcomeWindow
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

ScarAlgo v1

by g-moe
3 stars

ScarAlgo v1 is a trend-following and market-structure strategy plotted directly on the main price chart. It triggers entries using an ATR-based trailing envelope filtered by a configurable multi-timeframe moving average, while employing a WaveTrend momentum os

Strategy v5 TrendSMC / ICT / price action
Updated 5 months ago · None stated GitHub repository of ScarAlgo v1

This strategy overlays the price chart and looks for VWAP pullbacks. It goes long when a short SMA crosses above VWAP after RSI has been oversold within ten bars, and short on the opposite cross after RSI has been overbought. Optional Minervini-style moving-av

Strategy v5 TrendOscillators & momentum
Updated 2 years ago · GPL-3.0 GitHub repository of Advanced VWAP-Momentum Pullback Trend Qualifier
57 stars

This strategy identifies trend reversals through market structure changes (CHoCH) filtered by MACD momentum. It initiates long positions on bullish structure breaks when MACD is positive and above its signal line, and short positions on bearish breaks when MAC

Strategy v6 SMC / ICT / price actionOscillators & momentum Repaints
Updated 1 year ago · Apache-2.0 GitHub repository of Market Structure MACD 4H + Trailing

This strategy trades 1-minute futures contracts on Binance. It plots pivot high and low resistance and support levels on the price chart, along with an EMA and a custom Cto indicator. Long and short entries trigger on EMA and Cto crossovers combined with price

Strategy v5 TrendMoving averages Repaints
Updated 2 years ago · None stated GitHub repository of Binancash Futures Strategy 1M Candles

FilledValueGap A006

by ruckard
13 stars

This strategy enters trades in the opposite direction of filled value gaps, operating under the assumption that price action will resume the primary trend after filling a recent gap. It plots directly on the main price chart and supports MA-based trend filters

Strategy v5 SMC / ICT / price actionTrend
Updated 1 year ago · None stated GitHub repository of FilledValueGap A006

More SMC / ICT scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers