Home / Strategies / SMC 3-Step Strategy

SMC 3-Step Strategy [A+ Setup] V6

This is a complete Smart Money Concepts (SMC) strategy that combines Daily Bias, Intraday Structure, and Liquidity Sweeps into a mechanical "A+ Setup".

Strategy Logic

The strategy follows a strict 3-step process to validate entries:

  1. Daily Bias: Checks if the previous day closed higher/lower than it opened.
  2. Intraday Structure: Confirms trend alignment on the M15 timeframe (Non-Repainting).
  3. Liquidity Sweep: Waits for a sweep of Asia Session High/Low followed by a Change of Character (ChoCh).

Pine Script V6 Code

Copy the code below and paste it into your TradingView Pine Editor.

SMC_3_Step.pine
//@version=6
strategy("SMC 3-Step Strategy [A+ Setup] Non-Repainting", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=1, currency=currency.USD)

// =============================================================================
// 1. INPUTS
// =============================================================================
grp_time    = "Session Times (NY UTC-5)"
asia_sess   = input.session("1900-0000", "Asian Session", group=grp_time) // Pre-London
killzone    = input.session("0200-1100", "Trading Killzone (Lon/NY)", group=grp_time) // Execution window

grp_struc   = "Structure Settings"
m15_len     = input.int(5, "M15 Swing Length", minval=1, group=grp_struc, tooltip="Length to define swing points on M15")
entry_len   = input.int(3, "Entry Chart Swing Length", minval=1, group=grp_struc, tooltip="Length to define ChoCh on current timeframe")

grp_risk    = "Risk Management"
rr_ratio    = input.float(5.0, "Risk:Reward Ratio", step=0.5, group=grp_risk)
use_fixed_sl= input.bool(false, "Use Fixed Pips SL?", group=grp_risk)
sl_pips     = input.float(10.0, "Fixed SL (Pips)", group=grp_risk)

// =============================================================================
// 2. STEP 1: DAILY BIAS
// =============================================================================
// Get Daily Open and Close.
// We use [1] and lookahead=on to ensure we are referencing the COMPLETED previous day.
d_open  = request.security(syminfo.tickerid, "D", open[1], lookahead=barmerge.lookahead_on)
d_close = request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_on)

// Determine Bias
daily_bullish = d_close > d_open
daily_bearish = d_close < d_open

// Visuals for Step 1
plotshape(daily_bullish, "Daily Bull", shape.triangleup, location.top, color.green, size=size.tiny)
plotshape(daily_bearish, "Daily Bear", shape.triangledown, location.bottom, color.red, size=size.tiny)

// =============================================================================
// 3. STEP 2: INTRADAY BIAS (M15 STRUCTURE) - NON-REPAINTING
// =============================================================================
// Structure calculation function
calc_structure(len) =>
    float ph = ta.pivothigh(high, len, len)
    float pl = ta.pivotlow(low, len, len)
    
    var int trend = 0 // 1 = Bullish, -1 = Bearish
    var float last_ph = high
    var float last_pl = low
    
    if not na(ph)
        last_ph := ph
    if not na(pl)
        last_pl := pl
        
    // Break of Structure Logic
    if close > last_ph
        trend := 1
    if close < last_pl
        trend := -1
    trend

// Request M15 structure data.
// CRITICAL FIX: We request 'calc_structure(m15_len)[1]'.
// The [1] ensures we get the value of the PREVIOUSLY CLOSED M15 bar.
// This eliminates the repainting risk identified by the AI.
m15_trend_confirmed = request.security(syminfo.tickerid, "15", calc_structure(m15_len)[1], lookahead=barmerge.lookahead_on)

// Visual verification of Alignment (Daily Bias + Intraday Bias)
bias_aligned_bull = daily_bullish and m15_trend_confirmed == 1
bias_aligned_bear = daily_bearish and m15_trend_confirmed == -1

// Background color to show valid trading days/times
bgcolor(bias_aligned_bull ? color.new(color.green, 90) : na, title="Bullish Alignment Background")
bgcolor(bias_aligned_bear ? color.new(color.red, 90) : na, title="Bearish Alignment Background")

// =============================================================================
// 4. STEP 3: LIQUIDITY SWEEP (ASIA SESSION)
// =============================================================================
// Define time zone
t_zone = "America/New_York"

// Check time windows
in_asia     = time(timeframe.period, asia_sess, t_zone)
in_killzone = time(timeframe.period, killzone, t_zone)

// Variables to store Asia High/Low
var float asia_h = na
var float asia_l = na

// [FIX] Strict boolean check for new day
bool is_new_day = ta.change(time("D")) != 0

// Reset Asia High/Low at the start of a new day
if is_new_day
    asia_h := na
    asia_l := na

// Track High/Low during Asia Session
if not na(in_asia)
    asia_h := na(asia_h) ? high : math.max(asia_h, high)
    asia_l := na(asia_l) ? low : math.min(asia_l, low)

// Plot Asia Range
plot(not na(in_asia) or not na(in_killzone) ? asia_h : na, "Asia High", color.red, style=plot.style_linebr)
plot(not na(in_asia) or not na(in_killzone) ? asia_l : na, "Asia Low", color.green, style=plot.style_linebr)

// Detect Sweep Logic
// Bullish Setup: Bias is Bullish + Price sweeps BELOW Asia Low
sweep_low_cond = bias_aligned_bull and not na(in_killzone) and low < asia_l
// Bearish Setup: Bias is Bearish + Price sweeps ABOVE Asia High
sweep_high_cond = bias_aligned_bear and not na(in_killzone) and high > asia_h

// State management for Sweep (Latch mechanism)
var bool swept_asia_low = false
var bool swept_asia_high = false

if is_new_day
    swept_asia_low := false
    swept_asia_high := false

if sweep_low_cond
    swept_asia_low := true
    
if sweep_high_cond
    swept_asia_high := true

// =============================================================================
// 5. ENTRY TRIGGER (CHOCH / STRUCTURE SHIFT)
// =============================================================================
// Calculate local pivots on the CURRENT timeframe (e.g. M5)
l_ph = ta.pivothigh(high, entry_len, entry_len)
l_pl = ta.pivotlow(low, entry_len, entry_len)

var float entry_swing_high = high
var float entry_swing_low = low

if not na(l_ph)
    entry_swing_high := l_ph
if not na(l_pl)
    entry_swing_low := l_pl

// [FIX] Calculate crossovers globally to ensure consistent execution history
bull_break_confirmed = ta.crossover(close, entry_swing_high)
bear_break_confirmed = ta.crossunder(close, entry_swing_low)

// -----------------------------------------------------------------------------
// LONG ENTRY EXECUTION
// -----------------------------------------------------------------------------
// 1. Daily & Intraday Bias are Bullish
// 2. We have swept the Asia Low
// 3. We get a Candle Close ABOVE the recent local High (ChoCh)
long_trigger = bias_aligned_bull and swept_asia_low and bull_break_confirmed and strategy.position_size == 0

if long_trigger
    // Stop Loss calculation
    float sl_price = use_fixed_sl ? close - (sl_pips * syminfo.mintick * 10) : entry_swing_low
    float risk = close - sl_price
    float tp_price = close + (risk * rr_ratio)
    
    if risk > 0
        strategy.entry("Long A+", strategy.long)
        strategy.exit("Exit Long", "Long A+", stop=sl_price, limit=tp_price)
        swept_asia_low := false // Reset flag to prevent reentry on same sweep

// -----------------------------------------------------------------------------
// SHORT ENTRY EXECUTION
// -----------------------------------------------------------------------------
// 1. Daily & Intraday Bias are Bearish
// 2. We have swept the Asia High
// 3. We get a Candle Close BELOW the recent local Low (ChoCh)
short_trigger = bias_aligned_bear and swept_asia_high and bear_break_confirmed and strategy.position_size == 0

if short_trigger
    // Stop Loss calculation
    float sl_price = use_fixed_sl ? close + (sl_pips * syminfo.mintick * 10) : entry_swing_high
    float risk = sl_price - close
    float tp_price = close - (risk * rr_ratio)
    
    if risk > 0
        strategy.entry("Short A+", strategy.short)
        strategy.exit("Exit Short", "Short A+", stop=sl_price, limit=tp_price)
        swept_asia_high := false // Reset flag

// =============================================================================
// VISUALIZATION
// =============================================================================
plot(entry_swing_high, "Entry Structure High", color.new(color.blue, 50))
plot(entry_swing_low, "Entry Structure Low", color.new(color.blue, 50))

// [FIX] Used named argument 'textcolor' to fix syntax error
plotshape(long_trigger, "Long Entry", shape.labelup, location.belowbar, color.green, text="BUY", textcolor=color.white)
plotshape(short_trigger, "Short Entry", shape.labeldown, location.abovebar, color.red, text="SELL", textcolor=color.white)

How to Optimize This Strategy

Raw strategies often fail due to slippage and lack of dynamic risk management. To make this strategy production-ready:

  1. Add Commission Logic: Most backtests ignore fees.
  2. Implement Dynamic Stops: Use ATR-based trailing stops.
  3. Filter Choppy Markets: Add an ADX or Volume filter.

You can do all of this automatically using our AI Co-pilot.