Price sweeps below a swing low, prints a long lower wick, and closes back inside the range. Traders who trade this pattern mechanically call it a liquidity grab and expect a reversal. Often they’re right. Often they aren’t. Institutional Liquidity Sweep [PickMyTrade] doesn’t treat the wick as sufficient evidence, it scores every sweep against five structural factors and only signals once the composite confluence score clears a threshold you control.
Table of Contents
- Why Isn’t Every Stop-Hunt Wick a Real Reversal?
- How Does the Confluence Score Combine Five Factors?
- What Do the Five Confluence Factors Actually Measure?
- How Does the Order Block Layer Add Confluence?
- Why Do the London and New York Session Windows Matter?
- How Do You Read the Confluence Pressure Band and Signal Shapes?
- How Do You Use Institutional Liquidity Sweep to Trade?
- How Do You Automate Signals with PickMyTrade?
- What Are Institutional Liquidity Sweep’s Limitations?
- Full Pine Script Source
- You May Also Like
- Frequently Asked Questions
- Does Institutional Liquidity Sweep repaint?
- What does a score of 65 actually mean versus a score of 90?
- Can I use this on any market and timeframe?
- How is this different from a standard order block or FVG indicator?
- Do I need a paid TradingView plan to use it?
Key Takeaways
- Institutional Liquidity Sweep scores every swing-level stop hunt with a five-factor Multi-Factor Sweep Confluence Score (MFSCS) instead of treating every wick through a swing high or low as a tradeable reversal.
- The five factors, wick rejection purity, volume participation, CVD net absorption, structural recovery, and session window, are each z-score normalized and combined with fixed weights (28/20/25/17/10%) into a single 0โ100 score.
- London accounts for roughly 35โ43% of global daily forex turnover according to Bank for International Settlements data, which is why session timing carries real weight (10%) in the composite score rather than being ignored.
- A signal only fires when a confirmed sweep occurs, the composite score clears a configurable threshold (65 by default), and price sits on the correct side of a 50-period trend EMA.
- An aligned Order Block adds up to 15 bonus points to the score, and the entire pipeline is non-repainting: no
request.security()lookahead, and swing levels use only confirmed prior bars.

Retail traders cluster stop-loss orders below obvious swing lows and above obvious swing highs; large participants who need counterparty liquidity to fill size can identify those clusters and push price into them before reversing (International Trading Institute). The mechanism is well documented. Confirming any single instance of it is the harder problem this indicator is built to solve.
Why Isn’t Every Stop-Hunt Wick a Real Reversal?
A liquidity sweep and a genuine breakdown look identical for exactly one candle: both pierce the level with a wick. What separates them is what happens inside that bar and immediately after it. A liquidity grab typically shows a sharp volume spike followed by an immediate, decisive reversal back into the prior range; a real breakout doesn’t snap back within minutes (CrossTrade). Trading every wick-through-a-swing-level as a reversal signal means trading both cases identically and eating the losses from the ones that were real breakdowns.
The standard workaround is discretionary: does the wick “look” rejected, does volume “look” elevated. Institutional Liquidity Sweep replaces that judgment call with five measurable factors, scored the same way on every bar, so the difference between a clean rejection and a level giving way is a number, not an impression.
How Does the Confluence Score Combine Five Factors?
Each of the five factors below is normalized to a 0โ1 range using a rolling z-score (mean and standard deviation over a configurable window, default 50 bars), then combined with fixed weights into a single composite:
Score = (0.28 ร F1 + 0.20 ร F2 + 0.25 ร F3 + 0.17 ร F4 + 0.10 ร F5) ร 100
An Order Block alignment bonus (default 15 points, configurable) is added on top when the swept level coincides with an active Order Block zone, and the total is capped at 100. The Score Threshold input (default 65) sets the minimum composite required to fire a signal; raising it toward 80 restricts signals to the highest-conviction sweeps, lowering it toward 50 admits more signals on weaker structure.
Z-score normalization rescales any raw value to standard deviations from its own recent mean, which is why factors measured in completely different units, wick recovery as a fraction, volume in contracts, CVD delta in signed volume units, can be combined into one weighted score without one factor mechanically dominating the others (QuestDB Glossary).
What Do the Five Confluence Factors Actually Measure?
| Factor | Weight | What It Measures |
|---|---|---|
| F1: Wick Rejection Purity | 28% | Fraction of the sweep depth recovered within the same bar |
| F2: Volume Participation | 20% | Volume z-score at the sweep bar vs. recent history |
| F3: CVD Net Absorption | 25% | Signed order-flow delta at the sweep bar, direction-adjusted |
| F4: Structural Recovery | 17% | How far the close reclaimed the swept swing level relative to sweep depth |
| F5: Session Window | 10% | London (1.0) / New York (0.85) / off-peak (0.45) timing weight |
F1 and F4 both measure recovery, but at different scales: F1 looks at same-bar recovery relative to how deep the wick went, while F4 looks at how far the close reclaimed the actual swing level, allowing up to 2x the sweep depth for a fully decisive reclaim. F3 is the factor that actually reads order flow: cumulative volume delta is estimated from each bar’s close position within its range (volume * (close - low) / (high - low) for buying pressure), and the single-bar delta is sign-adjusted so a bullish sweep wants positive delta and a bearish sweep wants negative delta. When price and order flow disagree, absorption pushes back against the sweep and the market shows aggressive orders being met by opposing limit orders without the expected price follow-through (LuxAlgo: CVD Explained; Bookmap), which is exactly the condition F3 is built to detect and penalize.
How Does the Order Block Layer Add Confluence?
A bullish Order Block is the last bearish candle before a run of consecutive bullish candles (an impulse, default 3 candles required); a bearish Order Block mirrors that off the last bullish candle before a downside impulse. Only one active OB per direction is tracked, the most recent, not a library of every historical zone, and each is drawn as a box that extends bar-by-bar until mitigated by a close through its boundary.

When a confirmed sweep’s swept level falls inside an active OB’s price range, the composite score receives the OB bonus (default 15 points). The logic: a sweep that occurs at a level with no prior structural significance is a weaker signal than one that sweeps directly through a zone the market previously used as an institutional reference point. The info table reports this as “Aligned” (bonus applied), “Active” (an OB exists but this sweep didn’t hit it), or “None.”
Why Do the London and New York Session Windows Matter?
The UK accounts for an estimated 35โ43% of global daily forex turnover, according to Bank for International Settlements data, making the London session the single largest liquidity window in the trading day (Arongroups, citing BIS). Institutional desks begin executing sizeable, planned order flow at their regional session open, which is the basis for F5: sweeps during the London window (07:00โ10:00 UTC by default) score 1.0, the New York window (13:00โ16:00 UTC by default) scores 0.85, and everything else scores 0.45.
Session weight is deliberately the smallest factor (10%) because it’s a prior, not evidence from the sweep itself, a sweep with strong wick rejection, elevated volume, and CVD absorption during off-peak hours can still clear the threshold on the strength of the other four factors. Session highlighting on the chart (faint yellow for London, teal for New York) can be toggled independently of whether F5 is applied, and the timezone and window-hour inputs need to match the chart’s actual timezone to detect sessions correctly.
How Do You Read the Confluence Pressure Band and Signal Shapes?
A fill band runs from the trend EMA to ยฑ1 ATR, and its opacity scales continuously with the current composite score, a faint band means the market is currently in a low-scoring environment even before any sweep fires, a deep saturated band means confluence is already building. This gives a read on background conditions independent of any specific signal.
Signal shapes carry the score information directly on the chart: a circle marks a high-conviction signal (score โฅ 80), a triangle marks a standard signal at or above the configured threshold, and the signal label prints the rounded score next to the direction (for example, “BUY 72”). X-cross markers show where an opposite-direction signal effectively closes the implied position from a prior signal. Dashed SL and TP lines project from each signal bar, sized from ATR and the configured risk:reward ratio.
How Do You Use Institutional Liquidity Sweep to Trade?
A signal requires three conditions on the same bar:
- A confirmed sweep is detected: the wick pierces the N-bar swing high or low and the close returns inside it.
- The composite score clears the threshold: 65 or above by default, configurable from 20 to 95.
- Price is on the correct side of the trend EMA: above it for longs, below it for shorts.
The signal is edge-triggered: it fires once on the bar where all three conditions first align and doesn’t repeat while they stay true. Circle signals (score โฅ 80) represent the strongest confluence the model currently measures; triangle signals meet the threshold without reaching that higher bar. A sweep that fails the EMA filter or falls short of the score threshold still shows up in the info table’s factor breakdown, it just doesn’t produce a signal shape.
How Do You Automate Signals with PickMyTrade?
The script ships five alertcondition() triggers: Long Signal, Short Signal, Any Signal, Close Long, and Close Short, the close conditions fire when an opposite-direction signal effectively invalidates the prior position bias. Wiring any of these into a TradingView alert with a webhook sends the same JSON payload structure PickMyTrade uses across its indicator lineup, so a five-factor, session-weighted sweep signal reaches a live order without manual execution.
Ready to turn confluence-scored sweep signals into executed trades? Connect Institutional Liquidity Sweep to PickMyTrade for Binance, Bybit, and 10+ exchanges, or pickmytrade.trade for Tradovate futures. No manual order entry, no missed sweeps while you’re away from the chart.
What Are Institutional Liquidity Sweep’s Limitations?
CVD here is estimated from each bar’s close position within its high-low range, a synthetic approximation of net order flow, not true bid/ask executed volume. On illiquid instruments or timeframes where intrabar price travel is minimal, this delta gets noisy and F3 contributes less discriminative signal than it would on a liquid, actively-traded chart (DefinedEdge: Liquidity Sweep Detector discusses the same estimation tradeoff common to Pine-native order flow proxies).
The indicator needs the full Normalisation Window (default 50 bars) before z-scores are statistically meaningful; before that, scores lean almost entirely on F5 (session timing) since the other four factors haven’t built up a reliable mean and standard deviation yet. Session timing itself is only meaningful on intraday timeframes (15-minute through 4-hour); on daily or weekly charts F5 sits at its off-peak value permanently, and session highlights are better turned off at that scale. The single-active-OB-per-direction design also means only the most recent zone matters, older unmitigated zones aren’t tracked once a new one of the same direction forms.
None of this makes the score a probability. A reading of 72 means the current sweep shows strong structural confluence relative to the indicator’s own normalized history, not a 72% chance of a successful reversal trade.
For a sense of how a reversal case is typically confirmed: a genuine liquidity grab tends to show a sharp volume spike followed by an immediate snap back into the prior range, while a real breakout simply doesn’t retrace that fast (ACY: Stop Hunting 101), which is the same speed-of-reversal logic F1 and F4 are built to quantify instead of eyeball.
Full Pine Script Source
The complete v6 source for Institutional Liquidity Sweep [PickMyTrade], exactly as published on TradingView. Copy it into Pine Editor, or add the indicator directly from the TradingView script page. It’s self-contained, no external library imports, no external data feeds.
// This Pine Scriptยฎ code is subject to the terms of the Mozilla Public License 2.0
// ยฉ PickMyTrade โ Institutional Liquidity Sweep [PMT]
//
// Identifies institutional stop-hunt reversals using a Multi-Factor Sweep
// Confluence Score (MFSCS) โ five structural microstructure factors scored and
// weighted at every confirmed swing-level sweep bar:
//
// F1 = Wick Rejection Purity โ fraction of sweep depth recovered in the same bar
// F2 = Volume Participation โ volume z-score; elevated sweep volume = institutional
// F3 = CVD Net Absorption โ order flow absorbed at the sweep bar (signed)
// F4 = Structural Recovery โ how far the close reclaimed the swept swing level
// F5 = Session Window โ London / NY open windows carry higher institutional weight
//
// An Order Block (OB) layer adds structural confluence: when a swing-level sweep
// aligns with a recently formed OB zone, the composite score receives a bonus.
// OB zones are drawn as persistent boxes and mitigated when price closes through them.
//
// Signal fires when:
// ยท A confirmed swing-level wick sweep is detected (bar closes back inside the swing)
// ยท Composite MFSCS score โฅ configurable threshold (0-100)
// ยท Price is on the correct side of the trend EMA
// Non-repainting. No request.security() lookahead.
//@version=6
indicator(
title = "Institutional Liquidity Sweep [PMT]",
shorttitle = "ILS [PMT]",
overlay = true,
max_labels_count = 50,
max_lines_count = 100,
max_boxes_count = 30
)
var string G_SWEEP = "Sweep Detection"
var string G_SCORE = "Confluence Score"
var string G_OB = "Order Blocks"
var string G_SESS = "Session Windows"
var string G_VIS = "Visual"
var string G_RISK = "Signal Levels"
var string G_ZEN = "Zen / Display"
var string G_DBG = "Developer Tools"
i_swing = input.int (20, "Swing Lookback (bars)", minval=5, maxval=100, group=G_SWEEP,
tooltip="N-bar swing high/low used to define liquidity levels. Wider = fewer but more significant sweeps.")
i_score_thresh = input.int (65, "Score Threshold (0-100)", minval=20, maxval=95, group=G_SCORE,
tooltip="Minimum composite score (0-100) required to fire a signal. 50=permissive | 65=balanced (default) | 80=high conviction only.")
i_z_len = input.int (50, "Normalisation Window", minval=10, maxval=200, group=G_SCORE,
tooltip="Rolling window for z-score normalisation across all five factors. Larger = more stable, slower to adapt to regime shifts.")
i_ob_bonus = input.int (15, "Order Block Score Bonus", minval=0, maxval=30, group=G_SCORE,
tooltip="Score points added (0-100 scale) when the sweep level aligns with an active Order Block zone.")
show_ob = input.bool (true, "Show OB Zones", group=G_OB)
i_ob_imp = input.int (3, "Impulse Candles Required", minval=2, maxval=5, group=G_OB,
tooltip="Consecutive same-direction candles defining an impulse. The last opposing candle before the impulse becomes the Order Block.")
show_session = input.bool (true, "Show Session Highlights", group=G_SESS,
tooltip="Shades the London and NY open windows โ the most active hours for institutional order flow. The session weight (F5) is applied regardless of whether highlights are shown.")
i_tz = input.string("UTC", "Chart Timezone", group=G_SESS,
options=["UTC", "America/New_York", "Europe/London", "Asia/Tokyo"],
tooltip="Must match your chart timezone for correct session detection.")
i_lon_hr = input.int (7, "London Open Hour (UTC)", minval=0, maxval=23, group=G_SESS,
tooltip="Start of the London session window in UTC hours. Default 7 = 07:00 UTC.")
i_ny_hr = input.int (13, "NY Open Hour (UTC)", minval=0, maxval=23, group=G_SESS,
tooltip="Start of the NY session window in UTC hours. Default 13 = 13:00 UTC.")
i_sess_hrs = input.int (3, "Window Width (hours)", minval=1, maxval=6, group=G_SESS,
tooltip="How many hours each session window spans from its open hour.")
show_levels = input.bool (true, "Show SL / TP Lines", group=G_RISK)
sl_atr_mult = input.float(1.5, "SL ATR Multiplier", minval=0.5, maxval=5.0, step=0.1, group=G_RISK, active=show_levels)
rr_ratio = input.float(2.0, "Risk : Reward", minval=0.5, maxval=10.0, step=0.1, group=G_RISK, active=show_levels)
atr_period = input.int (14, "ATR Period", minval=5, maxval=50, group=G_RISK)
ema_period = input.int (50, "Trend EMA Period", minval=5, maxval=500, group=G_RISK,
tooltip="Longs only above this EMA, shorts only below. Prevents counter-trend sweep signals during trending markets.")
bull_col = input.color(color.new(#2962FF, 0), "Bull Colour", group=G_VIS)
bear_col = input.color(color.new(#FF6D00, 0), "Bear Colour", group=G_VIS)
show_band = input.bool (true, "Confluence Pressure Band", group=G_VIS,
tooltip="EMA fill band whose opacity scales continuously with the current sweep confluence score.")
zen_mode = input.bool (false, "Zen Mode (hide text labels)", group=G_ZEN,
tooltip="Hides all text labels. Only shapes and zones remain โ ideal for screenshots and live use.")
show_table = input.bool (true, "Show Info Table", group=G_ZEN)
dbg_on = input.bool (false, "Enable Developer Tools", group=G_DBG,
tooltip="Activates diagnostic overlays: swing level lines, per-sweep markers, score breakdown labels, OB formation marks, and Pine Script console logging. Off by default.")
dbg_swing = input.bool (true, "Show Swing Levels", group=G_DBG,
tooltip="Plots the sw_hi and sw_lo step-lines the sweep detector measures against. Useful for confirming the lookback window is set correctly.")
dbg_sweeps = input.bool (true, "Mark ALL Sweep Bars", group=G_DBG,
tooltip="Marks every bar where a sweep is detected โ including those blocked by the score threshold or EMA filter. Separates detection from signal filtering.")
dbg_obs = input.bool (true, "Mark OB Formation Bars", group=G_DBG,
tooltip="Places a cross on the bar where each Order Block is confirmed, so you can verify the impulse lookback is identifying the correct candle.")
dbg_labels = input.bool (true, "Score Labels on Sweep Bars", group=G_DBG,
tooltip="Shows a compact breakdown label on every sweep bar: composite score, all five factor values, OB bonus, and the reason the signal fired or was filtered.")
dbg_console = input.bool (true, "Console Log on Sweeps", group=G_DBG,
tooltip="Logs factor values to the Pine Script console on every sweep bar. Open the console via the Pine Editor to inspect values.")
norm_z(float z) =>
math.min(1.0, math.max(0.0, (z + 3.0) / 6.0))
float atr = ta.atr(atr_period)
float ema_line = ta.ema(close, ema_period)
bool above_ema = close > ema_line
bool below_ema = close < ema_line
float hl_rng = high - low + syminfo.mintick
var float cvd = 0.0
cvd := cvd + volume * (close - low) / hl_rng - volume * (high - close) / hl_rng
float cvd_delta = ta.change(cvd, 1)
float sw_lo = ta.lowest (low, i_swing)[1]
float sw_hi = ta.highest(high, i_swing)[1]
bool bull_sweep = low < sw_lo and close > sw_lo
bool bear_sweep = high > sw_hi and close < sw_hi
float sdepth_b = sw_lo - low + syminfo.mintick
float sdepth_s = high - sw_hi + syminfo.mintick
float f1_bull = bull_sweep ? math.min(1.0, (close - low) / sdepth_b) : 0.0
float f1_bear = bear_sweep ? math.min(1.0, (high - close) / sdepth_s) : 0.0
float f1_neut = (high - low) > 0 ? (close - low) / (high - low) : 0.5
float f1_raw = bull_sweep ? f1_bull : (bear_sweep ? f1_bear : f1_neut)
float f1_mu = ta.sma (f1_raw, i_z_len)
float f1_sd = ta.stdev(f1_raw, i_z_len)
float f1_z = f1_sd > 0 ? (f1_raw - f1_mu) / f1_sd : 0.0
float f2_mu = ta.sma (volume, i_z_len)
float f2_sd = ta.stdev(volume, i_z_len)
float f2_z = f2_sd > 0 ? (volume - f2_mu) / f2_sd : 0.0
float f3_mu = ta.sma (cvd_delta, i_z_len)
float f3_sd = ta.stdev(cvd_delta, i_z_len)
float f3_raw = f3_sd > 0 ? (cvd_delta - f3_mu) / f3_sd : 0.0
float f3_z = bull_sweep ? f3_raw : (bear_sweep ? -f3_raw : math.abs(f3_raw))
float f4_bull = bull_sweep and sdepth_b > 0 ? math.min(2.0, (close - sw_lo) / sdepth_b) : 0.0
float f4_bear = bear_sweep and sdepth_s > 0 ? math.min(2.0, (sw_hi - close) / sdepth_s) : 0.0
float f4_raw = bull_sweep ? f4_bull : f4_bear
float f4_mu = ta.sma (f4_raw, i_z_len)
float f4_sd = ta.stdev(f4_raw, i_z_len)
float f4_z = f4_sd > 0 ? (f4_raw - f4_mu) / f4_sd : 0.0
int bar_hr = hour(time, i_tz)
bool in_london = bar_hr >= i_lon_hr and bar_hr < (i_lon_hr + i_sess_hrs)
bool in_ny = bar_hr >= i_ny_hr and bar_hr < (i_ny_hr + i_sess_hrs)
float f5 = in_london ? 1.0 : (in_ny ? 0.85 : 0.45)
float s1 = norm_z(f1_z)
float s2 = norm_z(f2_z)
float s3 = norm_z(f3_z)
float s4 = norm_z(f4_z)
float s5 = f5
float score_100 = (0.28 * s1 + 0.20 * s2 + 0.25 * s3 + 0.17 * s4 + 0.10 * s5) * 100.0
bool impulse_bull = true
bool impulse_bear = true
for i = 1 to i_ob_imp
if close[i] <= open[i]
impulse_bull := false
if close[i] >= open[i]
impulse_bear := false
bool bull_ob_formed = impulse_bull and close[i_ob_imp + 1] < open[i_ob_imp + 1]
bool bear_ob_formed = impulse_bear and close[i_ob_imp + 1] > open[i_ob_imp + 1]
var float bull_ob_top = na
var float bull_ob_bot = na
var float bear_ob_top = na
var float bear_ob_bot = na
var box bull_ob_box = na
var box bear_ob_box = na
if not na(bull_ob_top) and close < bull_ob_bot
bull_ob_top := na
bull_ob_bot := na
if show_ob and not na(bull_ob_box)
box.delete(bull_ob_box)
bull_ob_box := na
if not na(bear_ob_top) and close > bear_ob_top
bear_ob_top := na
bear_ob_bot := na
if show_ob and not na(bear_ob_box)
box.delete(bear_ob_box)
bear_ob_box := na
if bull_ob_formed
bull_ob_top := high[i_ob_imp + 1]
bull_ob_bot := low [i_ob_imp + 1]
if show_ob
if not na(bull_ob_box)
box.delete(bull_ob_box)
bull_ob_box := box.new(
bar_index - (i_ob_imp + 1), bull_ob_top,
bar_index + 10, bull_ob_bot,
border_color = color.new(bull_col, 30),
bgcolor = color.new(bull_col, 82),
border_width = 1)
if bear_ob_formed
bear_ob_top := high[i_ob_imp + 1]
bear_ob_bot := low [i_ob_imp + 1]
if show_ob
if not na(bear_ob_box)
box.delete(bear_ob_box)
bear_ob_box := box.new(
bar_index - (i_ob_imp + 1), bear_ob_top,
bar_index + 10, bear_ob_bot,
border_color = color.new(bear_col, 30),
bgcolor = color.new(bear_col, 82),
border_width = 1)
if show_ob and not na(bull_ob_box)
box.set_right(bull_ob_box, bar_index + 5)
if show_ob and not na(bear_ob_box)
box.set_right(bear_ob_box, bar_index + 5)
bool ob_bull_hit = bull_sweep and not na(bull_ob_bot) and sw_lo >= bull_ob_bot and sw_lo <= bull_ob_top
bool ob_bear_hit = bear_sweep and not na(bear_ob_bot) and sw_hi >= bear_ob_bot and sw_hi <= bear_ob_top
float ob_bonus = (ob_bull_hit or ob_bear_hit) ? float(i_ob_bonus) : 0.0
float final_score = math.min(100.0, score_100 + ob_bonus)
bool long_sig = bull_sweep and final_score >= float(i_score_thresh) and above_ema
bool short_sig = bear_sweep and final_score >= float(i_score_thresh) and below_ema
bool long_entry = long_sig and not long_sig [1]
bool short_entry = short_sig and not short_sig[1]
bool high_conv = final_score >= 70.0
var int pos_state = 0
if long_entry
pos_state := 1
else if short_entry
pos_state := -1
bool close_long = pos_state[1] == 1 and short_entry
bool close_short = pos_state[1] == -1 and long_entry
bool mid_conv = final_score >= 60.0 and not high_conv
color long_col_c = high_conv ? bull_col : mid_conv ? color.new(bull_col, 30) : color.new(bull_col, 55)
color short_col_c = high_conv ? bear_col : mid_conv ? color.new(bear_col, 30) : color.new(bear_col, 55)
int band_a = int(math.max(8, 100 - int(final_score * 0.92)))
plot(ema_line, "Trend EMA", color=color.new(color.gray, 50), linewidth=1)
plot(dbg_on and dbg_swing ? sw_hi : na, "DBG sw_hi",
color=color.new(color.yellow, 40), linewidth=1, style=plot.style_stepline)
plot(dbg_on and dbg_swing ? sw_lo : na, "DBG sw_lo",
color=color.new(color.yellow, 40), linewidth=1, style=plot.style_stepline)
b_top = plot(show_band ? ema_line + atr : na, display=display.none)
b_bot = plot(show_band ? ema_line : na, display=display.none)
fill(b_top, b_bot,
color = show_band and above_ema ? color.new(bull_col, band_a) : na,
title = "Bull Pressure Band")
s_top = plot(show_band ? ema_line : na, display=display.none)
s_bot = plot(show_band ? ema_line - atr : na, display=display.none)
fill(s_top, s_bot,
color = show_band and below_ema ? color.new(bear_col, band_a) : na,
title = "Bear Pressure Band")
bgcolor(show_session and in_london ? color.new(color.yellow, 94) : na, title="London Session")
bgcolor(show_session and in_ny ? color.new(color.teal, 94) : na, title="NY Session")
bgcolor(long_sig ? color.new(bull_col, 93) : na, title="Bull Regime")
bgcolor(short_sig ? color.new(bear_col, 93) : na, title="Bear Regime")
plotshape(long_entry and high_conv and not zen_mode, "Long High",
location=location.belowbar, style=shape.circle, size=size.normal, color=long_col_c)
plotshape(long_entry and not high_conv and not zen_mode, "Long Normal",
location=location.belowbar, style=shape.triangleup, size=size.small, color=long_col_c)
plotshape(long_entry and zen_mode, "Long Zen",
location=location.belowbar, style=shape.triangleup, size=size.small, color=long_col_c)
plotshape(short_entry and high_conv and not zen_mode, "Short High",
location=location.abovebar, style=shape.circle, size=size.normal, color=short_col_c)
plotshape(short_entry and not high_conv and not zen_mode, "Short Normal",
location=location.abovebar, style=shape.triangledown, size=size.small, color=short_col_c)
plotshape(short_entry and zen_mode, "Short Zen",
location=location.abovebar, style=shape.triangledown, size=size.small, color=short_col_c)
plotshape(close_long and not zen_mode, "Close Long",
location=location.abovebar, style=shape.xcross, size=size.tiny,
color=color.new(color.red, 20))
plotshape(close_short and not zen_mode, "Close Short",
location=location.belowbar, style=shape.xcross, size=size.tiny,
color=color.new(color.lime, 20))
if long_entry and not zen_mode
string sig_lbl = "BUY " + str.tostring(math.round(final_score))
label.new(bar_index, low,
sig_lbl,
style = label.style_arrowup,
color = color.new(long_col_c, 10),
textcolor = color.white,
size = size.small)
if short_entry and not zen_mode
string sig_lbl = "SELL " + str.tostring(math.round(final_score))
label.new(bar_index, high,
sig_lbl,
style = label.style_arrowdown,
color = color.new(short_col_c, 10),
textcolor = color.white,
size = size.small)
var line sl_line = na
var line tp_line = na
if long_entry and show_levels and not zen_mode
float sl_price = close - atr * sl_atr_mult
float tp_price = close + atr * sl_atr_mult * rr_ratio
sl_line := line.new(bar_index, sl_price, bar_index + 8, sl_price,
color=color.new(color.red, 30), width=1, style=line.style_dashed)
tp_line := line.new(bar_index, tp_price, bar_index + 8, tp_price,
color=color.new(color.green, 30), width=1, style=line.style_dashed)
if short_entry and show_levels and not zen_mode
float sl_price = close + atr * sl_atr_mult
float tp_price = close - atr * sl_atr_mult * rr_ratio
sl_line := line.new(bar_index, sl_price, bar_index + 8, sl_price,
color=color.new(color.red, 30), width=1, style=line.style_dashed)
tp_line := line.new(bar_index, tp_price, bar_index + 8, tp_price,
color=color.new(color.green, 30), width=1, style=line.style_dashed)
if long_entry and show_levels and not zen_mode
line.new(bar_index, close, bar_index + 20, sw_hi,
color=color.new(bull_col, 55), width=1, style=line.style_dotted)
if short_entry and show_levels and not zen_mode
line.new(bar_index, close, bar_index + 20, sw_lo,
color=color.new(bear_col, 55), width=1, style=line.style_dotted)
var table tbl = table.new(position.top_right, 2, 9,
bgcolor = color.new(color.black, 75),
border_color = color.new(color.gray, 60),
border_width = 1,
frame_color = color.new(color.gray, 50),
frame_width = 1)
if barstate.islast and show_table
color hdr_c = color.new(color.gray, 0)
color txt_c = color.new(color.white, 0)
color hi_c = color.new(color.lime, 0)
color dim_c = color.new(color.gray, 40)
color score_c = final_score >= 70.0 ? hi_c : (final_score >= float(i_score_thresh) ? color.new(bull_col, 0) : dim_c)
string ob_str = (ob_bull_hit or ob_bear_hit) ? "Aligned" : (not na(bull_ob_top) or not na(bear_ob_top)) ? "Active" : "None"
color ob_c = (ob_bull_hit or ob_bear_hit) ? hi_c : (not na(bull_ob_top) or not na(bear_ob_top)) ? color.new(color.yellow, 0) : dim_c
table.cell(tbl, 0, 0, "Inst Liq Sweep",
text_color=hdr_c, text_size=size.small, bgcolor=color.new(color.black, 60))
table.cell(tbl, 1, 0, "PMT",
text_color=color.new(color.aqua, 0), text_size=size.small, bgcolor=color.new(color.black, 60))
table.cell(tbl, 0, 1, "Score", text_color=txt_c, text_size=size.small)
table.cell(tbl, 1, 1, str.tostring(math.round(final_score)) + " / 100",
text_color=score_c, text_size=size.small)
table.cell(tbl, 0, 2, "Wick Purity", text_color=txt_c, text_size=size.small)
table.cell(tbl, 1, 2, str.format("{0,number,#.##}", s1),
text_color=s1 > 0.6 ? hi_c : txt_c, text_size=size.small)
table.cell(tbl, 0, 3, "Volume", text_color=txt_c, text_size=size.small)
table.cell(tbl, 1, 3, str.format("{0,number,#.##}", s2),
text_color=s2 > 0.6 ? hi_c : txt_c, text_size=size.small)
table.cell(tbl, 0, 4, "CVD Absorb", text_color=txt_c, text_size=size.small)
table.cell(tbl, 1, 4, str.format("{0,number,#.##}", s3),
text_color=s3 > 0.6 ? hi_c : txt_c, text_size=size.small)
table.cell(tbl, 0, 5, "Recovery", text_color=txt_c, text_size=size.small)
table.cell(tbl, 1, 5, str.format("{0,number,#.##}", s4),
text_color=s4 > 0.6 ? hi_c : txt_c, text_size=size.small)
table.cell(tbl, 0, 6, "Session", text_color=txt_c, text_size=size.small)
table.cell(tbl, 1, 6, in_london ? "London" : (in_ny ? "NY Open" : "Off-Peak"),
text_color=in_london ? color.new(color.yellow, 0) : (in_ny ? hi_c : dim_c),
text_size=size.small)
table.cell(tbl, 0, 7, "OB Zone", text_color=txt_c, text_size=size.small)
table.cell(tbl, 1, 7, ob_str, text_color=ob_c, text_size=size.small)
table.cell(tbl, 0, 8, "Threshold", text_color=dim_c, text_size=size.tiny)
table.cell(tbl, 1, 8, str.tostring(i_score_thresh) + "+ to fire",
text_color=dim_c, text_size=size.tiny)
plotshape(dbg_on and dbg_sweeps and bull_sweep and not long_entry, "DBG Bull Sweep (no signal)",
location=location.belowbar, style=shape.diamond, size=size.tiny,
color=color.new(color.aqua, 55))
plotshape(dbg_on and dbg_sweeps and bear_sweep and not short_entry, "DBG Bear Sweep (no signal)",
location=location.abovebar, style=shape.diamond, size=size.tiny,
color=color.new(color.orange, 55))
plotshape(dbg_on and dbg_obs and bull_ob_formed, "DBG Bull OB Formed",
location=location.belowbar, style=shape.cross, size=size.tiny,
color=color.new(bull_col, 20))
plotshape(dbg_on and dbg_obs and bear_ob_formed, "DBG Bear OB Formed",
location=location.abovebar, style=shape.cross, size=size.tiny,
color=color.new(bear_col, 20))
if dbg_on and dbg_labels and (bull_sweep or bear_sweep)
bool ema_ok = bull_sweep ? above_ema : below_ema
string reason = long_entry or short_entry ? "SIGNAL" : not ema_ok ? "EMA BLOCK" : final_score < float(i_score_thresh) ? "SCORE LOW" : "OTHER"
string lbl_txt = str.format(
"{0} S:{1} OB:+{2}\nF1:{3} F2:{4}\nF3:{5} F4:{6}\nF5:{7} [{8}]",
bull_sweep ? "BULL" : "BEAR",
math.round(final_score),
math.round(ob_bonus),
str.format("{0,number,#.##}", s1),
str.format("{0,number,#.##}", s2),
str.format("{0,number,#.##}", s3),
str.format("{0,number,#.##}", s4),
str.format("{0,number,#.##}", f5),
reason)
color lbl_col = (long_entry or short_entry) ? color.new(color.lime, 20) : not ema_ok ? color.new(color.gray, 30) : color.new(color.orange, 30)
if bull_sweep
label.new(bar_index, low - atr * 1.8, lbl_txt,
style=label.style_label_up, color=lbl_col,
textcolor=color.white, size=size.tiny)
else
label.new(bar_index, high + atr * 1.8, lbl_txt,
style=label.style_label_down, color=lbl_col,
textcolor=color.white, size=size.tiny)
if dbg_on and dbg_console and (bull_sweep or bear_sweep)
bool ema_pass = bull_sweep ? above_ema : below_ema
log.info("[ILS] {0} sweep | Score:{1} OB+{2} | F1:{3} F2:{4} F3:{5} F4:{6} F5:{7} | EMA:{8} | -> {9}",
bull_sweep ? "BULL" : "BEAR",
math.round(final_score),
math.round(ob_bonus),
str.format("{0,number,#.##}", s1),
str.format("{0,number,#.##}", s2),
str.format("{0,number,#.##}", s3),
str.format("{0,number,#.##}", s4),
str.format("{0,number,#.##}", f5),
ema_pass ? "PASS" : "BLOCK",
(long_entry or short_entry) ? "FIRED" : "NO SIGNAL")
alertcondition(long_entry, "ILS Long Signal", "Institutional Liquidity Sweep [PMT]: Bullish sweep โ score above threshold")
alertcondition(short_entry, "ILS Short Signal", "Institutional Liquidity Sweep [PMT]: Bearish sweep โ score above threshold")
alertcondition(long_entry or short_entry, "ILS Any Signal", "Institutional Liquidity Sweep [PMT]: Signal fired")
alertcondition(close_long, "ILS Close Long", "Institutional Liquidity Sweep [PMT]: Close long โ bearish sweep confirmed")
alertcondition(close_short, "ILS Close Short", "Institutional Liquidity Sweep [PMT]: Close short โ bullish sweep confirmed")
You May Also Like
- Order Block AI [PickMyTrade] โ A KNN confidence engine scoring smart money order blocks, the structural counterpart to this sweep-and-OB confluence model
- AI SuperTrend [PickMyTrade] โ A Hurst-gated, KNN-optimized adaptive multiplier engine, built on the same PickMyTradeLib foundation
- Best Free Fair Value Gap Indicator on TradingView โ Another free smart-money-concepts indicator with its own fill-probability scoring layer
- TradingView AI Chart Copilot: The Guide for Prop Firm Traders โ How TradingView’s own AI tooling approaches the same interpret-the-chart problem
Frequently Asked Questions
Does Institutional Liquidity Sweep repaint?
No. Swing levels use only confirmed prior bars (ta.lowest/ta.highest shifted by one bar), there’s no request.security() lookahead, and the signal is edge-triggered on the bar where all three conditions first align. Nothing recalculates after the fact.
What does a score of 65 actually mean versus a score of 90?
Both cleared the default threshold, but a 90 means most or all five factors, wick rejection, volume, CVD absorption, structural recovery, and session timing, scored well above their recent averages, plus likely an Order Block bonus. A 65 is a bare pass: it’s possible for one or two very strong factors to compensate for weak readings elsewhere and still clear the threshold. The info table’s per-factor breakdown shows which of the five actually drove any given score.
Can I use this on any market and timeframe?
Yes, with a caveat on session timing. It runs on crypto, futures, forex, or equities, but F5 (session window) is only meaningful on intraday timeframes, roughly 15-minute through 4-hour. On daily or weekly charts, disable session highlighting since F5 will sit at its off-peak weight regardless of the actual bar.
How is this different from a standard order block or FVG indicator?
Standard smart-money-concept indicators draw a zone whenever the pattern’s structural definition is met and leave the judgment of whether it’s tradeable to the trader. Institutional Liquidity Sweep instead requires a confirmed sweep, then scores it against five independently measured factors and an Order Block confluence bonus, and only shows a signal once that score clears a threshold you control.
Do I need a paid TradingView plan to use it?
No. It runs as a standard, self-contained Pine Script v6 indicator (no external library imports) on any TradingView plan that supports custom indicators. Webhook alerts for automation with PickMyTrade require TradingView’s Pro plan or higher, the same as any other alert-driven strategy.
Institutional Liquidity Sweep [PickMyTrade] is live on TradingView. Pine Script v6.
Connect your alerts with PickMyTrade โ automated trade execution, no coding required. Start free →
