1. Liquidity Delta Profiler Description (LuxAlgo)
A liquidity-zone indicator that splits each Buy-Side/Sell-Side Liquidity zone into 4 quadrants of tracked volume delta, tracks each zone's remaining "health," detects 4 distinct reversal patterns during sweeps, and validates each signal type's real win-rate with a built-in forward-tracked trade simulation
1.1 Indicator Concept
This indicator is built around a Smart Money Concepts idea: the wick above a swing high (or below a swing low) marks a pool of resting stop orders - "Buy-Side Liquidity" (BSL) above highs, "Sell-Side Liquidity" (SSL) below lows. Rather than treating that zone as a single flat level, it slices it into 4 horizontal quadrants and tracks, bar by bar, how much buy vs. sell volume actually traded inside each slice as price interacts with the zone. When price eventually sweeps through the zone, the pattern of volume across those 4 quadrants is analyzed to classify the sweep into one of 4 distinct reversal-signal types - and, unlike a typical indicator, each signal's real outcome is then tracked forward in time and rolled into a live win-rate dashboard.
- Zones have a volume "capacity" that decays: each zone starts at 100% health and loses health as volume trades through it, based on a capacity budget sized from average volume - a zone that's absorbed a lot of volume without breaking is "weaker" than a fresh one, even before it's actually swept.
- Only the most significant overlapping zone survives: if a new swing point would create a zone overlapping an existing one, only the more extreme of the two (the higher high for BSL, the lower low for SSL) is kept.
- 4 reversal patterns, checked in priority order: Absorption (contrary volume trapped at the sweep edge), Exhaustion (a sweep on suspiciously thin volume), Divergence (heavy same-direction volume that still failed to break out), and Rejection (an immediate strong opposite-direction bar right after the sweep) - a zone can only ever fire one of these, once.
- Every signal becomes a tracked, simulated trade: the indicator doesn't just plot a label and move on - it opens a hypothetical position at the signal's price and watches forward whether price actually held in the expected direction long enough to count as a win, feeding a live, per-pattern-type win-rate table.
1.2 Indicator Features
- BSL/SSL Zone Boxes: a red-outlined zone above each qualifying swing high (Buy-Side Liquidity) and a green-outlined zone below each qualifying swing low (Sell-Side Liquidity), each split into 4 stacked quadrant boxes.
- Per-Quadrant Volume Delta: each quadrant box shows its own accumulated buy/sell delta (colored and shaded by dominance and magnitude), updating live as price trades through different parts of the zone.
- Zone Health / Decay (optional): a percentage label at each active zone's midpoint, draining from 100% toward 0% as cumulative volume trades through it.
- Swept Zone Memory (optional): once a zone is swept, it can remain visible with a dashed outline and faded quadrants instead of disappearing outright.
- Reversal Signal Labels (optional): "ABS", "EXH", "DIV", or "REJ" tags appear at the sweep point when one of the 4 patterns is detected, sized/shaded by the pattern's calculated significance, with a detailed hover tooltip.
- Reversal Performance Dashboard (optional): a live table showing, per signal type, how many have fired, how many were forward-tracked wins, and the resulting win percentage.
- Bar Highlighting (optional): yellow bars while a signal is still being evaluated, aqua bars while it's currently holding in profit.
- Alerts: 7 dynamic alert conditions (zone testing, zone swept - separately for BSL/SSL - and reversal signals). All alerts are dynamic alert() calls; the script defines no static alertcondition() entries, so none of them appear individually in TradingView's alert-creation dropdown - you'd select "Any alert() function call" to catch them.
1.3 How to Use the Indicator
- Treat BSL/SSL zones as stop-hunting targets: price is statistically drawn toward these wick zones to run the resting orders there - watch how it behaves once it arrives.
- Read the quadrant colors before a sweep happens - heavy contrary-colored volume building up in the outer quadrant (closest to the zone's edge) can hint at absorption before the sweep is even confirmed.
- Use the reversal labels as your headline signal, and lean on the dashboard's win-rate numbers to judge which of the 4 pattern types has actually been performing well on this specific chart/timeframe before trusting a fresh one.
- Watch zone health as a secondary read - a zone that's already absorbed most of its capacity without sweeping may be more likely to hold as a level going forward, versus a fresh, "full health" zone.
- Give the dashboard time to accumulate meaningful sample sizes - win rates on only 1-2 historical signals of a given type aren't statistically reliable; let more signals build up before trusting the percentage.
1.4 How the Indicator Works
Inputs & Roles
- length ("Pivot Length", default: 15, min 2): bars required on each side of a swing high/low before it's confirmed as a zone anchor (delayed confirmation by length bars, standard pivot-detection lag).
- maxZones ("Max Zones per Type", default: 10, range 1-40): how many BSL zones and how many SSL zones (each capped separately) are kept; oldest is dropped first once full.
- showSwept ("Show Swept Zones", default: true): keeps a zone's dashed outline and faded quadrants visible after it's swept, instead of hiding them.
- filterOverlaps ("Filter Overlapping Zones", default: true): when a new pivot would create a zone overlapping an existing active one, only the more extreme zone (further from price) survives - the other is either skipped (new one less extreme) or replaced (new one more extreme).
- showDecay ("Show Zone Decay", default: true) / zoneCapacity ("Zone Volume Capacity", default: 5.0, min 1.0): whether to display each zone's health percentage, and how large its volume "budget" is - set as this multiple of the average volume (over the pivot-length window) at the moment the zone was created.
- enableReversals ("Enable Reversal Detection", default: true): master switch for the entire 4-pattern signal system (and, by extension, the trade simulation and dashboard, since they depend on signals firing).
- dashboardInput ("Show Dashboard", default: true): shows the performance table, or clears it entirely when off (the underlying win/loss tracking keeps running either way - only the visible table is affected).
- dashboardPositionInput / dashboardSizeInput: the dashboard's screen corner and text size.
- dashboardWindowInput ("Eval Window (Bars)", default: 10): the maximum number of bars a simulated trade is given to reach the hold requirement before it's marked a timeout (counted as a loss, since it never reached "won").
- dashboardHoldInput ("Hold Time (Bars)", default: 3): how many consecutive bars price must stay in the expected profitable direction for a simulated trade to count as a win. A single bar back against the trade resets this streak to zero.
- dashboardHighlightInput ("Highlight Eval Bars", default: false): colors bars yellow while an active trade isn't currently in profit, aqua while it is.
- bslColor / sslColor: outline colors for BSL (default red) and SSL (default green) zones.
- buyDeltaColor / sellDeltaColor: fill colors for a quadrant when buy or sell volume dominates it.
Main Logic Blocks
🎯 Flow 1: What You See
- BSL zones (red) above swing highs and SSL zones (green) below swing lows, each split into 4 quadrant boxes showing live buy/sell delta; an optional health percentage per zone; ABS/EXH/DIV/REJ labels at confirmed reversal points; optional yellow/aqua trade evaluation bar highlighting; and an optional win-rate performance table.
🌱 Flow 2: Creating a New Zone
- On a confirmed pivot high, the zone spans from that candle's body top (the higher of its open/close) up to its actual wick high - i.e. just the wick portion above the candle's body, padded to a small minimum height if the wick is too thin relative to ATR. A pivot low creates the mirrored SSL zone using the candle's body bottom down to its wick low.
- Each new zone is split into 4 equal-height quadrant boxes with small visual gaps between them, an outer outline box, and a "100%" health label - then added to the front of its type's zone list (newest first), with the oldest zone dropped if the list now exceeds maxZones.
🔍 Flow 3: Keeping Only the Most Significant Overlapping Zone
- If filterOverlaps is on and the new candidate zone's price range overlaps an existing active (not-yet-swept) zone of the same type: the more extreme zone wins. If the new pivot is more extreme (a higher high for BSL, a lower low for SSL), the old zone is deleted and the new one is created; otherwise, the new candidate is discarded entirely and the existing zone is left untouched.
📊 Flow 4: Attributing Volume Delta to Quadrants
- Each bar's overall delta is first estimated the same way as a close-position proxy: volume × (close-open)/(high-low) - a signed value representing how directional that bar's volume was.
- For every active zone, the current bar's high-low range is checked against each of the 4 quadrants individually; wherever there's an actual price overlap, that quadrant receives a proportional share of the bar's total delta and volume, weighted by how much of the bar's total range fell inside that specific quadrant - so a single bar touching 2 quadrants splits its delta between them rather than crediting it all to one.
🌡️ Flow 5: Zone Health / Decay
- Independently of the per-quadrant delta split, every bit of volume that overlaps the zone at all (regardless of direction) also adds to the zone's cumulative volumeTraded total. Health = 100% minus that total's percentage of the zone's capacity budget, floored at 0% - a zone that's absorbed a lot of two-way volume without breaking shows a low health reading even if its quadrant deltas are relatively balanced.
🔄 Flow 6: Sweep Detection & the 4 Reversal Patterns
- A zone is "swept" the instant the bar's high closes beyond its top (BSL) or its low beyond its bottom (SSL). Once swept, it stops accumulating delta/health updates and (if enabled) switches to its faded, dashed "memory" appearance.
- While the zone is being touched or right at the moment it sweeps, and only if it hasn't already fired a signal, four patterns are checked in order (first match wins):
- 1. ABS (Absorption): price is sweeping through the zone's outer edge, but the volume delta in that specific outer quadrant runs contrary to the sweep direction, and makes up more than 20% of the zone's total activity - read as aggressive opposite-side orders absorbing the breakout attempt.
- 2. EXH (Exhaustion): price is sweeping through, but the outer quadrant's activity is under 10% of the zone's total - the breakout happened on unusually thin volume, suggesting little real conviction behind it.
- 3. DIV (Divergence): price wicked beyond the zone but closed back inside it, while the outer quadrant shows over 60% of the zone's activity in the breakout's own direction - heavy, apparently-committed volume that still failed to hold the breakout.
- 4. REJ (Snapback Rejection): price swept through and closed back on the "inside" half of the zone, and this single bar's own delta is both contrary to the sweep direction and represents over 20% of that bar's own volume - an immediate, forceful rejection bar right at the sweep.
- Whichever pattern fires (if any) locks the zone from ever signaling again, fires an alert, and draws a size/opacity-scaled label (scaled by a 0-1 "significance" score specific to that pattern's own formula) with a detailed tooltip explaining the reasoning.
🎨 Flow 7: Per-Quadrant Visual Feedback
- Each quadrant's fill color and opacity are driven by its own delta relative to whichever quadrant in that zone has the largest absolute delta so far - the more a quadrant's volume leans buy or sell, and the larger that lean is relative to the zone's most active quadrant, the more saturated its color becomes. Quadrants with no meaningful delta yet fall back to a neutral positional gradient. Once a zone is swept, all of this fades to a flat, faint appearance (or disappears entirely if showSwept is off).
🧪 Flow 8: Simulated Trade Tracking & the Win-Rate Dashboard
- Every fired signal creates a lightweight simulated "trade" starting the bar after the signal (entry = that bar's close, direction = away from the swept zone). Starting the following bar, each active trade is checked every bar: if price is currently on the profitable side of entry, its consecutive-profit-bar counter increases; any bar back against it resets that counter to zero.
- A trade is marked a win once its consecutive-profit counter reaches dashboardHoldInput. If that hasn't happened within dashboardWindowInput bars of the entry, the trade times out (counted as a non-win). Either way, the outcome is tallied into that signal type's running total/win counters, which feed the dashboard's live win-rate percentages.
- If bar highlighting is enabled, the current bar is colored yellow while any trade is actively "not yet in profit" and aqua while one is currently holding in profit (aqua takes visual priority when multiple trades disagree in the same bar).
🔔 Flow 9: Alerts
- All 7 alert conditions - testing a BSL/SSL zone, a BSL/SSL zone being swept, and a reversal signal firing - are raised through dynamic alert() calls only. There are no static alertcondition() definitions in this script, so none of these appear as individually-selectable options in TradingView's standard alert dialog; you would need to create an alert on this indicator using the "Any alert() function call" condition to receive any of them.
Outputs & Usage Roles
- BSL/SSL zones + quadrant deltas: where stop-hunting liquidity likely sits, and a live read of who's actually trading through it.
- Zone health: a secondary durability read independent of the delta split.
- Reversal signals: the headline actionable output, classified into 4 distinct, mutually-exclusive pattern types.
- Win-rate dashboard: genuine, forward-tracked validation of how well each pattern type has actually performed on this chart, not just a historical co-occurrence count.
- Alerts: all delivered via dynamic alert() only (see Flow 9).
// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © LuxAlgo
//@version=6
indicator("Liquidity Delta Profiler [LuxAlgo]", "LuxAlgo - Liquidity Delta Profiler", overlay = true, max_boxes_count = 500, max_labels_count = 500)
//---------------------------------------------------------------------------------------------------------------------}
// Constants
//---------------------------------------------------------------------------------------------------------------------{
color DATA = #DBDBDB
color HEADERS = #808080
color BACKGROUND = #161616
color BORDERS = #2E2E2E
string TOP_RIGHT = 'Top Right'
string BOTTOM_RIGHT = 'Bottom Right'
string BOTTOM_LEFT = 'Bottom Left'
string TINY = 'Tiny'
string SMALL = 'Small'
string NORMAL = 'Normal'
string LARGE = 'Large'
string HUGE = 'Huge'
string DASHBOARD_GROUP = 'Dashboard'
//---------------------------------------------------------------------------------------------------------------------}
// Settings
//---------------------------------------------------------------------------------------------------------------------{
length = input.int(15, "Pivot Length", minval = 2, tooltip = "Lookback and lookforward length for detecting major swing highs/lows.")
maxZones = input.int(10, "Max Zones per Type", minval = 1, maxval = 40, tooltip = "Maximum number of active/historical buy and sell zones to keep on the chart.")
showSwept = input.bool(true, "Show Swept Zones", tooltip = "Keep zones visible (with dashed outlines and reduced opacity) after price sweeps them.")
filterOverlaps = input.bool(true, "Filter Overlapping Zones", tooltip = "When enabled, prevents creating new zones that overlap with existing active zones. Only the most significant (highest for BSL, lowest for SSL) level is kept.")
showDecay = input.bool(true, "Show Zone Decay", tooltip = "Displays the remaining 'health' of active zones based on the volume traded inside them. Health drops from 100% to 0%.")
zoneCapacity = input.float(5.0, "Zone Volume Capacity", minval = 1.0, tooltip = "Multiplier for average volume to determine how much volume a zone can absorb before reaching 0% health.")
enableReversals = input.bool(true, "Enable Reversal Detection", group = "Reversals", tooltip = "Detects unusual volume delta patterns during liquidity sweeps to signal potential reversals. Plots a bubble with signal type (ABS, EXH, DIV, REJ) and hover tooltip.")
dashboardInput = input.bool( true, 'Show Dashboard', group = DASHBOARD_GROUP, tooltip = 'Enable or disable the dashboard.')
dashboardPositionInput = input.string( TOP_RIGHT, 'Position', group = DASHBOARD_GROUP, tooltip = 'Select the dashboard location.', options = [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT])
dashboardSizeInput = input.string( TINY, 'Size', group = DASHBOARD_GROUP, tooltip = 'Select the dashboard size.', options = [TINY, SMALL, NORMAL, LARGE, HUGE])
dashboardWindowInput = input.int( 10, 'Eval Window (Bars)', group = DASHBOARD_GROUP, minval = 1, tooltip = 'Maximum number of bars to wait for the reversal to occur.')
dashboardHoldInput = input.int( 3, 'Hold Time (Bars)', group = DASHBOARD_GROUP, minval = 1, tooltip = 'Number of consecutive bars price must stay in profit (opposite direction) to be considered a win.')
dashboardHighlightInput = input.bool( false, 'Highlight Eval Bars',group = DASHBOARD_GROUP, tooltip = 'Highlights bars actively evaluated. Yellow = Evaluating, Aqua = Holding in profit.')
bslColor = input.color(color.new(#f23645, 0), "BSL Outline Color", group = "Style", tooltip = "Color for Buy-Side Liquidity zones (above price).")
sslColor = input.color(color.new(#089981, 0), "SSL Outline Color", group = "Style", tooltip = "Color for Sell-Side Liquidity zones (below price).")
buyDeltaColor = input.color(color.new(#089981, 0), "Buy Delta Fill", group = "Style", tooltip = "Fill color when buy volume dominates a zone quadrant.")
sellDeltaColor = input.color(color.new(#f23645, 0), "Sell Delta Fill", group = "Style", tooltip = "Fill color when sell volume dominates a zone quadrant.")
//---------------------------------------------------------------------------------------------------------------------}
// Types
//---------------------------------------------------------------------------------------------------------------------{
type Zone
float top
float bottom
int left
int right
bool swept
bool signaled
box[] quads
float[] deltas
box outline
float volumeTraded
float capacity
bool wasHit
label decayLabel
type Trade
string type
int dir
float entry
int entryBar
bool active
bool won
int consecBars
//---------------------------------------------------------------------------------------------------------------------}
// Variables
//---------------------------------------------------------------------------------------------------------------------{
var Zone[] bslZones = array.new()
var Zone[] sslZones = array.new()
var activeTrades = array.new()
var int absTotal = 0
var int absWins = 0
var int exhTotal = 0
var int exhWins = 0
var int divTotal = 0
var int divWins = 0
var int rejTotal = 0
var int rejWins = 0
var string parsedDashboardPosition = switch dashboardPositionInput
TOP_RIGHT => position.top_right
BOTTOM_RIGHT => position.bottom_right
BOTTOM_LEFT => position.bottom_left
=> position.top_right
var string parsedDashboardSize = switch dashboardSizeInput
TINY => size.tiny
SMALL => size.small
NORMAL => size.normal
LARGE => size.large
HUGE => size.huge
=> size.normal
var table t_able = table.new(parsedDashboardPosition, 4, 11, bgcolor = dashboardInput ? BACKGROUND : na, border_width = 0, frame_color = dashboardInput ? BORDERS : na, frame_width = 1, force_overlay = true)
//---------------------------------------------------------------------------------------------------------------------}
// Methods / Functions
//---------------------------------------------------------------------------------------------------------------------{
cell(table t_able, int col, int r, string data, color txtColor = color.white, string align = text.align_right, color background = na, float h = 0) =>
t_able.cell(col, r, data, text_color = txtColor, text_size = parsedDashboardSize, text_halign = align, bgcolor = background, height = h)
divider(table t_able, int r, int lastColumn) =>
string rowDivider = '━━━━━━━━━━━━━━━━'
t_able.merge_cells(0, r, lastColumn, r)
cell(t_able, 0, r, rowDivider, txtColor = BORDERS, align = text.align_center, h = 0.5)
method evaluateReversals(Zone z, bool isBsl, float barH, float barL, float barC, float barO, float barDelta, float barVol) =>
if enableReversals and not z.signaled
float totalD = 0.0
float absTotalD = 0.0
for d in z.deltas
totalD += d
absTotalD += math.abs(d)
if absTotalD > 0
int outerIdx = isBsl ? 3 : 0
float outerD = z.deltas.get(outerIdx)
bool isSweeping = isBsl ? barH > z.top : barL < z.bottom
bool closesInside = isBsl ? (barC <= z.top and barC >= z.bottom) : (barC >= z.bottom and barC <= z.top)
float midPoint = (z.top + z.bottom) / 2
bool closesRejecting = isBsl ? barC < midPoint : barC > midPoint
string signalType = ""
string tooltipTxt = ""
float significance = 0.0
color sigColor = isBsl ? color.red : color.green // Reversing from Resistance is Bearish (Red), from Support is Bullish (Green)
// 1. Absorption at the Extreme (Trap)
if isSweeping and ((isBsl and outerD < 0) or (not isBsl and outerD > 0))
float ratio = math.abs(outerD) / (absTotalD + 0.0001)
if ratio > 0.2
signalType := "ABS"
tooltipTxt := "Absorption at Extreme\n" + (isBsl ? "Sellers" : "Buyers") + " aggressively absorbed the sweep.\nOuter Quadrant Delta: " + str.tostring(outerD, format.volume)
significance := ratio * 2
// 2. Exhaustion (Dry Sweep)
if signalType == "" and isSweeping
float ratio = math.abs(outerD) / (absTotalD + 0.0001)
if ratio < 0.1
signalType := "EXH"
tooltipTxt := "Exhaustion (Dry Sweep)\nMinimal volume at the extreme edge.\nOuter Quadrant Delta: " + str.tostring(outerD, format.volume)
significance := 1.0 - (ratio * 5)
// 3. Delta Divergence (FOMO)
if signalType == "" and closesInside
float ratio = math.abs(outerD) / (absTotalD + 0.0001)
if ratio > 0.6 and ((isBsl and outerD > 0) or (not isBsl and outerD < 0))
signalType := "DIV"
tooltipTxt := "Delta Divergence (FOMO)\nHigh volume trapped at the extreme, but price failed to breakout.\nOuter Quadrant Delta: " + str.tostring(outerD, format.volume)
significance := ratio
// 4. Snapback (Climax + Rejection)
if signalType == "" and isSweeping and closesRejecting
float barRatio = math.abs(barDelta) / (barVol + 0.0001)
if ((isBsl and barDelta < 0) or (not isBsl and barDelta > 0)) and barRatio > 0.2
signalType := "REJ"
tooltipTxt := "Snapback Rejection\nSweep followed by immediate strong rejection.\nBar Delta: " + str.tostring(barDelta, format.volume)
significance := barRatio * 2
// Plot Signal
if signalType != ""
z.signaled := true
alert("Reversal Signal (" + signalType + ") detected at " + str.tostring(barC), alert.freq_once_per_bar)
significance := math.min(math.max(significance, 0.0), 1.0)
string sSize = size.tiny
int trans = 50
if significance > 0.7
sSize := size.normal
trans := 10
else if significance > 0.4
sSize := size.small
trans := 30
color finalColor = color.new(sigColor, trans)
float yLoc = isBsl ? barH : barL
string labelStyle = isBsl ? label.style_label_down : label.style_label_up
label.new(bar_index, yLoc, text=signalType, color=finalColor, style=labelStyle, textcolor=color.white, size=sSize, tooltip=tooltipTxt)
// Track Trade Performance
float entry = barC
activeTrades.push(Trade.new(signalType, isBsl ? -1 : 1, entry, bar_index, true, false, 0))
method updateVisuals(Zone z, bool isBsl, bool showS, color buyC, color sellC, color bslC, color sslC) =>
float maxD = 0.0
for d in z.deltas
maxD := math.max(maxD, math.abs(d))
for i = 0 to 3
float d = z.deltas.get(i)
color c = na
string txt = ""
color tColor = na
if maxD > 0 and math.abs(d) > 0.001
color baseC = d > 0 ? buyC : sellC
int trans = z.swept ? 90 : 100 - int((math.abs(d) / maxD) * 60)
c := color.rgb(color.r(baseC), color.g(baseC), color.b(baseC), trans)
txt := (d > 0 ? "+" : d < 0 ? "-" : "") + str.tostring(math.abs(d), format.volume)
tColor := color.new(baseC, z.swept ? 50 : 0)
else
color baseC = isBsl ? bslC : sslC
int defaultTrans = 60 + (isBsl ? (3 - i) : i) * 10
c := color.rgb(color.r(baseC), color.g(baseC), color.b(baseC), z.swept ? 90 : defaultTrans)
txt := "0"
tColor := color.new(baseC, z.swept ? 80 : 50)
box q = z.quads.get(i)
q.set_bgcolor(not showS and z.swept ? na : c)
q.set_text(not showS and z.swept ? "" : txt)
q.set_text_color(not showS and z.swept ? na : tColor)
if z.swept
q.set_border_color(not showS ? na : color.new(isBsl ? bslC : sslC, 80))
q.set_border_style(line.style_dashed)
else
q.set_border_color(color.new(isBsl ? bslC : sslC, 60))
q.set_border_style(line.style_solid)
if z.swept
if showS
z.outline.set_border_style(line.style_dashed)
z.outline.set_border_color(color.new(isBsl ? bslC : sslC, 80))
else
z.outline.set_border_color(na)
barOverlap(float barH, float barL, float qTop, float qBot) =>
float overlapTop = math.min(barH, qTop)
float overlapBot = math.max(barL, qBot)
overlapTop > overlapBot ? (overlapTop - overlapBot) : 0.0
//---------------------------------------------------------------------------------------------------------------------}
// Logic
//---------------------------------------------------------------------------------------------------------------------{
float atr = ta.atr(14)
float ph = ta.pivothigh(high, length, length)
float pl = ta.pivotlow(low, length, length)
float avgVol = nz(ta.sma(volume, length))
if avgVol == 0
avgVol := 1
float currentZoneCap = avgVol * zoneCapacity
if not na(ph) and bar_index >= length
int pivotIdx = bar_index - length
float pHigh = high[length]
float pBot = math.max(close[length], open[length])
if pHigh - pBot < atr[length] * 0.1
pBot := pHigh - atr[length] * 0.1
bool skip = false
if filterOverlaps and bslZones.size() > 0
for i = bslZones.size() - 1 to 0
if i < bslZones.size()
Zone ex = bslZones.get(i)
if not ex.swept
bool overlaps = math.max(pBot, ex.bottom) <= math.min(pHigh, ex.top)
if overlaps
if pHigh > ex.top
for b in ex.quads
b.delete()
ex.outline.delete()
ex.decayLabel.delete()
bslZones.remove(i)
else
skip := true
break
if not skip
box[] quads = array.new(4)
float[] deltas = array.new(4, 0.0)
float step = (pHigh - pBot) / 4
for i = 0 to 3
float qBot = pBot + i * step
float qTop = pBot + (i + 1) * step
color baseC = bslColor
int trans = 60 + (3 - i) * 10
color c = color.rgb(color.r(baseC), color.g(baseC), color.b(baseC), trans)
float gap = step * 0.05
float bTop = qTop - gap
float bBot = qBot + gap
if i == 0
bBot := qBot
if i == 3
bTop := qTop
quads.set(i, box.new(pivotIdx, bTop, pivotIdx, bBot, border_color = color.new(baseC, 60), bgcolor = c, text = "0", text_color = color.new(baseC, 50), text_halign = text.align_right, text_size = size.auto))
box outline = box.new(pivotIdx, pHigh, pivotIdx, pBot, border_color = color.new(bslColor, 80), bgcolor = na)
label dLabel = label.new(bar_index, pBot + (pHigh - pBot)/2, text = "100%", style = label.style_label_left, color = color.new(bslColor, 80), textcolor = color.white, size = size.small)
if not showDecay
dLabel.set_x(na)
Zone z = Zone.new(pHigh, pBot, pivotIdx, pivotIdx, false, false, quads, deltas, outline, 0.0, currentZoneCap, false, dLabel)
bslZones.unshift(z)
if bslZones.size() > maxZones
Zone removed = bslZones.pop()
for b in removed.quads
b.delete()
removed.outline.delete()
removed.decayLabel.delete()
if not na(pl) and bar_index >= length
int pivotIdx = bar_index - length
float pLow = low[length]
float pTop = math.min(close[length], open[length])
if pTop - pLow < atr[length] * 0.1
pTop := pLow + atr[length] * 0.1
bool skip = false
if filterOverlaps and sslZones.size() > 0
for i = sslZones.size() - 1 to 0
if i < sslZones.size()
Zone ex = sslZones.get(i)
if not ex.swept
bool overlaps = math.max(pLow, ex.bottom) <= math.min(pTop, ex.top)
if overlaps
if pLow < ex.bottom
for b in ex.quads
b.delete()
ex.outline.delete()
ex.decayLabel.delete()
sslZones.remove(i)
else
skip := true
break
if not skip
box[] quads = array.new(4)
float[] deltas = array.new(4, 0.0)
float step = (pTop - pLow) / 4
for i = 0 to 3
float qBot = pLow + i * step
float qTop = pLow + (i + 1) * step
color baseC = sslColor
int trans = 60 + i * 10
color c = color.rgb(color.r(baseC), color.g(baseC), color.b(baseC), trans)
float gap = step * 0.05
float bTop = qTop - gap
float bBot = qBot + gap
if i == 0
bBot := qBot
if i == 3
bTop := qTop
quads.set(i, box.new(pivotIdx, bTop, pivotIdx, bBot, border_color = color.new(baseC, 60), bgcolor = c, text = "0", text_color = color.new(baseC, 50), text_halign = text.align_right, text_size = size.auto))
box outline = box.new(pivotIdx, pTop, pivotIdx, pLow, border_color = color.new(sslColor, 80), bgcolor = na)
label dLabel = label.new(bar_index, pLow + (pTop - pLow)/2, text = "100%", style = label.style_label_left, color = color.new(sslColor, 80), textcolor = color.white, size = size.small)
if not showDecay
dLabel.set_x(na)
Zone z = Zone.new(pTop, pLow, pivotIdx, pivotIdx, false, false, quads, deltas, outline, 0.0, currentZoneCap, false, dLabel)
sslZones.unshift(z)
if sslZones.size() > maxZones
Zone removed = sslZones.pop()
for b in removed.quads
b.delete()
removed.outline.delete()
removed.decayLabel.delete()
float vol = nz(volume)
float totalRange = high - low
float barDelta = totalRange == 0 ? 0 : vol * (close - open) / totalRange
if bslZones.size() > 0
for i = bslZones.size() - 1 to 0
if i < bslZones.size()
Zone z = bslZones.get(i)
if not z.swept
z.right := bar_index
z.outline.set_right(bar_index)
for b in z.quads
b.set_right(bar_index)
bool hit = false
float step = (z.top - z.bottom) / 4
for j = 0 to 3
float qBot = z.bottom + j * step
float qTop = z.bottom + (j + 1) * step
float overlap = barOverlap(high, low, qTop, qBot)
if overlap > 0
hit := true
float overlapRatio = totalRange == 0 ? 0 : overlap / totalRange
float qDelta = barDelta * overlapRatio
z.deltas.set(j, z.deltas.get(j) + qDelta)
z.volumeTraded += vol * overlapRatio
if hit and not z.wasHit
alert("Price testing Resistance BSL Zone at " + str.tostring(z.top), alert.freq_once_per_bar)
if high > z.top
z.swept := true
alert("Resistance BSL Zone Swept at " + str.tostring(z.top), alert.freq_once_per_bar)
if not z.swept
if showDecay
int health = int(math.max(0, 100 - (z.volumeTraded / z.capacity * 100)))
z.decayLabel.set_x(bar_index + 1)
z.decayLabel.set_text(str.tostring(health) + "%")
else
z.decayLabel.set_x(na)
else
z.decayLabel.delete()
z.wasHit := hit
if hit or z.swept
z.evaluateReversals(true, high, low, close, open, barDelta, vol)
z.updateVisuals(true, showSwept, buyDeltaColor, sellDeltaColor, bslColor, sslColor)
if sslZones.size() > 0
for i = sslZones.size() - 1 to 0
if i < sslZones.size()
Zone z = sslZones.get(i)
if not z.swept
z.right := bar_index
z.outline.set_right(bar_index)
for b in z.quads
b.set_right(bar_index)
bool hit = false
float step = (z.top - z.bottom) / 4
for j = 0 to 3
float qBot = z.bottom + j * step
float qTop = z.bottom + (j + 1) * step
float overlap = barOverlap(high, low, qTop, qBot)
if overlap > 0
hit := true
float overlapRatio = totalRange == 0 ? 0 : overlap / totalRange
float qDelta = barDelta * overlapRatio
z.deltas.set(j, z.deltas.get(j) + qDelta)
z.volumeTraded += vol * overlapRatio
if hit and not z.wasHit
alert("Price testing Support SSL Zone at " + str.tostring(z.bottom), alert.freq_once_per_bar)
if low < z.bottom
z.swept := true
alert("Support SSL Zone Swept at " + str.tostring(z.bottom), alert.freq_once_per_bar)
if not z.swept
if showDecay
int health = int(math.max(0, 100 - (z.volumeTraded / z.capacity * 100)))
z.decayLabel.set_x(bar_index + 1)
z.decayLabel.set_text(str.tostring(health) + "%")
else
z.decayLabel.set_x(na)
else
z.decayLabel.delete()
z.wasHit := hit
if hit or z.swept
z.evaluateReversals(false, high, low, close, open, barDelta, vol)
z.updateVisuals(false, showSwept, buyDeltaColor, sellDeltaColor, bslColor, sslColor)
// Process Active Trades
color barHighlight = na
if activeTrades.size() > 0
for i = activeTrades.size() - 1 to 0
if i < activeTrades.size()
Trade t = activeTrades.get(i)
if t.active
if bar_index > t.entryBar // Wait until the next bar to start evaluating
bool inProfit = (t.dir == 1 and close > t.entry) or (t.dir == -1 and close < t.entry)
if inProfit
t.consecBars += 1
if na(barHighlight) or barHighlight == color.yellow
barHighlight := color.aqua
else
t.consecBars := 0
if na(barHighlight)
barHighlight := color.yellow
if t.consecBars >= dashboardHoldInput
t.active := false
t.won := true
else if bar_index - t.entryBar >= dashboardWindowInput
t.active := false // Time ran out
if not t.active
if t.type == "ABS"
absTotal += 1
if t.won
absWins += 1
else if t.type == "EXH"
exhTotal += 1
if t.won
exhWins += 1
else if t.type == "DIV"
divTotal += 1
if t.won
divWins += 1
else if t.type == "REJ"
rejTotal += 1
if t.won
rejWins += 1
activeTrades.remove(i)
barcolor(dashboardHighlightInput ? barHighlight : na)
if barstate.islast
if dashboardInput
t_able.merge_cells(0, 0, 3, 0)
cell(t_able, 0, 0, 'Reversal Performance (Time-Based)', txtColor = DATA, align = text.align_center)
divider(t_able, 1, 3)
cell(t_able, 0, 2, 'Signal', txtColor = HEADERS, align = text.align_left)
cell(t_able, 1, 2, 'Total', txtColor = HEADERS, align = text.align_right)
cell(t_able, 2, 2, 'Wins', txtColor = HEADERS, align = text.align_right)
cell(t_able, 3, 2, 'Win %', txtColor = HEADERS, align = text.align_right)
divider(t_able, 3, 3)
float absRate = absTotal > 0 ? (absWins / absTotal) * 100 : 0
cell(t_able, 0, 4, 'ABS', txtColor = DATA, align = text.align_left)
cell(t_able, 1, 4, str.tostring(absTotal), txtColor = DATA, align = text.align_right)
cell(t_able, 2, 4, str.tostring(absWins), txtColor = DATA, align = text.align_right)
cell(t_able, 3, 4, str.tostring(absRate, '#.##') + '%', txtColor = absTotal > 0 ? (absRate >= 50 ? color.green : color.red) : DATA, align = text.align_right)
divider(t_able, 5, 3)
float exhRate = exhTotal > 0 ? (exhWins / exhTotal) * 100 : 0
cell(t_able, 0, 6, 'EXH', txtColor = DATA, align = text.align_left)
cell(t_able, 1, 6, str.tostring(exhTotal), txtColor = DATA, align = text.align_right)
cell(t_able, 2, 6, str.tostring(exhWins), txtColor = DATA, align = text.align_right)
cell(t_able, 3, 6, str.tostring(exhRate, '#.##') + '%', txtColor = exhTotal > 0 ? (exhRate >= 50 ? color.green : color.red) : DATA, align = text.align_right)
divider(t_able, 7, 3)
float divRate = divTotal > 0 ? (divWins / divTotal) * 100 : 0
cell(t_able, 0, 8, 'DIV', txtColor = DATA, align = text.align_left)
cell(t_able, 1, 8, str.tostring(divTotal), txtColor = DATA, align = text.align_right)
cell(t_able, 2, 8, str.tostring(divWins), txtColor = DATA, align = text.align_right)
cell(t_able, 3, 8, str.tostring(divRate, '#.##') + '%', txtColor = divTotal > 0 ? (divRate >= 50 ? color.green : color.red) : DATA, align = text.align_right)
divider(t_able, 9, 3)
float rejRate = rejTotal > 0 ? (rejWins / rejTotal) * 100 : 0
cell(t_able, 0, 10, 'REJ', txtColor = DATA, align = text.align_left)
cell(t_able, 1, 10, str.tostring(rejTotal), txtColor = DATA, align = text.align_right)
cell(t_able, 2, 10, str.tostring(rejWins), txtColor = DATA, align = text.align_right)
cell(t_able, 3, 10, str.tostring(rejRate, '#.##') + '%', txtColor = rejTotal > 0 ? (rejRate >= 50 ? color.green : color.red) : DATA, align = text.align_right)
else
t_able.clear(0, 0, 3, 10)
//---------------------------------------------------------------------------------------------------------------------}
