Strategy · Pine Script v5 · On the chart

Wick Rejection S/R Strategy

by alboogycOdR· code credited to KingdomFinancier - Wick Rejection S/R Trading Strategy

This trading strategy detects key support and resistance zones on the price chart by identifying candle rejection wicks over a customizable lookback period.

View on GitHub

GitHub stars6on the repository
Pine versionv5runs as is
Lines of code962
Settings40inputs
LicenceNone statedfile: MPL-2.0
Last update7 months ago16 Mar 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

962 lines of Pine Script v5 from tradingview/shane-scalp-snr/wick_rejection_sr_strategy_v3.1_FIXED.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

wick_rejection_sr_strategy_v3.1_FIXED.pine
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © KingdomFinancier - Wick Rejection S/R Trading Strategy
// Version: 3.1 (CRITICAL BUG FIXES - Ready for Live Trading)
// Based on: Liquidity Sweep & Wick Rejection Scalping Methodology
// 
// ✅ FIXED: Stop Loss now executes properly via strategy.exit()
// ✅ FIXED: TP percentages calculated correctly (qty_percent of original position)
// ✅ FIXED: Confidence scoring uses actual trend alignment checks
// ✅ FIXED: TP lines memory leak resolved with var declarations
// ✅ FIXED: Trailing stop uses single strategy.exit() call
// ✅ FIXED: Position state resets properly on close

//@version=5
strategy("Wick Rejection S/R Strategy v3.1", shorttitle="WR-SR v3.1", overlay=true, max_lines_count=100, max_boxes_count=100, max_labels_count=100, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=2, commission_type=strategy.commission.percent, commission_value=0.05, slippage=2, pyramiding=1, close_entries_rule="ANY")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - LEVEL DETECTION
// ═══════════════════════════════════════════════════════════════════════════════
grp_levels = "══════ Level Detection ══════"
lookback_period     = input.int(100, "Lookback Period", minval=20, maxval=200, group=grp_levels, tooltip="Number of candles to scan for rejection wicks")
wick_threshold      = input.float(0.35, "Wick Threshold %", minval=0.30, maxval=0.80, step=0.05, group=grp_levels, tooltip="Minimum wick size as % of candle range")
level_proximity     = input.float(2.0, "Level Merge Distance", minval=0.5, maxval=20.0, step=0.5, group=grp_levels, tooltip="Points tolerance for merging nearby levels")
max_levels          = input.int(10, "Max Levels to Show", minval=2, maxval=12, group=grp_levels)
zone_buffer         = input.float(1.5, "Zone Buffer (points)", minval=0.5, maxval=10.0, step=0.5, group=grp_levels, tooltip="Buffer above/below level for zone definition")
min_candle_range    = input.float(1.0, "Min Candle Range", minval=0.1, maxval=10.0, step=0.1, group=grp_levels, tooltip="Ignore tiny candles below this range")
level_expiration    = input.int(500, "Level Expiration (bars)", minval=50, maxval=500, group=grp_levels, tooltip="Levels older than this will be removed")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - TREND FILTER
// ═══════════════════════════════════════════════════════════════════════════════
grp_trend = "══════ Trend Filter ══════"
ema_length          = input.int(9, "EMA Length", minval=5, maxval=50, group=grp_trend)
use_ema_filter      = input.bool(true, "Use EMA Filter", group=grp_trend, tooltip="Only allow signals aligned with EMA direction")
require_h1_align    = input.bool(true, "Require H1 Confluence", group=grp_trend, tooltip="Signal must align with H1 trend")
allow_reversal_trades = input.bool(true, "Allow Reversal Trades", group=grp_trend, tooltip="Allow high-confidence counter-trend trades at strong S/R levels")
reversal_min_confidence = input.int(4, "Reversal Min Confidence", minval=3, maxval=5, group=grp_trend, tooltip="Minimum confidence for counter-trend trades (3-5)")
show_ema            = input.bool(true, "Show EMA Line", group=grp_trend)

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - CISD (Change in State of Delivery)
// ═══════════════════════════════════════════════════════════════════════════════
grp_cisd = "══════ CISD Settings ══════"
use_cisd            = input.bool(true, "Enable CISD", group=grp_cisd, tooltip="Track momentum shift for early reversal detection")
cisd_min_duration   = input.int(3, "Min CISD Duration", minval=0, maxval=20, group=grp_cisd, tooltip="Minimum bars before CISD signal (0-20)")
cisd_max_duration   = input.int(50, "Max CISD Validity", minval=10, maxval=200, group=grp_cisd, tooltip="Maximum bars to track CISD state (10-200)")
cisd_confidence_boost = input.int(1, "CISD Confidence Boost", minval=1, maxval=2, group=grp_cisd, tooltip="Bonus confidence when CISD detected (1-2)")
show_cisd_labels    = input.bool(true, "Show CISD Labels", group=grp_cisd, tooltip="Display CISD markers on chart")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - SIGNAL SETTINGS
// ═══════════════════════════════════════════════════════════════════════════════
grp_signals = "══════ Signal Settings ══════"
show_zone_test      = input.bool(true, "Show Zone Test Signals", group=grp_signals)
show_sweeps         = input.bool(true, "Show Liquidity Sweeps", group=grp_signals)
show_breakout_retest = input.bool(true, "Show Breakout Retests", group=grp_signals)
min_confidence      = input.int(2, "Minimum Confidence Score", minval=1, maxval=5, group=grp_signals, tooltip="Minimum score (1-5) to show signal")
signal_cooldown     = input.int(8, "Signal Cooldown (bars)", minval=1, maxval=20, group=grp_signals, tooltip="Minimum bars between signals at same level")
max_signal_labels   = input.int(5, "Max Signal Labels", minval=3, maxval=10, group=grp_signals, tooltip="Maximum number of signal labels to show on chart")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - VISUALS
// ═══════════════════════════════════════════════════════════════════════════════
grp_visuals = "══════ Visual Settings ══════"
resistance_color    = input.color(color.red, "Resistance Color", group=grp_visuals)
support_color       = input.color(color.red, "Support Color", group=grp_visuals)
pivot_color         = input.color(color.orange, "Pivot Level Color", group=grp_visuals)
zone_transparency   = input.int(90, "Zone Transparency", minval=70, maxval=98, group=grp_visuals)
show_zones          = input.bool(true, "Show Zone Boxes", group=grp_visuals)
show_labels         = input.bool(true, "Show Price Labels", group=grp_visuals)
show_info_table     = input.bool(true, "Show Info Table", group=grp_visuals)
table_text_size     = input.string("Normal", "Table Font Size", options=["Small", "Normal", "Large"], group=grp_visuals, tooltip="Adjust info table text size for better readability")
buy_signal_color    = input.color(color.green, "Buy Signal Color", group=grp_visuals)
sell_signal_color   = input.color(color.red, "Sell Signal Color", group=grp_visuals)
sweep_color         = input.color(color.blue, "Liquidity Sweep Color", group=grp_visuals)

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - STRATEGY & RISK MANAGEMENT
// ═══════════════════════════════════════════════════════════════════════════════
grp_strategy = "══════ Strategy & Risk Management ══════"
enable_strategy     = input.bool(true, "Enable Auto Trading", group=grp_strategy, tooltip="Enable automatic trade execution")
risk_reward_ratio   = input.float(2.0, "Risk:Reward Ratio", minval=1.0, maxval=5.0, step=0.5, group=grp_strategy, tooltip="TP calculation multiplier (2.0 = 2x risk)")
use_dynamic_tp      = input.bool(true, "Use Dynamic TP", group=grp_strategy, tooltip="Calculate TP based on next S/R level")
tp_buffer           = input.float(0.5, "TP Buffer (points)", minval=0.0, maxval=5.0, step=0.5, group=grp_strategy, tooltip="Distance before next S/R for TP")
use_trailing_stop   = input.bool(false, "Use Trailing Stop", group=grp_strategy, tooltip="Enable trailing stop after profit threshold")
trail_activation    = input.float(1.5, "Trail Activation (R)", minval=0.5, maxval=3.0, step=0.5, group=grp_strategy, tooltip="Activate trailing after X times risk")
trail_offset        = input.float(0.5, "Trail Offset (R)", minval=0.1, maxval=2.0, step=0.1, group=grp_strategy, tooltip="Trailing stop offset in risk multiples")
close_on_opposite   = input.bool(true, "Close on Opposite Signal", group=grp_strategy, tooltip="Close position when opposite signal appears")
max_trades_per_day  = input.int(10, "Max Trades Per Day", minval=0, maxval=50, group=grp_strategy, tooltip="0 = unlimited")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - TAKE PROFIT LEVELS
// ═══════════════════════════════════════════════════════════════════════════════
grp_tp = "══════ Take Profit Settings ══════"
use_multiple_tp     = input.bool(true, "Use Multiple TP Levels", group=grp_tp, tooltip="Close position in stages")
tp1_percent         = input.int(50, "TP1 Close %", minval=10, maxval=100, group=grp_tp, tooltip="% of ORIGINAL position to close at TP1")
tp1_rr              = input.float(1.5, "TP1 R:R", minval=0.5, maxval=5.0, step=0.5, group=grp_tp, tooltip="TP1 at X times risk")
tp2_percent         = input.int(30, "TP2 Close %", minval=10, maxval=100, group=grp_tp, tooltip="% of ORIGINAL position to close at TP2")
tp2_rr              = input.float(2.5, "TP2 R:R", minval=1.0, maxval=10.0, step=0.5, group=grp_tp, tooltip="TP2 at X times risk")
tp3_rr              = input.float(4.0, "TP3 R:R", minval=2.0, maxval=20.0, step=1.0, group=grp_tp, tooltip="TP3 (final) at X times risk")

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS - ALERT AUTOMATION
// ═══════════════════════════════════════════════════════════════════════════════
grp_alert_auto = "══════ Alert Automation ══════"
alert_on_entry      = input.bool(true, "Alert on Entry Signal", group=grp_alert_auto)
alert_on_tp         = input.bool(true, "Alert on TP Hit", group=grp_alert_auto)
alert_on_sl         = input.bool(true, "Alert on SL Hit", group=grp_alert_auto)
alert_on_cisd       = input.bool(false, "Alert on CISD Signal", group=grp_alert_auto, tooltip="Alert when CISD momentum shift detected")
alert_prefix        = input.string("WR-SR", "Alert Prefix", group=grp_alert_auto, tooltip="Prefix for all alerts")
show_tp_lines       = input.bool(true, "Show TP Lines", group=grp_alert_auto, tooltip="Display TP level lines on chart")

// ═══════════════════════════════════════════════════════════════════════════════
// CALCULATIONS - EMA & HTF DATA
// ═══════════════════════════════════════════════════════════════════════════════
ema_9 = ta.ema(close, ema_length)

// H1 Data for confluence
[h1_close, h1_ema9] = request.security(syminfo.tickerid, "60", [close, ta.ema(close, ema_length)], lookahead=barmerge.lookahead_off)

// Trend determination
m5_bullish = close > ema_9
m5_bearish = close < ema_9
h1_bullish = h1_close > h1_ema9
h1_bearish = h1_close < h1_ema9

// Combined bias with clear logic
f_has_h1_bullish_confluence() => not require_h1_align or h1_bullish
f_has_h1_bearish_confluence() => not require_h1_align or h1_bearish

overall_bullish = m5_bullish and f_has_h1_bullish_confluence()
overall_bearish = m5_bearish and f_has_h1_bearish_confluence()

// ═══════════════════════════════════════════════════════════════════════════════
// CISD (Change in State of Delivery) TRACKING
// ═══════════════════════════════════════════════════════════════════════════════
bull_candle = close > open
bear_candle = close < open

var float cisd_bull_level = na
var int cisd_bull_start = 0
var bool cisd_bull_active = false
var bool cisd_bearish_signal = false

var float cisd_bear_level = na
var int cisd_bear_start = 0
var bool cisd_bear_active = false
var bool cisd_bullish_signal = false

cisd_bearish_signal := false
cisd_bullish_signal := false

if use_cisd and barstate.isconfirmed
    if bull_candle and bear_candle[1]
        if cisd_bull_active and not na(cisd_bull_level)
            cisd_bull_active := false
        cisd_bull_level := open
        cisd_bull_start := bar_index
        cisd_bull_active := true
    
    if cisd_bull_active and not na(cisd_bull_level)
        if bar_index - cisd_bull_start <= cisd_max_duration
            if close < cisd_bull_level
                if bar_index - cisd_bull_start >= cisd_min_duration
                    cisd_bearish_signal := true
                    if show_cisd_labels
                        label.new(bar_index, high, "CISD ▼", style=label.style_label_down, 
                                  color=color.new(sell_signal_color, 80), textcolor=sell_signal_color, size=size.tiny)
                cisd_bull_active := false
        else
            cisd_bull_active := false
    
    if bear_candle and bull_candle[1]
        if cisd_bear_active and not na(cisd_bear_level)
            cisd_bear_active := false
        cisd_bear_level := open
        cisd_bear_start := bar_index
        cisd_bear_active := true
    
    if cisd_bear_active and not na(cisd_bear_level)
        if bar_index - cisd_bear_start <= cisd_max_duration
            if close > cisd_bear_level
                if bar_index - cisd_bear_start >= cisd_min_duration
                    cisd_bullish_signal := true
                    if show_cisd_labels
                        label.new(bar_index, low, "CISD ▲", style=label.style_label_up, 
                                  color=color.new(buy_signal_color, 80), textcolor=buy_signal_color, size=size.tiny)
                cisd_bear_active := false
        else
            cisd_bear_active := false

// ═══════════════════════════════════════════════════════════════════════════════
// FUNCTIONS - WICK CALCULATIONS
// ═══════════════════════════════════════════════════════════════════════════════
f_candle_range() => high - low
f_body_top() => math.max(open, close)
f_body_bottom() => math.min(open, close)
f_upper_wick() => high - f_body_top()
f_lower_wick() => f_body_bottom() - low
f_body_size() => math.abs(close - open)

f_upper_wick_ratio() =>
    range_size = f_candle_range()
    range_size > min_candle_range ? f_upper_wick() / range_size : 0.0

f_lower_wick_ratio() =>
    range_size = f_candle_range()
    range_size > min_candle_range ? f_lower_wick() / range_size : 0.0

f_is_bullish_rejection() => f_lower_wick_ratio() >= wick_threshold
f_is_bearish_rejection() => f_upper_wick_ratio() >= wick_threshold

// ═══════════════════════════════════════════════════════════════════════════════
// LEVEL STORAGE
// ═══════════════════════════════════════════════════════════════════════════════
type LevelData
    float price
    int touches
    int bar_created
    int last_signal_bar
    line level_line
    box level_box
    label level_label

var LevelData[] support_data = array.new<LevelData>()
var LevelData[] resistance_data = array.new<LevelData>()
var bool levels_changed = false

// ═══════════════════════════════════════════════════════════════════════════════
// FUNCTIONS - LEVEL MANAGEMENT
// ═══════════════════════════════════════════════════════════════════════════════
f_find_nearby_level_idx(LevelData[] levels, float price, float proximity) =>
    idx = -1
    if array.size(levels) > 0
        for i = 0 to array.size(levels) - 1
            level_obj = array.get(levels, i)
            if math.abs(level_obj.price - price) <= proximity
                idx := i
                break
    idx

f_delete_level_drawings(LevelData level_obj) =>
    if not na(level_obj.level_line)
        line.delete(level_obj.level_line)
    if not na(level_obj.level_box)
        box.delete(level_obj.level_box)
    if not na(level_obj.level_label)
        label.delete(level_obj.level_label)

f_remove_weakest_level(LevelData[] levels) =>
    if array.size(levels) > 0
        min_score = 999999.0
        min_idx = 0
        for i = 0 to array.size(levels) - 1
            level_obj = array.get(levels, i)
            age = bar_index - level_obj.bar_created
            score = level_obj.touches * 10.0 - age * 0.1
            if score < min_score
                min_score := score
                min_idx := i
        level_to_remove = array.get(levels, min_idx)
        f_delete_level_drawings(level_to_remove)
        array.remove(levels, min_idx)

f_add_or_update_support(float price) =>
    changed = false
    nearby_idx = f_find_nearby_level_idx(support_data, price, level_proximity)
    if nearby_idx >= 0
        level_obj = array.get(support_data, nearby_idx)
        level_obj.touches += 1
        level_obj.bar_created := bar_index
        array.set(support_data, nearby_idx, level_obj)
        changed := true
    else
        if array.size(support_data) >= max_levels
            f_remove_weakest_level(support_data)
        new_level = LevelData.new()
        new_level.price := price
        new_level.touches := 1
        new_level.bar_created := bar_index
        new_level.last_signal_bar := 0
        new_level.level_line := na
        new_level.level_box := na
        new_level.level_label := na
        array.push(support_data, new_level)
        changed := true
    changed

f_add_or_update_resistance(float price) =>
    changed = false
    nearby_idx = f_find_nearby_level_idx(resistance_data, price, level_proximity)
    if nearby_idx >= 0
        level_obj = array.get(resistance_data, nearby_idx)
        level_obj.touches += 1
        level_obj.bar_created := bar_index
        array.set(resistance_data, nearby_idx, level_obj)
        changed := true
    else
        if array.size(resistance_data) >= max_levels
            f_remove_weakest_level(resistance_data)
        new_level = LevelData.new()
        new_level.price := price
        new_level.touches := 1
        new_level.bar_created := bar_index
        new_level.last_signal_bar := 0
        new_level.level_line := na
        new_level.level_box := na
        new_level.level_label := na
        array.push(resistance_data, new_level)
        changed := true
    changed

f_expire_old_levels() =>
    changed = false
    if array.size(support_data) > 0
        for i = array.size(support_data) - 1 to 0
            level_obj = array.get(support_data, i)
            if bar_index - level_obj.bar_created > level_expiration
                f_delete_level_drawings(level_obj)
                array.remove(support_data, i)
                changed := true
    if array.size(resistance_data) > 0
        for i = array.size(resistance_data) - 1 to 0
            level_obj = array.get(resistance_data, i)
            if bar_index - level_obj.bar_created > level_expiration
                f_delete_level_drawings(level_obj)
                array.remove(resistance_data, i)
                changed := true
    changed

levels_changed := f_expire_old_levels() or levels_changed

if barstate.isconfirmed
    if f_is_bullish_rejection()
        levels_changed := f_add_or_update_support(low) or levels_changed
    if f_is_bearish_rejection()
        levels_changed := f_add_or_update_resistance(high) or levels_changed

// ═══════════════════════════════════════════════════════════════════════════════
// TAKE PROFIT CALCULATION FUNCTIONS
// ═══════════════════════════════════════════════════════════════════════════════
f_calculate_tp_levels(float entry, float sl, bool is_long) =>
    risk_distance = math.abs(entry - sl)
    tp1 = 0.0
    tp2 = 0.0
    tp3 = 0.0
    if is_long
        tp1 := entry + (risk_distance * tp1_rr)
        tp2 := entry + (risk_distance * tp2_rr)
        tp3 := entry + (risk_distance * tp3_rr)
    else
        tp1 := entry - (risk_distance * tp1_rr)
        tp2 := entry - (risk_distance * tp2_rr)
        tp3 := entry - (risk_distance * tp3_rr)
    [tp1, tp2, tp3]

f_find_next_resistance(float current_price) =>
    nearest_res = 0.0
    min_dist = 99999.0
    if array.size(resistance_data) > 0
        for i = 0 to array.size(resistance_data) - 1
            r = array.get(resistance_data, i)
            dist = r.price - current_price
            if dist > 0 and dist < min_dist
                min_dist := dist
                nearest_res := r.price
    nearest_res

f_find_next_support(float current_price) =>
    nearest_sup = 0.0
    min_dist = 99999.0
    if array.size(support_data) > 0
        for i = 0 to array.size(support_data) - 1
            s = array.get(support_data, i)
            dist = current_price - s.price
            if dist > 0 and dist < min_dist
                min_dist := dist
                nearest_sup := s.price
    nearest_sup

f_calculate_dynamic_tp(float entry, float sl, bool is_long) =>
    dynamic_tp = 0.0
    if use_dynamic_tp
        if is_long
            next_level = f_find_next_resistance(entry)
            dynamic_tp := next_level > 0 ? next_level - tp_buffer : entry + (math.abs(entry - sl) * risk_reward_ratio)
        else
            next_level = f_find_next_support(entry)
            dynamic_tp := next_level > 0 ? next_level + tp_buffer : entry - (math.abs(entry - sl) * risk_reward_ratio)
    else
        risk_distance = math.abs(entry - sl)
        dynamic_tp := is_long ? entry + (risk_distance * risk_reward_ratio) : entry - (risk_distance * risk_reward_ratio)
    dynamic_tp

// ═══════════════════════════════════════════════════════════════════════════════
// ASYMMETRIC ZONE BUFFERS
// ═══════════════════════════════════════════════════════════════════════════════
support_zone_above = zone_buffer * 1.2
support_zone_below = zone_buffer * 0.8
resist_zone_above = zone_buffer * 0.8
resist_zone_below = zone_buffer * 1.2

// ═══════════════════════════════════════════════════════════════════════════════
// SIGNAL DETECTION
// ═══════════════════════════════════════════════════════════════════════════════
var int last_buy_bar = 0
var int last_sell_bar = 0

var bool buy_signal = false
var bool sell_signal = false
var bool is_sweep_signal = false
var bool is_breakout_retest = false
var float signal_level = 0.0
var int signal_confidence = 0
var string signal_type = ""
var float suggested_sl = 0.0

buy_signal := false
sell_signal := false
is_sweep_signal := false
is_breakout_retest := false
signal_confidence := 0
signal_type := ""

// ═══════════════════════════════════════════════════════════════════════════════
// CHECK SUPPORT SIGNALS (FIXED: Actual trend checks for confidence)
// ═══════════════════════════════════════════════════════════════════════════════
f_check_support_signals() =>
    _signal_found = false
    _is_sweep = false
    _level = 0.0
    _conf = 0
    _type = ""
    _sl = 0.0
    
    if array.size(support_data) > 0 and barstate.isconfirmed
        for i = 0 to array.size(support_data) - 1
            if _signal_found
                break
            level_obj = array.get(support_data, i)
            level_price = level_obj.price
            touches = level_obj.touches
            last_sig = level_obj.last_signal_bar
            if bar_index - last_sig < signal_cooldown and last_sig > 0
                continue
            zone_top = level_price + support_zone_above
            zone_bottom = level_price - support_zone_below
            in_zone = low <= zone_top and low >= zone_bottom
            swept = low < level_price and close > level_price
            rejecting = in_zone and f_is_bullish_rejection()
            if show_sweeps and swept
                _is_sweep := true
                _signal_found := true
                _level := level_price
                _sl := low - (zone_buffer * 0.5)
                _conf := 3
                _conf += touches >= 3 ? 1 : 0
                _conf += m5_bullish ? 1 : 0
                _conf += h1_bullish ? 1 : 0
                if use_cisd and cisd_bullish_signal
                    _conf += cisd_confidence_boost
                _type := "LIQUIDITY SWEEP"
                level_obj.last_signal_bar := bar_index
                array.set(support_data, i, level_obj)
            else if show_zone_test and rejecting and not _signal_found
                conf = 1
                conf += touches >= 3 ? 1 : 0
                conf += f_lower_wick_ratio() > 0.6 ? 1 : 0
                conf += m5_bullish ? 1 : 0
                conf += h1_bullish ? 1 : 0
                if use_cisd and cisd_bullish_signal
                    conf += cisd_confidence_boost
                if conf >= min_confidence
                    _signal_found := true
                    _level := level_price
                    _sl := zone_bottom - (zone_buffer * 0.5)
                    _conf := conf
                    _type := "ZONE TEST"
                    level_obj.last_signal_bar := bar_index
                    array.set(support_data, i, level_obj)
    [_signal_found, _is_sweep, _level, _conf, _type, _sl]

// ═══════════════════════════════════════════════════════════════════════════════
// CHECK RESISTANCE SIGNALS (FIXED: Actual trend checks for confidence)
// ═══════════════════════════════════════════════════════════════════════════════
f_check_resistance_signals() =>
    _signal_found = false
    _is_sweep = false
    _level = 0.0
    _conf = 0
    _type = ""
    _sl = 0.0
    
    if array.size(resistance_data) > 0 and barstate.isconfirmed
        for i = 0 to array.size(resistance_data) - 1
            if _signal_found
                break
            level_obj = array.get(resistance_data, i)
            level_price = level_obj.price
            touches = level_obj.touches
            last_sig = level_obj.last_signal_bar
            if bar_index - last_sig < signal_cooldown and last_sig > 0
                continue
            zone_top = level_price + resist_zone_above
            zone_bottom = level_price - resist_zone_below
            in_zone = high >= zone_bottom and high <= zone_top
            swept = high > level_price and close < level_price
            rejecting = in_zone and f_is_bearish_rejection()
            if show_sweeps and swept
                _is_sweep := true
                _signal_found := true
                _level := level_price
                _sl := high + (zone_buffer * 0.5)
                _conf := 3
                _conf += touches >= 3 ? 1 : 0
                _conf += m5_bearish ? 1 : 0
                _conf += h1_bearish ? 1 : 0
                if use_cisd and cisd_bearish_signal
                    _conf += cisd_confidence_boost
                _type := "LIQUIDITY SWEEP"
                level_obj.last_signal_bar := bar_index
                array.set(resistance_data, i, level_obj)
            else if show_zone_test and rejecting and not _signal_found
                conf = 1
                conf += touches >= 3 ? 1 : 0
                conf += f_upper_wick_ratio() > 0.6 ? 1 : 0
                conf += m5_bearish ? 1 : 0
                conf += h1_bearish ? 1 : 0
                if use_cisd and cisd_bearish_signal
                    conf += cisd_confidence_boost
                if conf >= min_confidence
                    _signal_found := true
                    _level := level_price
                    _sl := zone_top + (zone_buffer * 0.5)
                    _conf := conf
                    _type := "ZONE TEST"
                    level_obj.last_signal_bar := bar_index
                    array.set(resistance_data, i, level_obj)
    [_signal_found, _is_sweep, _level, _conf, _type, _sl]

[buy_found, buy_is_sweep, buy_level, buy_conf, buy_type, buy_sl] = f_check_support_signals()
[sell_found, sell_is_sweep, sell_level, sell_conf, sell_type, sell_sl] = f_check_resistance_signals()

if buy_found and (not use_ema_filter or overall_bullish or (allow_reversal_trades and buy_conf >= reversal_min_confidence))
    buy_signal := true
    is_sweep_signal := buy_is_sweep
    signal_level := buy_level
    signal_confidence := buy_conf
    signal_type := buy_type
    suggested_sl := buy_sl
    last_buy_bar := bar_index

if sell_found and (not use_ema_filter or overall_bearish or (allow_reversal_trades and sell_conf >= reversal_min_confidence))
    sell_signal := true
    is_sweep_signal := sell_is_sweep
    signal_level := sell_level
    signal_confidence := sell_conf
    signal_type := sell_type
    suggested_sl := sell_sl
    last_sell_bar := bar_index

// ═══════════════════════════════════════════════════════════════════════════════
// BREAKOUT-RETEST DETECTION
// ═══════════════════════════════════════════════════════════════════════════════
type BrokenLevel
    float price
    int bar_broken
    bool was_support

var BrokenLevel[] broken_levels = array.new<BrokenLevel>()

if barstate.isconfirmed
    if array.size(support_data) > 0
        for i = array.size(support_data) - 1 to 0
            level_obj = array.get(support_data, i)
            if close < level_obj.price - support_zone_below
                broken = BrokenLevel.new()
                broken.price := level_obj.price
                broken.bar_broken := bar_index
                broken.was_support := true
                array.push(broken_levels, broken)
                if array.size(broken_levels) > 10
                    array.shift(broken_levels)
    if array.size(resistance_data) > 0
        for i = array.size(resistance_data) - 1 to 0
            level_obj = array.get(resistance_data, i)
            if close > level_obj.price + resist_zone_above
                broken = BrokenLevel.new()
                broken.price := level_obj.price
                broken.bar_broken := bar_index
                broken.was_support := false
                array.push(broken_levels, broken)
                if array.size(broken_levels) > 10
                    array.shift(broken_levels)

if array.size(broken_levels) > 0
    for i = array.size(broken_levels) - 1 to 0
        bl = array.get(broken_levels, i)
        if bar_index - bl.bar_broken > 100
            array.remove(broken_levels, i)

if show_breakout_retest and barstate.isconfirmed and not buy_signal and not sell_signal
    if array.size(broken_levels) > 0
        for i = 0 to array.size(broken_levels) - 1
            bl = array.get(broken_levels, i)
            bars_since_break = bar_index - bl.bar_broken
            if bars_since_break >= 3 and bars_since_break <= 50
                if not bl.was_support
                    if low <= bl.price + zone_buffer and low >= bl.price - zone_buffer
                        if f_is_bullish_rejection() and (not use_ema_filter or overall_bullish)
                            buy_signal := true
                            is_breakout_retest := true
                            signal_level := bl.price
                            signal_confidence := 4
                            signal_type := "BREAKOUT RETEST"
                            suggested_sl := low - zone_buffer
                            break
                if bl.was_support
                    if high >= bl.price - zone_buffer and high <= bl.price + zone_buffer
                        if f_is_bearish_rejection() and (not use_ema_filter or overall_bearish)
                            sell_signal := true
                            is_breakout_retest := true
                            signal_level := bl.price
                            signal_confidence := 4
                            signal_type := "BREAKOUT RETEST"
                            suggested_sl := high + zone_buffer
                            break

// ═══════════════════════════════════════════════════════════════════════════════
// TRADE EXECUTION & MANAGEMENT (FIXED: Proper SL/TP implementation)
// ═══════════════════════════════════════════════════════════════════════════════
var int trades_today = 0
var int last_trade_day = 0

if dayofweek != dayofweek[1]
    trades_today := 0
    last_trade_day := dayofweek

max_trades_reached = max_trades_per_day > 0 and trades_today >= max_trades_per_day

var float entry_price = 0.0
var float stop_loss = 0.0
var float tp1_price = 0.0
var float tp2_price = 0.0
var float tp3_price = 0.0
var float original_position_size = 0.0
var bool tp1_hit = false
var bool tp2_hit = false
var bool is_long_position = false

// FIXED: Reset position state when no position
if strategy.position_size == 0 and strategy.position_size[1] != 0
    is_long_position := false
    tp1_hit := false
    tp2_hit := false
    entry_price := 0.0
    original_position_size := 0.0

if close_on_opposite and enable_strategy
    if buy_signal and strategy.position_size < 0
        strategy.close("Short", alert_message=alert_prefix + " | Closed SHORT on opposite BUY signal")
    if sell_signal and strategy.position_size > 0
        strategy.close("Long", alert_message=alert_prefix + " | Closed LONG on opposite SELL signal")

// LONG ENTRY (FIXED: Separate SL via strategy.exit)
if buy_signal and enable_strategy and not max_trades_reached and strategy.position_size == 0
    if use_multiple_tp
        [tp1, tp2, tp3] = f_calculate_tp_levels(close, suggested_sl, true)
        tp1_price := tp1
        tp2_price := tp2
        tp3_price := tp3
    else
        tp1_price := f_calculate_dynamic_tp(close, suggested_sl, true)
        tp2_price := tp1_price
        tp3_price := tp1_price
    entry_price := close
    stop_loss := suggested_sl
    is_long_position := true
    tp1_hit := false
    tp2_hit := false
    trades_today += 1
    alert_msg = alert_prefix + " BUY | " + signal_type + " (Conf:" + str.tostring(signal_confidence) + ") | Entry:" + str.tostring(close, format.mintick) + " | SL:" + str.tostring(suggested_sl, format.mintick) + " | TP1:" + str.tostring(tp1_price, format.mintick)
    strategy.entry("Long", strategy.long, alert_message=alert_msg)
    original_position_size := strategy.position_size
    if use_trailing_stop
        risk_dist = math.abs(entry_price - stop_loss)
        trail_trigger = entry_price + (risk_dist * trail_activation)
        trail_offset_points = risk_dist * trail_offset
        strategy.exit("Long SL/Trail", "Long", stop=stop_loss, trail_price=trail_trigger, trail_offset=trail_offset_points)
    else
        strategy.exit("Long SL", "Long", stop=stop_loss)

// SHORT ENTRY (FIXED: Separate SL via strategy.exit)
if sell_signal and enable_strategy and not max_trades_reached and strategy.position_size == 0
    if use_multiple_tp
        [tp1, tp2, tp3] = f_calculate_tp_levels(close, suggested_sl, false)
        tp1_price := tp1
        tp2_price := tp2
        tp3_price := tp3
    else
        tp1_price := f_calculate_dynamic_tp(close, suggested_sl, false)
        tp2_price := tp1_price
        tp3_price := tp1_price
    entry_price := close
    stop_loss := suggested_sl
    is_long_position := false
    tp1_hit := false
    tp2_hit := false
    trades_today += 1
    alert_msg = alert_prefix + " SELL | " + signal_type + " (Conf:" + str.tostring(signal_confidence) + ") | Entry:" + str.tostring(close, format.mintick) + " | SL:" + str.tostring(suggested_sl, format.mintick) + " | TP1:" + str.tostring(tp1_price, format.mintick)
    strategy.entry("Short", strategy.short, alert_message=alert_msg)
    original_position_size := math.abs(strategy.position_size)
    if use_trailing_stop
        risk_dist = math.abs(entry_price - stop_loss)
        trail_trigger = entry_price - (risk_dist * trail_activation)
        trail_offset_points = risk_dist * trail_offset
        strategy.exit("Short SL/Trail", "Short", stop=stop_loss, trail_price=trail_trigger, trail_offset=trail_offset_points)
    else
        strategy.exit("Short SL", "Short", stop=stop_loss)

// MANAGE TP LEVELS (FIXED: Calculate actual quantities from original position)
if strategy.position_size != 0 and original_position_size == 0.0
    original_position_size := math.abs(strategy.position_size)

if strategy.position_size != 0
    if is_long_position and strategy.position_size > 0
        if use_multiple_tp
            if not tp1_hit and high >= tp1_price
                close_qty = original_position_size * (tp1_percent / 100.0)
                strategy.close("Long", qty=close_qty, comment="TP1", alert_message=alert_prefix + " TP1 HIT | Closed " + str.tostring(tp1_percent) + "% at " + str.tostring(tp1_price, format.mintick))
                tp1_hit := true
            if tp1_hit and not tp2_hit and high >= tp2_price
                close_qty = original_position_size * (tp2_percent / 100.0)
                strategy.close("Long", qty=close_qty, comment="TP2", alert_message=alert_prefix + " TP2 HIT | Closed " + str.tostring(tp2_percent) + "% at " + str.tostring(tp2_price, format.mintick))
                tp2_hit := true
            if tp2_hit and high >= tp3_price
                strategy.close("Long", comment="TP3", alert_message=alert_prefix + " TP3 HIT | Full exit at " + str.tostring(tp3_price, format.mintick))
        else
            if high >= tp1_price
                strategy.close("Long", comment="TP", alert_message=alert_prefix + " TP HIT | Exit at " + str.tostring(tp1_price, format.mintick))
    if not is_long_position and strategy.position_size < 0
        if use_multiple_tp
            if not tp1_hit and low <= tp1_price
                close_qty = original_position_size * (tp1_percent / 100.0)
                strategy.close("Short", qty=close_qty, comment="TP1", alert_message=alert_prefix + " TP1 HIT | Closed " + str.tostring(tp1_percent) + "% at " + str.tostring(tp1_price, format.mintick))
                tp1_hit := true
            if tp1_hit and not tp2_hit and low <= tp2_price
                close_qty = original_position_size * (tp2_percent / 100.0)
                strategy.close("Short", qty=close_qty, comment="TP2", alert_message=alert_prefix + " TP2 HIT | Closed " + str.tostring(tp2_percent) + "% at " + str.tostring(tp2_price, format.mintick))
                tp2_hit := true
            if tp2_hit and low <= tp3_price
                strategy.close("Short", comment="TP3", alert_message=alert_prefix + " TP3 HIT | Full exit at " + str.tostring(tp3_price, format.mintick))
        else
            if low <= tp1_price
                strategy.close("Short", comment="TP", alert_message=alert_prefix + " TP HIT | Exit at " + str.tostring(tp1_price, format.mintick))

if strategy.position_size[1] != 0 and strategy.position_size == 0 and alert_on_sl
    if not tp1_hit and not tp2_hit
        alert(alert_prefix + " STOP LOSS HIT | Closed at " + str.tostring(close, format.mintick), alert.freq_once_per_bar)

if alert_on_cisd and use_cisd
    if cisd_bullish_signal
        alert(alert_prefix + " CISD BULLISH 🔥 | Momentum shift detected", alert.freq_once_per_bar)
    if cisd_bearish_signal
        alert(alert_prefix + " CISD BEARISH 🔥 | Momentum shift detected", alert.freq_once_per_bar)

// ═══════════════════════════════════════════════════════════════════════════════
// VISUALIZE TP LEVELS (FIXED: Use var to prevent memory leak)
// ═══════════════════════════════════════════════════════════════════════════════
var line tp1_line = na
var line tp2_line = na
var line tp3_line = na
var line sl_line = na
var label tp1_label = na
var label tp2_label = na
var label tp3_label = na
var label sl_label = na

if barstate.islast
    if not na(tp1_line)
        line.delete(tp1_line)
        line.delete(tp2_line)
        line.delete(tp3_line)
        line.delete(sl_line)
        label.delete(tp1_label)
        label.delete(tp2_label)
        label.delete(tp3_label)
        label.delete(sl_label)
    tp1_line := na
    tp2_line := na
    tp3_line := na
    sl_line := na
    tp1_label := na
    tp2_label := na
    tp3_label := na
    sl_label := na
    
    if show_tp_lines and strategy.position_size != 0
        tp1_line := line.new(bar_index - 10, tp1_price, bar_index + 10, tp1_price, color=color.new(color.green, 30), width=2, style=line.style_dashed, extend=extend.right)
        tp1_label := label.new(bar_index + 5, tp1_price, "TP1", style=label.style_label_left, color=color.new(color.green, 50), textcolor=color.black, size=size.tiny)
        if use_multiple_tp
            tp2_line := line.new(bar_index - 10, tp2_price, bar_index + 10, tp2_price, color=color.new(color.green, 50), width=1, style=line.style_dotted, extend=extend.right)
            tp2_label := label.new(bar_index + 5, tp2_price, "TP2", style=label.style_label_left, color=color.new(color.green, 70), textcolor=color.black, size=size.tiny)
            tp3_line := line.new(bar_index - 10, tp3_price, bar_index + 10, tp3_price, color=color.new(color.green, 70), width=1, style=line.style_dotted, extend=extend.right)
            tp3_label := label.new(bar_index + 5, tp3_price, "TP3", style=label.style_label_left, color=color.new(color.green, 80), textcolor=color.black, size=size.tiny)
        sl_line := line.new(bar_index - 10, stop_loss, bar_index + 10, stop_loss, color=color.new(color.red, 30), width=2, style=line.style_solid, extend=extend.right)
        sl_label := label.new(bar_index + 5, stop_loss, "SL", style=label.style_label_left, color=color.new(color.red, 50), textcolor=color.black, size=size.tiny)

// ═══════════════════════════════════════════════════════════════════════════════
// VISUALIZATION
// ═══════════════════════════════════════════════════════════════════════════════
f_draw_support_levels() =>
    if array.size(support_data) > 0
        for i = 0 to array.size(support_data) - 1
            level_obj = array.get(support_data, i)
            level_price = level_obj.price
            touches = level_obj.touches
            lwidth = touches >= 4 ? 3 : touches >= 2 ? 2 : 1
            lstyle = touches >= 3 ? line.style_solid : line.style_dashed
            age = bar_index - level_obj.bar_created
            age_factor = math.max(0.3, 1.0 - (age / level_expiration) * 0.5)
            line_alpha = math.round((1.0 - age_factor) * 50)
            f_delete_level_drawings(level_obj)
            level_obj.level_line := line.new(bar_index - lookback_period, level_price, bar_index + 20, level_price, color=color.new(support_color, line_alpha), width=lwidth, style=lstyle, extend=extend.right)
            if show_zones
                level_obj.level_box := box.new(bar_index - lookback_period, level_price + support_zone_above, bar_index + 20, level_price - support_zone_below, bgcolor=color.new(color.green, zone_transparency), border_color=color.new(color.green, 80), extend=extend.right)
            if show_labels
                touch_text = touches > 1 ? " (" + str.tostring(touches) + "x)" : ""
                level_obj.level_label := label.new(bar_index + 25, level_price, str.tostring(level_price, format.mintick) + touch_text, style=label.style_label_left, color=color.new(support_color, 60), textcolor=color.black, size=size.small)
            array.set(support_data, i, level_obj)

f_draw_resistance_levels() =>
    if array.size(resistance_data) > 0
        for i = 0 to array.size(resistance_data) - 1
            level_obj = array.get(resistance_data, i)
            level_price = level_obj.price
            touches = level_obj.touches
            lwidth = touches >= 4 ? 3 : touches >= 2 ? 2 : 1
            lstyle = touches >= 3 ? line.style_solid : line.style_dashed
            age = bar_index - level_obj.bar_created
            age_factor = math.max(0.3, 1.0 - (age / level_expiration) * 0.5)
            line_alpha = math.round((1.0 - age_factor) * 50)
            f_delete_level_drawings(level_obj)
            level_obj.level_line := line.new(bar_index - lookback_period, level_price, bar_index + 20, level_price, color=color.new(resistance_color, line_alpha), width=lwidth, style=lstyle, extend=extend.right)
            if show_zones
                level_obj.level_box := box.new(bar_index - lookback_period, level_price + resist_zone_above, bar_index + 20, level_price - resist_zone_below, bgcolor=color.new(color.red, zone_transparency), border_color=color.new(color.red, 80), extend=extend.right)
            if show_labels
                touch_text = touches > 1 ? " (" + str.tostring(touches) + "x)" : ""
                level_obj.level_label := label.new(bar_index + 25, level_price, str.tostring(level_price, format.mintick) + touch_text, style=label.style_label_left, color=color.new(resistance_color, 60), textcolor=color.black, size=size.small)
            array.set(resistance_data, i, level_obj)

if barstate.islast or levels_changed
    f_draw_support_levels()
    f_draw_resistance_levels()
    levels_changed := false

plot(show_ema ? ema_9 : na, "9 EMA", color=color.red, linewidth=2)

plotshape(buy_signal and not is_sweep_signal and not is_breakout_retest, "Buy Signal", shape.triangleup, location.belowbar, buy_signal_color, size=size.small)
plotshape(sell_signal and not is_sweep_signal and not is_breakout_retest, "Sell Signal", shape.triangledown, location.abovebar, sell_signal_color, size=size.small)
plotshape(buy_signal and is_sweep_signal, "Sweep Buy", shape.diamond, location.belowbar, sweep_color, size=size.large)
plotshape(sell_signal and is_sweep_signal, "Sweep Sell", shape.diamond, location.abovebar, sweep_color, size=size.large)
plotshape(buy_signal and is_breakout_retest, "BR Buy", shape.circle, location.belowbar, color.new(buy_signal_color, 20), size=size.normal)
plotshape(sell_signal and is_breakout_retest, "BR Sell", shape.circle, location.abovebar, color.new(sell_signal_color, 20), size=size.normal)

var label[] signal_labels = array.new_label()

if buy_signal and barstate.isconfirmed
    conf_emoji = signal_confidence >= 4 ? "🔥" : signal_confidence >= 3 ? "⚡" : "⚠"
    type_short = is_sweep_signal ? "SWEEP" : is_breakout_retest ? "B/R" : "ZONE"
    label_text = conf_emoji + " BUY " + type_short + " @" + str.tostring(signal_level, format.mintick) + " | SL:" + str.tostring(suggested_sl, format.mintick)
    new_label = label.new(bar_index, low - (zone_buffer * 1.5), label_text, style=label.style_label_up, color=buy_signal_color, textcolor=color.black, size=size.small)
    array.push(signal_labels, new_label)
    if array.size(signal_labels) > max_signal_labels
        old_label = array.shift(signal_labels)
        label.delete(old_label)

if sell_signal and barstate.isconfirmed
    conf_emoji = signal_confidence >= 4 ? "🔥" : signal_confidence >= 3 ? "⚡" : "⚠"
    type_short = is_sweep_signal ? "SWEEP" : is_breakout_retest ? "B/R" : "ZONE"
    label_text = conf_emoji + " SELL " + type_short + " @" + str.tostring(signal_level, format.mintick) + " | SL:" + str.tostring(suggested_sl, format.mintick)
    new_label = label.new(bar_index, high + (zone_buffer * 1.5), label_text, style=label.style_label_down, color=sell_signal_color, textcolor=color.black, size=size.small)
    array.push(signal_labels, new_label)
    if array.size(signal_labels) > max_signal_labels
        old_label = array.shift(signal_labels)
        label.delete(old_label)

// ═══════════════════════════════════════════════════════════════════════════════
// INFO TABLE (FIXED: Version updated to v3.1)
// ═══════════════════════════════════════════════════════════════════════════════
f_get_table_size() =>
    table_text_size == "Large" ? size.large : table_text_size == "Small" ? size.small : size.normal

var table info_table = table.new(position.top_right, 2, 10, bgcolor=color.new(color.black, 20), border_width=2, border_color=color.new(color.gray, 50))

if show_info_table and barstate.islast
    txt_size = f_get_table_size()
    table.cell(info_table, 0, 0, "WR-SR v3.1", text_color=color.white, text_size=txt_size, bgcolor=color.new(color.blue, 30), text_halign=text.align_center)
    table.cell(info_table, 1, 0, "STATUS", text_color=color.white, text_size=txt_size, bgcolor=color.new(color.blue, 30), text_halign=text.align_center)
    m5_bias_text = m5_bullish ? "BULLISH ▲" : m5_bearish ? "BEARISH ▼" : "NEUTRAL ●"
    m5_bias_color = m5_bullish ? color.new(color.lime, 0) : m5_bearish ? color.new(color.red, 0) : color.gray
    table.cell(info_table, 0, 1, "M5 Bias", text_color=color.new(color.silver, 0), text_size=txt_size, bgcolor=color.new(color.black, 30))
    table.cell(info_table, 1, 1, m5_bias_text, text_color=m5_bias_color, text_size=txt_size, bgcolor=color.new(color.black, 30), text_halign=text.align_right)
    h1_bias_text = h1_bullish ? "BULLISH ▲" : h1_bearish ? "BEARISH ▼" : "NEUTRAL ●"
    h1_bias_color = h1_bullish ? color.new(color.lime, 0) : h1_bearish ? color.new(color.red, 0) : color.gray
    table.cell(info_table, 0, 2, "H1 Bias", text_color=color.new(color.silver, 0), text_size=txt_size, bgcolor=color.new(color.black, 30))
    table.cell(info_table, 1, 2, h1_bias_text, text_color=h1_bias_color, text_size=txt_size, bgcolor=color.new(color.black, 30), text_halign=text.align_right)
    dir_text = overall_bullish ? "LONGS ONLY" : overall_bearish ? "SHORTS ONLY" : "NO TRADE"
    dir_color = overall_bullish ? color.new(color.lime, 0) : overall_bearish ? color.new(color.red, 0) : color.orange
    dir_bg = overall_bullish ? color.new(color.green, 80) : overall_bearish ? color.new(color.maroon, 80) : color.new(color.gray, 80)
    table.cell(info_table, 0, 3, "Direction", text_color=color.white, text_size=txt_size, bgcolor=dir_bg)
    table.cell(info_table, 1, 3, dir_text, text_color=dir_color, text_size=txt_size, bgcolor=dir_bg, text_halign=text.align_right)
    cisd_text = "INACTIVE"
    cisd_color = color.gray
    if use_cisd
        if cisd_bullish_signal
            cisd_text := "BULLISH 🔥"
            cisd_color := color.new(color.lime, 0)
        else if cisd_bearish_signal
            cisd_text := "BEARISH 🔥"
            cisd_color := color.new(color.red, 0)
        else if cisd_bull_active
            cisd_text := "TRACKING ▲"
            cisd_color := color.new(color.lime, 50)
        else if cisd_bear_active
            cisd_text := "TRACKING ▼"
            cisd_color := color.new(color.red, 50)
    table.cell(info_table, 0, 4, "CISD", text_color=color.new(color.silver, 0), text_size=txt_size, bgcolor=color.new(color.black, 30))
    table.cell(info_table, 1, 4, cisd_text, text_color=cisd_color, text_size=txt_size, bgcolor=color.new(color.black, 30), text_halign=text.align_right)
    table.cell(info_table, 0, 5, "━━━━━", text_color=color.new(color.gray, 30), text_size=txt_size, bgcolor=color.new(color.black, 50))
    table.cell(info_table, 1, 5, "━━━━━", text_color=color.new(color.gray, 30), text_size=txt_size, bgcolor=color.new(color.black, 50))
    nearest_sup = 0.0
    if array.size(support_data) > 0
        min_dist = 99999.0
        for i = 0 to array.size(support_data) - 1
            s = array.get(support_data, i)
            dist = close - s.price
            if dist > 0 and dist < min_dist
                min_dist := dist
                nearest_sup := s.price
    sup_text = nearest_sup > 0 ? str.tostring(nearest_sup, format.mintick) : "N/A"
    table.cell(info_table, 0, 6, "Support ↓", text_color=color.new(color.silver, 0), text_size=txt_size, bgcolor=color.new(color.black, 30))
    table.cell(info_table, 1, 6, sup_text, text_color=color.new(color.lime, 0), text_size=txt_size, bgcolor=color.new(color.black, 30), text_halign=text.align_right)
    nearest_res = 0.0
    if array.size(resistance_data) > 0
        min_dist = 99999.0
        for i = 0 to array.size(resistance_data) - 1
            r = array.get(resistance_data, i)
            dist = r.price - close
            if dist > 0 and dist < min_dist
                min_dist := dist
                nearest_res := r.price
    res_text = nearest_res > 0 ? str.tostring(nearest_res, format.mintick) : "N/A"
    table.cell(info_table, 0, 7, "Resist ↑", text_color=color.new(color.silver, 0), text_size=txt_size, bgcolor=color.new(color.black, 30))
    table.cell(info_table, 1, 7, res_text, text_color=color.new(color.red, 0), text_size=txt_size, bgcolor=color.new(color.black, 30), text_halign=text.align_right)
    table.cell(info_table, 0, 8, "Sup Lvls", text_color=color.new(color.silver, 0), text_size=txt_size, bgcolor=color.new(color.black, 30))
    table.cell(info_table, 1, 8, str.tostring(array.size(support_data)), text_color=color.new(color.lime, 0), text_size=txt_size, bgcolor=color.new(color.black, 30), text_halign=text.align_right)
    table.cell(info_table, 0, 9, "Res Lvls", text_color=color.new(color.silver, 0), text_size=txt_size, bgcolor=color.new(color.black, 30))
    table.cell(info_table, 1, 9, str.tostring(array.size(resistance_data)), text_color=color.new(color.red, 0), text_size=txt_size, bgcolor=color.new(color.black, 30), text_halign=text.align_right)

// ═══════════════════════════════════════════════════════════════════════════════
// END OF SCRIPT v3.1 - ALL CRITICAL BUGS FIXED ✅
// ═══════════════════════════════════════════════════════════════════════════════

Overview

Wick Rejection S/R Strategy filters potential trades using an Exponential Moving Average (EMA), Change in Status/Direction (CISD) logic, and optional higher timeframe alignment. Signals trigger on zone tests, liquidity sweeps, and breakout retests when minimum confidence thresholds are met. Drawn directly on the price chart, it features configurable zone buffers, level expiration, stop loss, and take profit controls.

Details

Published by

alboogycOdR on GitHub

Code credit

KingdomFinancier - Wick Rejection S/R Trading Strategy, as written in the script's header

Type

Strategy, Pine Script v5

Draws

On the price chart

Category

Support & resistance, Trend, SMC / ICT / price action, Scalping

Markets

Gold, Forex

Timeframes

M1-M5, M15-H1

Risk Notes

  • The strategy can add several entries in the same direction

Settings

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

SettingTypeDefaultVariable
Lookback Periodint100lookback_period
Wick Threshold %float0.35wick_threshold
Level Merge Distancefloat2.0level_proximity
Max Levels to Showint10max_levels
Zone Buffer (points)float1.5zone_buffer
Min Candle Rangefloat1.0min_candle_range
Level Expiration (bars)int500level_expiration
EMA Lengthint9ema_length
Use EMA Filterbooltrueuse_ema_filter
Require H1 Confluencebooltruerequire_h1_align
Allow Reversal Tradesbooltrueallow_reversal_trades
Reversal Min Confidenceint4reversal_min_confidence
Show all 40 settings
SettingTypeDefaultVariable
Show EMA Linebooltrueshow_ema
Enable CISDbooltrueuse_cisd
Min CISD Durationint3cisd_min_duration
Max CISD Validityint50cisd_max_duration
CISD Confidence Boostint1cisd_confidence_boost
Show CISD Labelsbooltrueshow_cisd_labels
Show Zone Test Signalsbooltrueshow_zone_test
Show Liquidity Sweepsbooltrueshow_sweeps
Show Breakout Retestsbooltrueshow_breakout_retest
Minimum Confidence Scoreint2min_confidence
Signal Cooldown (bars)int8signal_cooldown
Max Signal Labelsint5max_signal_labels
Resistance Colorcolorcolor.redresistance_color
Support Colorcolorcolor.redsupport_color
Pivot Level Colorcolorcolor.orangepivot_color
Zone Transparencyint90zone_transparency
Show Zone Boxesbooltrueshow_zones
Show Price Labelsbooltrueshow_labels
Show Info Tablebooltrueshow_info_table
Table Font SizestringNormaltable_text_size
Buy Signal Colorcolorcolor.greenbuy_signal_color
Sell Signal Colorcolorcolor.redsell_signal_color
Liquidity Sweep Colorcolorcolor.bluesweep_color
Enable Auto Tradingbooltrueenable_strategy
Risk:Reward Ratiofloat2.0risk_reward_ratio
Use Dynamic TPbooltrueuse_dynamic_tp
TP Buffer (points)float0.5tp_buffer
Use Trailing Stopboolfalseuse_trailing_stop
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

105 stars

Gold Scalping BOS & CHoCH is an overlay strategy designed for detecting market structure shifts. It calculates rolling swing highs and lows to identify Break of Structure (BOS) and Change of Character (CHoCH) events. Long signals trigger when bullish BOS and C

Strategy v6 SMC / ICT / price actionSupport & resistance
Updated 1 year ago · None stated GitHub repository of Gold Scalping BOS & CHoCH

ATOM 1m scalp HK

by dearvn
43 stars

This overlay strategy targets one-minute ATOM scalps. It tracks pivot highs and lows as resistance and support, compares a three-period EMA with a multi-stage smoothed close, and flags long or short when price crosses that curve in the matching direction. Dail

Strategy v5 ScalpingSupport & resistance Repaints
Updated 2 years ago · None stated GitHub repository of ATOM 1m scalp HK

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

This strategy identifies reversal opportunities based on liquidity sweeps and multi-gate confluence scoring. It evaluates setups using mandatory conditions such as swing sweeps, body and ATR filters, and dual EMA higher-timeframe trend alignment, alongside opt

Strategy v6 SMC / ICT / price actionTrend
Updated yesterday · MIT GitHub repository of Reversal Engine Score [WavesUnchained] Strategy

XXX

by MeridianAlgo
9 stars

This multi-timeframe scalping strategy plots moving averages alongside Smart Money Concepts elements on the price chart. It calculates trend signals using configurable moving average types like ALMA, Hull MA, or TEMA across alternate timeframes. It also plots

Strategy v5 TrendMoving averages Repaints
Updated 4 months ago · MIT GitHub repository of XXX

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