Strategy · Pine Script v5 · On the chart

The Judas Indicator

by regardhodler· code credited to The Judas Indicator — HFT Grade Rewrite

The Judas Indicator strategy calculates a multi-factor composite score combining Volume Profile High Volume Nodes, Cumulative Volume Delta (CVD), pivot proximity, volume absorption, order imbalance, and higher-timeframe trend EMAs.

View on GitHub

GitHub stars3on the repository
Pine versionv5runs as is
Lines of code570
Settings39inputs
LicenceNone statedfile: MPL-2.0
Last update8 months ago28 Feb 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

570 lines of Pine Script v5 from judas-strategy.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

judas-strategy.pine
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © The Judas Indicator — HFT Grade Rewrite

//@version=5
strategy("The Judas Indicator",
     shorttitle          = "JUDAS",
     overlay             = true,
     max_boxes_count     = 500,
     max_lines_count     = 50,
     max_labels_count    = 50,
     default_qty_type    = strategy.fixed,
     default_qty_value   = 0,
     initial_capital     = 100000,
     commission_type     = strategy.commission.percent,
     commission_value    = 0.04,
     slippage            = 1,
     pyramiding          = 0,
     calc_on_every_tick  = false)

// ═══════════════════════════════════════════════════════════════════════════════
// INPUTS
// ═══════════════════════════════════════════════════════════════════════════════

// ── Strategy ────────────────────────────────────────────────────────────────
atrPeriod       = input.int(14, "ATR Period", group="Strategy")
entryThreshold  = input.float(0.45, "Entry Threshold", minval=0.1, maxval=0.9, step=0.05, group="Strategy")

// ── Volume Profile ──────────────────────────────────────────────────────────
vpLookback  = input.int(100, "VP Lookback", minval=20, maxval=500, group="Volume Profile")
vpNumRows   = input.int(24, "VP Number of Rows", minval=8, maxval=50, group="Volume Profile")
vpHvnPct    = input.float(75.0, "HVN Threshold %", minval=50, maxval=95, group="Volume Profile")
vpVaPct     = input.float(70.0, "Value Area %", minval=50, maxval=90, group="Volume Profile")

// ── CVD ─────────────────────────────────────────────────────────────────────
cvdBarWeight    = input.float(0.40, "Bar Delta Weight", group="CVD")
cvdVwapWeight   = input.float(0.35, "VWAP Delta Weight", group="CVD")
cvdTickWeight   = input.float(0.25, "Tick Approx Weight", group="CVD")
cvdSmoothLen    = input.int(14, "Smoothing Length", group="CVD")
cvdDivLookback  = input.int(20, "Divergence Lookback", group="CVD")

// ── Pivots ──────────────────────────────────────────────────────────────────
pvtLeftBars  = input.int(3, "Left Bars", minval=1, maxval=10, group="Pivots")
pvtRightBars = input.int(2, "Right Bars", minval=1, maxval=10, group="Pivots")
pvtProxAtr   = input.float(0.5, "Proximity (ATR mult)", minval=0.1, maxval=2.0, group="Pivots")

// ── Absorption ──────────────────────────────────────────────────────────────
absLookback  = input.int(20, "Lookback", minval=5, maxval=100, group="Absorption")
absThreshold = input.float(2.0, "Ratio Threshold", minval=1.0, maxval=5.0, step=0.1, group="Absorption")

// ── Order Imbalance ─────────────────────────────────────────────────────────
oirSmoothing = input.int(10, "EMA Length", minval=3, maxval=50, group="Order Imbalance")
oirThreshold = input.float(0.6, "Extreme Threshold", minval=0.2, maxval=0.9, step=0.05, group="Order Imbalance")

// ── Multi-Timeframe ─────────────────────────────────────────────────────────
htfTimeframe = input.timeframe("60", "HTF Timeframe", group="Multi-Timeframe")
htfEmaFastN  = input.int(20, "HTF Fast EMA", group="Multi-Timeframe")
htfEmaSlowN  = input.int(50, "HTF Slow EMA", group="Multi-Timeframe")

// ── Signal Weights ──────────────────────────────────────────────────────────
wHvn        = input.float(0.20, "Volume Profile", minval=0, maxval=1, step=0.05, group="Signal Weights")
wCvd        = input.float(0.25, "CVD", minval=0, maxval=1, step=0.05, group="Signal Weights")
wPivot      = input.float(0.15, "Pivots", minval=0, maxval=1, step=0.05, group="Signal Weights")
wAbsorption = input.float(0.15, "Absorption", minval=0, maxval=1, step=0.05, group="Signal Weights")
wOir        = input.float(0.10, "Order Imbalance", minval=0, maxval=1, step=0.05, group="Signal Weights")
wMtf        = input.float(0.15, "Multi-Timeframe", minval=0, maxval=1, step=0.05, group="Signal Weights")

// ── Risk Management ─────────────────────────────────────────────────────────
riskPct         = input.float(1.0, "Risk Per Trade %", minval=0.1, maxval=10.0, step=0.1, group="Risk")
slMult          = input.float(1.5, "Stop Loss (ATR mult)", minval=0.5, maxval=5.0, step=0.1, group="Risk")
tpMult          = input.float(3.0, "Take Profit (ATR mult)", minval=1.0, maxval=10.0, step=0.1, group="Risk")
useTrailStop    = input.bool(true, "Use Trailing Stop", group="Risk")
trailMult       = input.float(2.0, "Trail Points (ATR mult)", minval=0.5, maxval=5.0, step=0.1, group="Risk")
trailOffsetMult = input.float(0.5, "Trail Offset (ATR mult)", minval=0.1, maxval=2.0, step=0.1, group="Risk")

// ── Visualization ───────────────────────────────────────────────────────────
showProfile   = input.bool(true, "Show Volume Profile", group="Visualization")
showDashboard = input.bool(true, "Show Dashboard", group="Visualization")
profileWidth  = input.int(50, "Profile Width (bars)", minval=10, maxval=200, group="Visualization")
buyColor      = input.color(color.new(color.green, 40), "Buy Color", group="Visualization")
sellColor     = input.color(color.new(color.red, 40), "Sell Color", group="Visualization")
pocColor      = input.color(color.yellow, "POC Color", group="Visualization")

// ═══════════════════════════════════════════════════════════════════════════════
// GLOBAL SERIES (must run every bar)
// ═══════════════════════════════════════════════════════════════════════════════

atrVal    = ta.atr(atrPeriod)
dailyVwap = ta.vwap

// MTF data — request.security must be at global scope
htfEma20 = request.security(syminfo.tickerid, htfTimeframe, ta.ema(close, htfEmaFastN))
htfEma50 = request.security(syminfo.tickerid, htfTimeframe, ta.ema(close, htfEmaSlowN))
htfRsi   = request.security(syminfo.tickerid, htfTimeframe, ta.rsi(close, 14))
htfVwap  = request.security(syminfo.tickerid, htfTimeframe, ta.vwap)

// Shared derived series
barRange = high - low

// Dashboard color helper (global scope function)
scoreColor(float s) =>
    s > 0.3 ? color.green : s < -0.3 ? color.red : color.gray

// ═══════════════════════════════════════════════════════════════════════════════
// MODULE 1: ROLLING VOLUME PROFILE
// Bins price range into ATR-scaled buckets. Tracks per-bin buy/sell volume.
// Extracts POC, VAH, VAL, HVN. Score: buy/sell delta at price's bin.
// Fixes: rolling grid (bug 3), per-bin delta (bug 4), no unlimited boxes (bug 2)
// ═══════════════════════════════════════════════════════════════════════════════

vpHighest  = ta.highest(high, vpLookback)
vpLowest   = ta.lowest(low, vpLookback)
vpRowHeight = (vpHighest - vpLowest) / vpNumRows

// Fresh arrays each bar — accumulate volume into bins
binBuyVol   = array.new_float(vpNumRows, 0.0)
binSellVol  = array.new_float(vpNumRows, 0.0)
binTotalVol = array.new_float(vpNumRows, 0.0)

if vpRowHeight > 0
    for i = 0 to vpLookback - 1
        midP   = (high[i] + low[i]) / 2.0
        bin    = math.floor((midP - vpLowest) / vpRowHeight)
        bin   := math.min(bin, vpNumRows - 1)
        bin   := math.max(bin, 0)
        bRatio = high[i] != low[i] ? (close[i] - low[i]) / (high[i] - low[i]) : 0.5
        bRatio := math.max(0.0, math.min(1.0, bRatio))
        bV     = volume[i] * bRatio
        sV     = volume[i] - bV
        array.set(binBuyVol,   bin, array.get(binBuyVol,   bin) + bV)
        array.set(binSellVol,  bin, array.get(binSellVol,  bin) + sV)
        array.set(binTotalVol, bin, array.get(binTotalVol, bin) + volume[i])

// POC — bin with max total volume
vpMaxVol   = 0.0
vpPocBin   = 0
vpTotalVol = 0.0
for i = 0 to vpNumRows - 1
    bv = array.get(binTotalVol, i)
    vpTotalVol += bv
    if bv > vpMaxVol
        vpMaxVol := bv
        vpPocBin := i

pocPrice = vpLowest + (vpPocBin + 0.5) * vpRowHeight

// Value Area — expand outward from POC until vpVaPct of total volume
vaVol       = array.get(binTotalVol, vpPocBin)
vaThreshold = vpTotalVol * (vpVaPct / 100.0)
vaLoBin     = vpPocBin
vaHiBin     = vpPocBin
while vaVol < vaThreshold and (vaLoBin > 0 or vaHiBin < vpNumRows - 1)
    upV = vaHiBin < vpNumRows - 1 ? array.get(binTotalVol, vaHiBin + 1) : 0.0
    dnV = vaLoBin > 0             ? array.get(binTotalVol, vaLoBin - 1) : 0.0
    if upV >= dnV and vaHiBin < vpNumRows - 1
        vaHiBin += 1
        vaVol   += upV
    else if vaLoBin > 0
        vaLoBin -= 1
        vaVol   += dnV
    else if vaHiBin < vpNumRows - 1
        vaHiBin += 1
        vaVol   += upV
    else
        break

vahPrice = vpLowest + (vaHiBin + 1) * vpRowHeight
valPrice = vpLowest + vaLoBin * vpRowHeight

// HVN Score — buy/sell delta at the bin containing current close
closeBin = vpRowHeight > 0 ? math.floor((close - vpLowest) / vpRowHeight) : 0
closeBin := math.min(closeBin, vpNumRows - 1)
closeBin := math.max(closeBin, 0)

binBV        = array.get(binBuyVol,   closeBin)
binSV        = array.get(binSellVol,  closeBin)
binTV        = array.get(binTotalVol, closeBin)
hvnThreshVol = vpMaxVol * (vpHvnPct / 100.0)
binDelta     = binTV > 0 ? (binBV - binSV) / binTV : 0.0

// Full weight at HVN, half weight at normal bins
hvnScore = binTV >= hvnThreshVol ? binDelta : binDelta * 0.5

// Value area position bonus: support holding below VAL or resistance holding above VAH
if close < valPrice and binDelta > 0
    hvnScore := math.min(1.0, hvnScore + 0.3)
if close > vahPrice and binDelta < 0
    hvnScore := math.max(-1.0, hvnScore - 0.3)
hvnScore := math.max(-1.0, math.min(1.0, hvnScore))

// ═══════════════════════════════════════════════════════════════════════════════
// MODULE 2: ENHANCED CVD
// Three-component composite: bar delta (40%), VWAP delta (35%), tick approx (25%).
// Detects CVD divergences vs price for reversal signals.
// ═══════════════════════════════════════════════════════════════════════════════

// Component A: Bar-level delta — (close-low)/(high-low) centered to [-1,+1], scaled by volume
barDelta    = barRange > 0 ? (close - low) / barRange : 0.5
barDeltaCvd = (barDelta - 0.5) * 2.0 * volume

// Component B: VWAP-anchored delta — directional pressure vs VWAP
vwapDelta = dailyVwap > 0 ? (close - dailyVwap) / dailyVwap * volume : 0.0

// Component C: Tick approximation — (close-open)/(high-low) as proxy for net tick direction
tickApprox = barRange > 0 ? (close - open) / barRange : 0.0
tickDelta  = tickApprox * volume

// Composite per-bar CVD contribution
rawCvdBar = cvdBarWeight * barDeltaCvd + cvdVwapWeight * vwapDelta + cvdTickWeight * tickDelta

// Cumulative CVD
var float cvdCumulative = 0.0
cvdCumulative += rawCvdBar

// Smoothed for trend detection
cvdSmooth = ta.ema(cvdCumulative, cvdSmoothLen)

// Divergence detection: price vs CVD extremes over lookback
priceLowNow  = ta.lowest(low, cvdDivLookback)
priceLowPrev = ta.lowest(low[cvdDivLookback], cvdDivLookback)
priceHiNow   = ta.highest(high, cvdDivLookback)
priceHiPrev  = ta.highest(high[cvdDivLookback], cvdDivLookback)
cvdLowNow    = ta.lowest(cvdSmooth, cvdDivLookback)
cvdLowPrev   = ta.lowest(cvdSmooth[cvdDivLookback], cvdDivLookback)
cvdHiNow     = ta.highest(cvdSmooth, cvdDivLookback)
cvdHiPrev    = ta.highest(cvdSmooth[cvdDivLookback], cvdDivLookback)

bullCvdDiv = priceLowNow < priceLowPrev and cvdLowNow > cvdLowPrev
bearCvdDiv = priceHiNow > priceHiPrev and cvdHiNow < cvdHiPrev

// CVD trend via rate of change, normalized by stdev
cvdRoc     = cvdSmooth - cvdSmooth[cvdSmoothLen]
cvdRocStd  = ta.stdev(cvdRoc, cvdSmoothLen * 2)
cvdTrend   = cvdRocStd > 0 ? math.max(-1.0, math.min(1.0, cvdRoc / cvdRocStd)) : 0.0

// Final CVD score: trend + divergence bonus
cvdScore = cvdTrend
if bullCvdDiv
    cvdScore := math.min(1.0, cvdScore + 0.5)
if bearCvdDiv
    cvdScore := math.max(-1.0, cvdScore - 0.5)
cvdScore := math.max(-1.0, math.min(1.0, cvdScore))

// ═══════════════════════════════════════════════════════════════════════════════
// MODULE 3: ADAPTIVE PIVOTS
// Reduced lag (3,2) vs original (5,5). Fractal confirmation at 2x scale.
// Score based on sweep detection and proximity to pivot levels.
// ═══════════════════════════════════════════════════════════════════════════════

pvtHi = ta.pivothigh(high, pvtLeftBars, pvtRightBars)
pvtLo = ta.pivotlow(low, pvtLeftBars, pvtRightBars)

var float lastPvtHi = na
var float lastPvtLo = na
if not na(pvtHi)
    lastPvtHi := pvtHi
if not na(pvtLo)
    lastPvtLo := pvtLo

// Fractal confirmation at larger scale
pvtHiLg = ta.pivothigh(high, pvtLeftBars * 2, pvtRightBars * 2)
pvtLoLg = ta.pivotlow(low, pvtLeftBars * 2, pvtRightBars * 2)
var float lastPvtHiLg = na
var float lastPvtLoLg = na
if not na(pvtHiLg)
    lastPvtHiLg := pvtHiLg
if not na(pvtLoLg)
    lastPvtLoLg := pvtLoLg

pvtProximity = atrVal * pvtProxAtr

// Sweep: wick through level but close back inside (liquidity grab)
sweepLow  = not na(lastPvtLo) and low < lastPvtLo and close > lastPvtLo
sweepHigh = not na(lastPvtHi) and high > lastPvtHi and close < lastPvtHi

// Proximity: price near a pivot level
nearSupport    = not na(lastPvtLo) and math.abs(close - lastPvtLo) < pvtProximity
nearResistance = not na(lastPvtHi) and math.abs(close - lastPvtHi) < pvtProximity

// Fractal confluence: small pivot aligns with large pivot
fractalSupConf  = not na(lastPvtLoLg) and not na(lastPvtLo) and math.abs(lastPvtLo - lastPvtLoLg) < pvtProximity
fractalResConf  = not na(lastPvtHiLg) and not na(lastPvtHi) and math.abs(lastPvtHi - lastPvtHiLg) < pvtProximity

pivotScore = 0.0
if sweepLow
    pivotScore += 0.7
if sweepHigh
    pivotScore -= 0.7
if nearSupport
    pivotScore += 0.3
if nearResistance
    pivotScore -= 0.3
if fractalSupConf and pivotScore > 0
    pivotScore += 0.3
if fractalResConf and pivotScore < 0
    pivotScore -= 0.3
pivotScore := math.max(-1.0, math.min(1.0, pivotScore))

// ═══════════════════════════════════════════════════════════════════════════════
// MODULE 4: ABSORPTION DETECTION
// absorptionRatio = normalizedVolume / normalizedRange
// High volume + small range = absorption (smart money accumulating/distributing)
// ═══════════════════════════════════════════════════════════════════════════════

avgVol   = ta.sma(volume, absLookback)
avgRange = ta.sma(barRange, absLookback)
normVol  = avgVol > 0   ? volume / avgVol          : 1.0
normRng  = avgRange > 0 ? barRange / avgRange       : 1.0
absRatio = normRng > 0  ? normVol / normRng          : 1.0

isBullClose = close > open

absorptionScore = 0.0
if absRatio >= absThreshold
    absorptionScore := isBullClose ? 1.0 : -1.0
else if absRatio > 1.0
    // Partial score scaled linearly between 1.0 and threshold
    absorptionScore := (absRatio - 1.0) / (absThreshold - 1.0) * (isBullClose ? 0.5 : -0.5)

// ═══════════════════════════════════════════════════════════════════════════════
// MODULE 5: ORDER IMBALANCE RATIO
// OIR = (buyVol - sellVol) / (buyVol + sellVol), smoothed with EMA.
// Extreme values signal directional pressure.
// ═══════════════════════════════════════════════════════════════════════════════

oirBuyVol  = barRange > 0 ? volume * ((close - low) / barRange) : volume * 0.5
oirSellVol = volume - oirBuyVol
oirTotal   = oirBuyVol + oirSellVol
rawOir     = oirTotal > 0 ? (oirBuyVol - oirSellVol) / oirTotal : 0.0
smoothOir  = ta.ema(rawOir, oirSmoothing)

oirScore = 0.0
if math.abs(smoothOir) >= oirThreshold
    // Beyond threshold: scale to [-1, +1] range
    oirScore := math.max(-1.0, math.min(1.0, smoothOir / oirThreshold))
else
    // Below threshold: half weight
    oirScore := smoothOir * 0.5
oirScore := math.max(-1.0, math.min(1.0, oirScore))

// ═══════════════════════════════════════════════════════════════════════════════
// MODULE 6: MULTI-TIMEFRAME
// HTF EMA20/50 trend, VWAP position, RSI direction.
// 2/3 or 3/3 agreement required for signal.
// ═══════════════════════════════════════════════════════════════════════════════

htfTrendUp   = htfEma20 > htfEma50
htfTrendDown = htfEma20 < htfEma50
htfAboveVwap = close > htfVwap
htfBelowVwap = close < htfVwap
htfRsiBull   = htfRsi > 50
htfRsiBear   = htfRsi < 50

htfBullCount = (htfTrendUp   ? 1 : 0) + (htfAboveVwap ? 1 : 0) + (htfRsiBull ? 1 : 0)
htfBearCount = (htfTrendDown ? 1 : 0) + (htfBelowVwap ? 1 : 0) + (htfRsiBear ? 1 : 0)

mtfScore = 0.0
if htfBullCount >= 2
    mtfScore := htfBullCount == 3 ? 1.0 : 0.6
else if htfBearCount >= 2
    mtfScore := htfBearCount == 3 ? -1.0 : -0.6

// ═══════════════════════════════════════════════════════════════════════════════
// COMPOSITE SCORING ENGINE
// Weighted sum normalized by total weight. Replaces the 6-way AND gate.
// Any strong 3-4 signals can trigger entry — not all 6 required.
// ═══════════════════════════════════════════════════════════════════════════════

totalWeight    = wHvn + wCvd + wPivot + wAbsorption + wOir + wMtf
compositeRaw   = wHvn * hvnScore + wCvd * cvdScore + wPivot * pivotScore
               + wAbsorption * absorptionScore + wOir * oirScore + wMtf * mtfScore
compositeScore = totalWeight > 0 ? compositeRaw / totalWeight : 0.0

// ═══════════════════════════════════════════════════════════════════════════════
// POSITION SIZING — ATR-based risk management
// qty = (equity * riskPct) / (ATR * SL multiplier)
// ═══════════════════════════════════════════════════════════════════════════════

riskPerUnit = atrVal * slMult
posQty      = riskPerUnit > 0 ? math.floor(strategy.equity * (riskPct / 100.0) / riskPerUnit) : 0
posQty     := math.max(1, posQty)

// ═══════════════════════════════════════════════════════════════════════════════
// ENTRY LOGIC
// ═══════════════════════════════════════════════════════════════════════════════

longCondition  = compositeScore >= entryThreshold and strategy.position_size == 0
shortCondition = compositeScore <= -entryThreshold and strategy.position_size == 0

if longCondition
    strategy.entry("Long", strategy.long, qty=posQty)

if shortCondition
    strategy.entry("Short", strategy.short, qty=posQty)

// ═══════════════════════════════════════════════════════════════════════════════
// EXIT LOGIC — anchored to strategy.position_avg_price (Bug Fix #1)
// Trail offset guaranteed > 0 (Bug Fix #9)
// ═══════════════════════════════════════════════════════════════════════════════

if strategy.position_size > 0
    entryP   = strategy.position_avg_price
    slPrice  = entryP - atrVal * slMult
    tpPrice  = entryP + atrVal * tpMult
    if useTrailStop
        trailPts = math.max(1, math.round(atrVal * trailMult / syminfo.mintick))
        trailOff = math.max(1, math.round(atrVal * trailOffsetMult / syminfo.mintick))
        strategy.exit("Exit Long", "Long", stop=slPrice, limit=tpPrice,
             trail_points=trailPts, trail_offset=trailOff)
    else
        strategy.exit("Exit Long", "Long", stop=slPrice, limit=tpPrice)

if strategy.position_size < 0
    entryP   = strategy.position_avg_price
    slPrice  = entryP + atrVal * slMult
    tpPrice  = entryP - atrVal * tpMult
    if useTrailStop
        trailPts = math.max(1, math.round(atrVal * trailMult / syminfo.mintick))
        trailOff = math.max(1, math.round(atrVal * trailOffsetMult / syminfo.mintick))
        strategy.exit("Exit Short", "Short", stop=slPrice, limit=tpPrice,
             trail_points=trailPts, trail_offset=trailOff)
    else
        strategy.exit("Exit Short", "Short", stop=slPrice, limit=tpPrice)

// ═══════════════════════════════════════════════════════════════════════════════
// ENTRY/EXIT MARKERS
// ═══════════════════════════════════════════════════════════════════════════════

plotshape(longCondition,  "Long Entry",  shape.triangleup,   location.belowbar, color.green, size=size.small)
plotshape(shortCondition, "Short Entry", shape.triangledown, location.abovebar, color.red,   size=size.small)
bgcolor(compositeScore >= entryThreshold ? color.new(color.green, 92) :
     compositeScore <= -entryThreshold ? color.new(color.red, 92) : na)

// ═══════════════════════════════════════════════════════════════════════════════
// VISUALIZATION — Volume Profile (barstate.islast only, Bug Fix #2)
// Clears all previous drawings via box.all/line.all/label.all loops.
// Budget: ~50 boxes + 3 lines + 6 labels = well within 500 limit.
// ═══════════════════════════════════════════════════════════════════════════════

if barstate.islast and showProfile
    // Clear all previous drawings (snapshot arrays to avoid mutation during iteration)
    aBoxes = box.all
    if array.size(aBoxes) > 0
        for i = 0 to array.size(aBoxes) - 1
            box.delete(array.get(aBoxes, i))
    aLines = line.all
    if array.size(aLines) > 0
        for i = 0 to array.size(aLines) - 1
            line.delete(array.get(aLines, i))
    aLabels = label.all
    if array.size(aLabels) > 0
        for i = 0 to array.size(aLabels) - 1
            label.delete(array.get(aLabels, i))

    rightBar = bar_index + profileWidth + 5
    leftBar  = bar_index + 5

    if vpRowHeight > 0 and vpMaxVol > 0
        // Draw per-bin buy/sell boxes
        for i = 0 to vpNumRows - 1
            binBottom = vpLowest + i * vpRowHeight
            binTop    = binBottom + vpRowHeight
            binTotal  = array.get(binTotalVol, i)

            if binTotal > 0
                totalW    = math.round(profileWidth * (binTotal / vpMaxVol))
                bV        = array.get(binBuyVol, i)
                sV        = array.get(binSellVol, i)
                sellW     = math.round(totalW * (sV / binTotal))
                buyW      = totalW - sellW
                if buyW > 0
                    box.new(rightBar - totalW, binTop, rightBar - sellW, binBottom,
                         border_color=buyColor, bgcolor=buyColor)
                if sellW > 0
                    box.new(rightBar - sellW, binTop, rightBar, binBottom,
                         border_color=sellColor, bgcolor=sellColor)

        // POC line
        line.new(leftBar, pocPrice, rightBar, pocPrice,
             color=pocColor, width=2, style=line.style_dashed)

        // VAH / VAL lines
        line.new(leftBar, vahPrice, rightBar, vahPrice,
             color=color.new(color.blue, 30), width=1, style=line.style_dashed)
        line.new(leftBar, valPrice, rightBar, valPrice,
             color=color.new(color.blue, 30), width=1, style=line.style_dashed)

        // Level labels
        label.new(rightBar, pocPrice, "POC",
             style=label.style_label_left, color=pocColor, textcolor=color.black, size=size.small)
        label.new(rightBar, vahPrice, "VAH",
             style=label.style_label_left, color=color.new(color.blue, 30), textcolor=color.white, size=size.small)
        label.new(rightBar, valPrice, "VAL",
             style=label.style_label_left, color=color.new(color.blue, 30), textcolor=color.white, size=size.small)

// ═══════════════════════════════════════════════════════════════════════════════
// DASHBOARD TABLE
// Signal scores, weights, composite, edge tracking metrics.
// ═══════════════════════════════════════════════════════════════════════════════

var dashTable = table.new(position.top_right, 4, 10, bgcolor=color.new(color.black, 80), border_width=1)

if barstate.islast and showDashboard
    hdrBg  = color.new(color.gray, 50)
    txtClr = color.white
    sz     = size.small

    // Header
    table.cell(dashTable, 0, 0, "Signal",     bgcolor=hdrBg, text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 0, "Score",      bgcolor=hdrBg, text_color=txtClr, text_size=sz)
    table.cell(dashTable, 2, 0, "Weight",     bgcolor=hdrBg, text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 0, "Wtd Score",  bgcolor=hdrBg, text_color=txtClr, text_size=sz)

    // Row 1: Volume Profile
    table.cell(dashTable, 0, 1, "Vol Profile", text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 1, str.tostring(hvnScore, "#.##"),          text_color=scoreColor(hvnScore), text_size=sz)
    table.cell(dashTable, 2, 1, str.tostring(wHvn, "#.##"),              text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 1, str.tostring(wHvn * hvnScore, "#.###"),  text_color=scoreColor(hvnScore), text_size=sz)

    // Row 2: CVD
    table.cell(dashTable, 0, 2, "CVD",         text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 2, str.tostring(cvdScore, "#.##"),          text_color=scoreColor(cvdScore), text_size=sz)
    table.cell(dashTable, 2, 2, str.tostring(wCvd, "#.##"),              text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 2, str.tostring(wCvd * cvdScore, "#.###"),  text_color=scoreColor(cvdScore), text_size=sz)

    // Row 3: Pivots
    table.cell(dashTable, 0, 3, "Pivots",      text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 3, str.tostring(pivotScore, "#.##"),            text_color=scoreColor(pivotScore), text_size=sz)
    table.cell(dashTable, 2, 3, str.tostring(wPivot, "#.##"),                text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 3, str.tostring(wPivot * pivotScore, "#.###"),  text_color=scoreColor(pivotScore), text_size=sz)

    // Row 4: Absorption
    table.cell(dashTable, 0, 4, "Absorption",  text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 4, str.tostring(absorptionScore, "#.##"),                text_color=scoreColor(absorptionScore), text_size=sz)
    table.cell(dashTable, 2, 4, str.tostring(wAbsorption, "#.##"),                    text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 4, str.tostring(wAbsorption * absorptionScore, "#.###"), text_color=scoreColor(absorptionScore), text_size=sz)

    // Row 5: OIR
    table.cell(dashTable, 0, 5, "OIR",         text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 5, str.tostring(oirScore, "#.##"),          text_color=scoreColor(oirScore), text_size=sz)
    table.cell(dashTable, 2, 5, str.tostring(wOir, "#.##"),              text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 5, str.tostring(wOir * oirScore, "#.###"),  text_color=scoreColor(oirScore), text_size=sz)

    // Row 6: MTF
    table.cell(dashTable, 0, 6, "MTF",         text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 6, str.tostring(mtfScore, "#.##"),          text_color=scoreColor(mtfScore), text_size=sz)
    table.cell(dashTable, 2, 6, str.tostring(wMtf, "#.##"),              text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 6, str.tostring(wMtf * mtfScore, "#.###"),  text_color=scoreColor(mtfScore), text_size=sz)

    // Row 7: Composite
    compBg = color.new(color.white, 85)
    table.cell(dashTable, 0, 7, "COMPOSITE",   bgcolor=compBg, text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 7, str.tostring(compositeScore, "#.###"), bgcolor=compBg, text_color=scoreColor(compositeScore), text_size=sz)
    table.cell(dashTable, 2, 7, "",            bgcolor=compBg, text_size=sz)
    signalLabel = compositeScore >= entryThreshold ? "LONG" : compositeScore <= -entryThreshold ? "SHORT" : "FLAT"
    signalClr   = compositeScore >= entryThreshold ? color.green : compositeScore <= -entryThreshold ? color.red : color.gray
    table.cell(dashTable, 3, 7, signalLabel,   bgcolor=compBg, text_color=signalClr, text_size=sz)

    // Row 8: Edge tracking — trades & win rate
    winRate = strategy.closedtrades > 0 ? strategy.wintrades / strategy.closedtrades * 100.0 : 0.0
    table.cell(dashTable, 0, 8, "Trades",      text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 8, str.tostring(strategy.closedtrades), text_color=txtClr, text_size=sz)
    table.cell(dashTable, 2, 8, "Win %",       text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 8, str.tostring(winRate, "#.#") + "%", text_color=winRate > 50 ? color.green : color.red, text_size=sz)

    // Row 9: Edge tracking — net P/L & profit factor
    pf = strategy.grossloss != 0 ? strategy.grossprofit / math.abs(strategy.grossloss) : 0.0
    table.cell(dashTable, 0, 9, "Net P/L",     text_color=txtClr, text_size=sz)
    table.cell(dashTable, 1, 9, str.tostring(strategy.netprofit, "#.##"), text_color=strategy.netprofit > 0 ? color.green : color.red, text_size=sz)
    table.cell(dashTable, 2, 9, "PF",          text_color=txtClr, text_size=sz)
    table.cell(dashTable, 3, 9, str.tostring(pf, "#.##"), text_color=pf > 1.0 ? color.green : color.red, text_size=sz)

Overview

The Judas Indicator triggers trade entries when the weighted factor score exceeds the specified entry threshold. Positions are managed using dynamic ATR-based stop loss and take profit levels. Settings allow custom factor weights, profile lookbacks, CVD smoothing, and risk percentage inputs. The strategy plots overlay elements directly on the price chart.

Details

Published by

regardhodler on GitHub

Code credit

The Judas Indicator — HFT Grade Rewrite, as written in the script's header

Type

Strategy, Pine Script v5

Draws

On the price chart

Category

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

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

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

SettingTypeDefaultVariable
ATR Periodint14atrPeriod
Entry Thresholdfloat0.45entryThreshold
VP Lookbackint100vpLookback
VP Number of Rowsint24vpNumRows
HVN Threshold %float75.0vpHvnPct
Value Area %float70.0vpVaPct
Bar Delta Weightfloat0.40cvdBarWeight
VWAP Delta Weightfloat0.35cvdVwapWeight
Tick Approx Weightfloat0.25cvdTickWeight
Smoothing Lengthint14cvdSmoothLen
Divergence Lookbackint20cvdDivLookback
Left Barsint3pvtLeftBars
Show all 39 settings
SettingTypeDefaultVariable
Right Barsint2pvtRightBars
Proximity (ATR mult)float0.5pvtProxAtr
Lookbackint20absLookback
Ratio Thresholdfloat2.0absThreshold
EMA Lengthint10oirSmoothing
Extreme Thresholdfloat0.6oirThreshold
HTF Timeframetimeframe60htfTimeframe
HTF Fast EMAint20htfEmaFastN
HTF Slow EMAint50htfEmaSlowN
Volume Profilefloat0.20wHvn
CVDfloat0.25wCvd
Pivotsfloat0.15wPivot
Absorptionfloat0.15wAbsorption
Order Imbalancefloat0.10wOir
Multi-Timeframefloat0.15wMtf
Risk Per Trade %float1.0riskPct
Stop Loss (ATR mult)float1.5slMult
Take Profit (ATR mult)float3.0tpMult
Use Trailing StopbooltrueuseTrailStop
Trail Points (ATR mult)float2.0trailMult
Trail Offset (ATR mult)float0.5trailOffsetMult
Show Volume ProfilebooltrueshowProfile
Show DashboardbooltrueshowDashboard
Profile Width (bars)int50profileWidth
Buy Colorcolorcolor.new(color.green, 40)buyColor
Sell Colorcolorcolor.new(color.red, 40)sellColor
POC Colorcolorcolor.yellowpocColor
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

VCP Probe 08 - Integration

by pineforge-4pass
1 stars

This integration strategy combines seven analytical layers into a single system drawn directly on the main price chart. It calculates pivot highs and lows, Fair Value Gaps (FVG) scaled by ATR, smoothed RSI momentum, volume z-score anomalies, cumulative volume

Strategy v6 TrendVolume
Updated 5 days ago · Apache-2.0 GitHub repository of VCP Probe 08 - Integration

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
12 stars

This strategy plots on the price chart and looks for three-candle fair value gaps and inverse gaps on Bitcoin. It enters long when a wide enough bullish gap appears in an uptrend with RSI above 30 and volume above its average, and short on the opposite bearish

Strategy v6 SMC / ICT / price actionTrend Repaints
Updated 4 months ago · None stated GitHub repository of BTC 4h FVG/iFVG Strategy

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