GitHub - 1VB0/VolProf-Mini · GitHub
Skip to content

1VB0/VolProf-Mini

Folders and files

Repository files navigation

//# VolProf-Mini // This work is based on: // Volume Profile with Node Detection © LuxAlgo (CC BY-NC-SA 4.0) // Delta Flow Profile © LuxAlgo (CC BY-NC-SA 4.0) // Merged, extended and converted to Pine Script v6 by 1vb0 // https://creativecommons.org/licenses/by-nc-sa/4.0/

//@version=6 indicator("Volume Profile + Delta Flow [Merged]", shorttitle = "VP+ΔFlow", overlay = true, max_boxes_count = 500, max_lines_count = 500, max_labels_count = 200, max_bars_back = 5000, dynamic_requests = true)

// ═══════════════════════════════════════════════════════════════════════════════ // DISPLAY HELPER (hides from status line) // ═══════════════════════════════════════════════════════════════════════════════ disp = display.all - display.status_line

// ═══════════════════════════════════════════════════════════════════════════════ // GROUP LABELS // ═══════════════════════════════════════════════════════════════════════════════ gVPN = "① Volume Nodes" gVPC = "② Volume Profile — Components" gVPD = "③ Volume Profile — Display" gDFP = "④ Delta Flow Profile" gKEY = "⑤ Key Legend (Bottom-Left)"

// ═══════════════════════════════════════════════════════════════════════════════ // ① VOLUME NODE SETTINGS // ═══════════════════════════════════════════════════════════════════════════════ vn_peaksShow = input.string("Peaks", "Volume Peaks", options=["Peaks","Clusters","None"], inline="vnP", group=gVPN, display=disp) vn_peakColor = input.color(color.new(color.blue, 50), "", inline="vnP", group=gVPN) vn_peakNodes = input.int(9, " Peak Detection %", minval=0, maxval=100, group=gVPN, display=disp) / 100

vn_troughsShow = input.string("None", "Volume Troughs", options=["Troughs","Clusters","None"], inline="vnT", group=gVPN, display=disp) vn_troughColor = input.color(color.new(color.gray, 50), "", inline="vnT", group=gVPN) vn_troughNodes = input.int(7, " Trough Detection %", minval=0, maxval=100, group=gVPN, display=disp) / 100

vn_threshold = input.int(1, "Volume Node Threshold %", minval=0, maxval=100, group=gVPN, display=disp) / 100

vn_topN = input.int(3, "Top N HVN in Key Table", minval=0, maxval=10, group=gVPN, tooltip="How many HVN levels to show in the bottom-left key table.") vn_topNLVN = input.int(3, "Top N LVN in Key Table", minval=0, maxval=10, group=gVPN, tooltip="How many LVN levels to show in the bottom-left key table.")

// ═══════════════════════════════════════════════════════════════════════════════ // ② VOLUME PROFILE — COMPONENTS // ═══════════════════════════════════════════════════════════════════════════════ vp_profileShow = input.bool(true, "Volume Profile", inline="vp", group=gVPC) vp_gradColors = input.string("Gradient Colors", "", options=["Gradient Colors","Classic Colors"], inline="vp", group=gVPC) vp_vaUpColor = input.color(color.new(#2962ff, 30), " Value Area Up", inline="VA", group=gVPC) vp_vaDnColor = input.color(color.new(#fbc02d, 30), "/ Down", inline="VA", group=gVPC) vp_upVolColor = input.color(color.new(#5d606b, 50), " Profile Up", inline="VP", group=gVPC) vp_dnVolColor = input.color(color.new(#d1d4dc, 50), "/ Down", inline="VP", group=gVPC)

vp_pocShow = input.string("Developing", "Point of Control", options=["Developing","Regular","None"], inline="poc", group=gVPC, display=disp) vp_pocColor = input.color(#fbc02d, "", inline="poc", group=gVPC) vp_pocWidth = input.int(2, "Width", inline="poc", group=gVPC, display=disp)

vp_vahShow = input.bool(false, "VAH", inline="vah", group=gVPC) vp_vahColor = input.color(#2962ff, "", inline="vah", group=gVPC) vp_valShow = input.bool(false, "VAL", inline="val", group=gVPC) vp_valColor = input.color(#2962ff, "", inline="val", group=gVPC)

vp_labelSz = input.string("Small", "Profile Price Labels", options=["Tiny","Small","Normal","None"], group=gVPC, display=disp)

// ═══════════════════════════════════════════════════════════════════════════════ // ③ VOLUME PROFILE — DISPLAY // ═══════════════════════════════════════════════════════════════════════════════ vp_lookback = input.int(360, "Profile Lookback", minval=10, maxval=5000, step=10, group=gVPD, display=disp) vp_valueAreaPct = input.float(70, "Value Area %", minval=0, maxval=100, group=gVPD, display=disp) / 100 vp_numRows = input.int(100, "Number of Rows", minval=30, maxval=130, step=10, group=gVPD, display=disp) vp_placement = input.string("Right", "Profile Placement", options=["Right","Left"], group=gVPD, display=disp) vp_profileWidth = input.float(31, "Profile Width %", minval=0, maxval=250, group=gVPD, display=disp) / 100 vp_hOffset = input.int(13, "Horizontal Offset", maxval=50, group=gVPD, display=disp) vp_vaBG = input.bool(false, "Value Area Background", inline="vBG", group=gVPD) vp_vaBGColor = input.color(color.new(#2962ff, 89), "", inline="vBG", group=gVPD) vp_prBG = input.bool(false, "Profile Range Background", inline="pBG", group=gVPD) vp_prBGColor = input.color(color.new(#2962ff, 95), "", inline="pBG", group=gVPD)

// ═══════════════════════════════════════════════════════════════════════════════ // ④ DELTA FLOW PROFILE // ═══════════════════════════════════════════════════════════════════════════════ dfp_moneyShow = input.bool(true, "Money Flow Overlay", group=gDFP, tooltip="Draws the normalized money-flow profile alongside the volume profile.") dfp_moneyColor = input.color(color.new(#5288C4, 0), "Money Flow Colour", group=gDFP) dfp_normalized = input.bool(true, "Normalized", group=gDFP) dfp_deltaShow = input.bool(true, "Delta Profile", group=gDFP, tooltip="Side profile showing bull/bear delta at each price row.") dfp_polarity = input.string("Bar Polarity", "Polarity Method", options=["Bar Polarity","Bar Buying/Selling Pressure"], group=gDFP, display=disp) dfp_bullColor = input.color(color.new(#5288C4, 0), "Delta Bull", inline="dp", group=gDFP) dfp_bearColor = input.color(color.new(#f7525f, 0), "/ Bear", inline="dp", group=gDFP) dfp_pocShow = input.bool(true, "Level of Significance", group=gDFP) dfp_pocType = input.string("Developing", "", options=["Developing","Level","Row"], group=gDFP, display=disp) dfp_pocColor = input.color(color.new(#f23645, 25), "", group=gDFP) dfp_lookback = input.int(360, "Delta Lookback", minval=10, maxval=1500, step=10, group=gDFP, display=disp) dfp_numRows = input.int(25, "Delta Rows", minval=10, maxval=125, step=5, group=gDFP, display=disp) dfp_profWidth = input.int(17, "Delta Width %", minval=10, maxval=50, group=gDFP, display=disp) / 100 dfp_hOffset = input.int(13, "Delta Offset", group=gDFP, display=disp) dfp_labelSz = input.string("Tiny", "Delta Text", options=["Auto","Tiny","Small","None"], inline="dtxt", group=gDFP, display=disp) dfp_showCcy = input.bool(false, "Currency", inline="dtxt", group=gDFP) dfp_priceLevels = input.bool(false, "Price Levels", group=gDFP)

// ═══════════════════════════════════════════════════════════════════════════════ // ⑤ KEY LEGEND // ═══════════════════════════════════════════════════════════════════════════════ key_show = input.bool(true, "Show Key Table", group=gKEY) key_showDelta = input.bool(true, "Show Delta % per Zone", group=gKEY, tooltip="Shows the bull/bear delta percentage at each HVN/LVN in the key table.") key_showVolPct = input.bool(true, "Show Volume % per Zone", group=gKEY)

// ═══════════════════════════════════════════════════════════════════════════════ // USER DEFINED TYPES // ═══════════════════════════════════════════════════════════════════════════════ type BAR float open = open float high = high float low = low float close = close float volume = volume int index = bar_index

type barData float[] barHigh float[] barLow float[] barVolume bool[] barPolarity int[] barCount

type volumeData float[] totalVolume float[] bullVolume float[] bearVolume float[] deltaVolume float[] moneyFlow float[] bullMoneyFlow int[] endProfileIndex bool[] isPeak bool[] isTrough

type volumeProfile box[] boxes chart.point[] pocPoints polyline pocPolyline int pocLevel int vahLevel int valLevel int startIndex

// ─── Delta flow types ──────────────────────────────────────────────────────── type dfpBar float o = open float h = high float l = low float c = close float v = volume int i = bar_index

// ═══════════════════════════════════════════════════════════════════════════════ // HELPERS // ═══════════════════════════════════════════════════════════════════════════════ f_labelSz(string t) => switch t "Tiny" => size.tiny "Small" => size.small "Normal" => size.normal => size.auto

f_calcTF(int depth) => int tf = timeframe.in_seconds(timeframe.period) int m = 60 if depth == 2 switch tf < 30 => "1S" tf < 1 * m => "5S" tf <= 15 * m => "1" tf <= 60 * m => "5" tf <= 240 * m => "15" tf <= 1440 * m => "60" => "D" else switch tf < 15 => "1S" tf < 30 => "5S" tf < 1 * m => "15S" tf <= 5 * m => "1" tf <= 15 * m => "5" tf <= 60 * m => "15" tf <= 240 * m => "60" tf <= 1440 * m => "240" => "D"

renderLine(_x1, _y1, _x2, _y2, _xloc, _extend, _color, _style, _width) => var id = line.new(_x1, _y1, _x2, _y2, _xloc, _extend, _color, _style, _width) line.set_xy1(id, _x1, _y1) line.set_xy2(id, _x2, _y2) line.set_color(id, _color)

renderLabel(_x, _y, _text, _color, _style, _tc, _sz, _tip) => var lb = label.new(_x, _y, _text, xloc.bar_index, yloc.price, _color, _style, _tc, _sz, text.align_left, _tip) lb.set_xy(_x, _y) lb.set_text(_text) lb.set_tooltip(_tip) lb.set_textcolor(_tc)

f_pct(float v) => str.tostring(math.round(v * 100, 1)) + "%" f_fmtV(float v) => float a = math.abs(v) a >= 1e6 ? str.tostring(v / 1e6, "#.0") + "M" : a >= 1e3 ? str.tostring(v / 1e3, "#.0") + "K" : str.tostring(math.round(v, 0), "#")

// ═══════════════════════════════════════════════════════════════════════════════ // VOLUME PROFILE STATE // ═══════════════════════════════════════════════════════════════════════════════ int vpLookback = last_bar_index > vp_lookback ? vp_lookback - 1 : last_bar_index

BAR vpBar = BAR.new() BAR[] ltfBars = array.new(1, BAR.new())

var barData bda = barData.new( array.new(), array.new(), array.new(), array.new(), array.new())

volumeData vda = volumeData.new( array.new_float(vp_numRows, 0.), array.new_float(vp_numRows, 0.), array.new_float(vp_numRows, 0.), array.new_float(vp_numRows, 0.), array.new_float(vp_numRows, 0.), array.new_float(vp_numRows, 0.), array.new_int (vp_numRows, 0 ), array.new_bool (vp_numRows, false), array.new_bool (vp_numRows, false))

var volumeProfile VP = volumeProfile.new( array.new(), array.new<chart.point>(), na, na, na, na, na)

var float vpHi = na var float vpLo = na

// ─── LTF data request for VP ───────────────────────────────────────────────── requestBars(string tf) => request.security_lower_tf(syminfo.tickerid, tf, BAR.new(), ignore_invalid_timeframe=true)

ltfBars := vpLookback <= 700 ? requestBars(f_calcTF(2)) : array.new(1, BAR.new(vpBar.open, vpBar.high, vpBar.low, vpBar.close, vpBar.volume))

// Track VP price range if vpBar.index == last_bar_index - vpLookback VP.startIndex := vpBar.index vpLo := vpBar.low vpHi := vpBar.high else if vpBar.index > last_bar_index - vpLookback vpLo := math.min(vpBar.low, vpLo) vpHi := math.max(vpBar.high, vpHi)

// Collect LTF bars during history if barstate.ishistory and vpBar.index >= last_bar_index - vpLookback and vpBar.index < last_bar_index and ltfBars.size() > 0 if not na(nz(ltfBars.get(0).volume)) for k = 0 to ltfBars.size() - 1 bda.barHigh.push (ltfBars.get(k).high) bda.barLow.push (ltfBars.get(k).low) bda.barVolume.push (ltfBars.get(k).volume) bda.barPolarity.push(ltfBars.get(k).close > ltfBars.get(k).open) bda.barCount.push(ltfBars.size())

// ═══════════════════════════════════════════════════════════════════════════════ // DELTA FLOW STATE // ═══════════════════════════════════════════════════════════════════════════════ int dfpLookback = last_bar_index > dfp_lookback ? dfp_lookback - 1 : last_bar_index

dfpBar db = dfpBar.new() float nzV = nz(db.v)

float[] dfpVST = array.new_float(dfp_numRows, 0.) // total money flow per row float[] dfpVSB = array.new_float(dfp_numRows, 0.) // bullish money flow per row float[] dfpVSD = array.new_float(dfp_numRows, 0.) // delta magnitude per row

var box[] dfpBoxes = array.new() var line[] dfpLines = array.new() var chart.point[] dfpPocPts = array.new<chart.point>() var polyline dfpPocPoly = na

var float dfpLo = na var float dfpHi = na var int dfpSI = na // start bar index

bool dfpBull = dfp_polarity == "Bar Polarity" ? db.c > db.o : (db.c - db.l) > (db.h - db.c)

if db.i == last_bar_index - dfpLookback dfpSI := db.i dfpLo := db.l dfpHi := db.h else if db.i > last_bar_index - dfpLookback dfpLo := math.min(db.l, dfpLo) dfpHi := math.max(db.h, dfpHi)

float dfpPSTP = (dfpHi - dfpLo) / dfp_numRows

// ═══════════════════════════════════════════════════════════════════════════════ // KEY TABLE DATA ARRAYS (populated during barstate.islast) // ═══════════════════════════════════════════════════════════════════════════════ var float[] hvnPrice = array.new_float() var float[] hvnVolPct = array.new_float() var float[] hvnDeltaPct = array.new_float() var bool[] hvnBullBias = array.new_bool()

var float[] lvnPrice = array.new_float() var float[] lvnVolPct = array.new_float() var float[] lvnDeltaPct = array.new_float() var bool[] lvnBullBias = array.new_bool()

// ═══════════════════════════════════════════════════════════════════════════════ // MAIN RENDER BLOCK (barstate.islast) // ═══════════════════════════════════════════════════════════════════════════════ bool placementRight = vp_placement == "Right" labelSz = f_labelSz(vp_labelSz) dfpLblSz = f_labelSz(dfp_labelSz)

float priceStep = na(vpHi) or na(vpLo) or vp_numRows <= 0 ? 0. : (vpHi - vpLo) / vp_numRows

if barstate.islast and not na(nzV) and not timeframe.isseconds and vpLookback > 0 and priceStep > 0 and nzV > 0

// ── Clear old VP boxes/lines ─────────────────────────────────────
if VP.boxes.size() > 0
    for i = 0 to VP.boxes.size() - 1
        box.delete(VP.boxes.shift())
if VP.pocPoints.size() > 0
    VP.pocPoints.clear()
VP.pocPolyline.delete()

// ── Clear old DFP boxes/lines ────────────────────────────────────
if dfpBoxes.size() > 0
    for i = 0 to dfpBoxes.size() - 1
        box.delete(dfpBoxes.shift())
if dfpLines.size() > 0
    for i = 0 to dfpLines.size() - 1
        line.delete(dfpLines.shift())
dfpPocPts.clear()
a_poly = polyline.all
if a_poly.size() > 0
    for i = 0 to a_poly.size() - 1
        polyline.delete(a_poly.get(i))

// ── Clear key table arrays ───────────────────────────────────────
hvnPrice.clear()   
hvnVolPct.clear()   
hvnDeltaPct.clear()   
hvnBullBias.clear()
lvnPrice.clear()   
lvnVolPct.clear()   
lvnDeltaPct.clear()   
lvnBullBias.clear() 

// ── Trim barData if overgrown ────────────────────────────────────
if bda.barCount.size() > vpLookback
    int cnt = bda.barCount.shift()
    for _ = 0 to cnt - 1
        bda.barHigh.shift()
        bda.barLow.shift()
        bda.barVolume.shift()
        bda.barPolarity.shift()

// ── Push current bar's LTF data ──────────────────────────────────
if ltfBars.size() > 0 and not na(nz(ltfBars.get(0).volume))
    for k = 0 to ltfBars.size() - 1
        bda.barHigh.push    (ltfBars.get(k).high)
        bda.barLow.push     (ltfBars.get(k).low)
        bda.barVolume.push  (ltfBars.get(k).volume)
        bda.barPolarity.push(ltfBars.get(k).close > ltfBars.get(k).open)
    bda.barCount.push(ltfBars.size())                                            


// ─────────────────────────────────────────────────────────────────
//  VOLUME PROFILE ACCUMULATION
// ─────────────────────────────────────────────────────────────────
int arrSz = bda.barVolume.size()
for ai = 0 to arrSz - 1
    float bHi   = bda.barHigh.get(ai)
    float bLo   = bda.barLow.get(ai)
    float bVol  = bda.barVolume.get(ai)
    bool  bBull = bda.barPolarity.get(ai)

    int rowStart = math.max(math.floor((bLo - vpLo) / priceStep), 0)
    int rowEnd   = math.min(math.floor((bHi - vpLo) / priceStep), vp_numRows - 1)

    for row = rowStart to rowEnd
        float rowPrice = vpLo + row * priceStep
        float vPOR = bLo >= rowPrice and bHi > rowPrice + priceStep ? (rowPrice + priceStep - bLo) / (bHi - bLo) :
                     bHi <= rowPrice + priceStep and bLo < rowPrice  ? (bHi - rowPrice) / (bHi - bLo) :
                     bLo >= rowPrice and bHi <= rowPrice + priceStep ? 1. :
                     priceStep / (bHi - bLo)

        float allocVol = bVol * vPOR
        vda.totalVolume.set(row, vda.totalVolume.get(row) + allocVol)
        if bBull
            vda.bullVolume.set(row, vda.bullVolume.get(row) + allocVol)

// ─────────────────────────────────────────────────────────────────
//  DELTA FLOW ACCUMULATION  (money flow = vol * price)
// ─────────────────────────────────────────────────────────────────
if dfpPSTP > 0
    for bI = dfpLookback to 0
        int lDfp = 0
        for pLL = dfpLo to dfpHi - dfpPSTP by dfpPSTP
            if (db[bI]).h >= pLL and (db[bI]).l < pLL + dfpPSTP
                float vPOR2 = (db[bI]).l >= pLL and (db[bI]).h > pLL + dfpPSTP ? (pLL + dfpPSTP - (db[bI]).l) / ((db[bI]).h - (db[bI]).l) :
                              (db[bI]).h <= pLL + dfpPSTP and (db[bI]).l < pLL  ? ((db[bI]).h - pLL) / ((db[bI]).h - (db[bI]).l) :
                              (db[bI]).l >= pLL and (db[bI]).h <= pLL + dfpPSTP ? 1. :
                              dfpPSTP / ((db[bI]).h - (db[bI]).l)

                float mf = nzV[bI] * vPOR2 * (dfpLo + (lDfp + 0.5) * dfpPSTP)
                dfpVST.set(lDfp, dfpVST.get(lDfp) + mf)
                if dfpBull[bI] and dfp_deltaShow
                    dfpVSB.set(lDfp, dfpVSB.get(lDfp) + mf)
            lDfp += 1

        if dfp_pocShow and dfp_pocType == "Developing"
            dfpPocPts.push(chart.point.from_index((db[bI]).i,
                 dfpLo + (dfpVST.indexof(dfpVST.max()) + 0.5) * dfpPSTP))
// ─────────────────────────────────────────────────────────────────
//  POC / VAH / VAL  (from VP)
// ─────────────────────────────────────────────────────────────────
VP.pocLevel := vda.totalVolume.indexof(vda.totalVolume.max())

float totalForVA = vda.totalVolume.sum() * vp_valueAreaPct
float vaVol      = VP.pocLevel != -1 ? vda.totalVolume.get(VP.pocLevel) : 0.
VP.vahLevel := VP.pocLevel
VP.valLevel := VP.pocLevel

while vaVol < totalForVA
    if VP.valLevel == 0 and VP.vahLevel == vp_numRows - 1
        break
    float volAbove = VP.vahLevel < vp_numRows - 1 ? vda.totalVolume.get(VP.vahLevel + 1) : 0.
    float volBelow = VP.valLevel > 0              ? vda.totalVolume.get(VP.valLevel - 1) : 0.
    if volAbove == 0. and volBelow == 0.
        break
    if volAbove >= volBelow
        vaVol       += volAbove
        VP.vahLevel += 1
    else
        vaVol       += volBelow
        VP.valLevel -= 1

float vahPrice = vpLo + (VP.vahLevel + 1.) * priceStep
float pocPrice = vpLo + (VP.pocLevel + 0.5) * priceStep
float valPrice = vpLo + (VP.valLevel + 0.0) * priceStep

// ─────────────────────────────────────────────────────────────────
//  RENDER:  VOLUME PROFILE BARS
// ─────────────────────────────────────────────────────────────────
int plotLen   = math.min(vpLookback, 360)
float profW   = plotLen * vp_profileWidth
int   hOff    = int(profW + vp_hOffset)

for row = 0 to vp_numRows - 1
    float totV  = vda.totalVolume.get(row)
    float bullV = vda.bullVolume.get(row)
    float vtMx  = vda.totalVolume.max()
    float LpM   = vtMx > 0 ? totV / vtMx : 0.

    bool inVA = row >= VP.valLevel and row <= VP.vahLevel

    color upC = vp_profileShow and vp_gradColors == "Gradient Colors" ?
         color.from_gradient(LpM, 0, 1, color.new(inVA ? vp_vaUpColor : vp_upVolColor, 95),
                                         color.new(inVA ? vp_vaUpColor : vp_upVolColor, 0)) :
         inVA ? vp_vaUpColor : vp_upVolColor
    color dnC = vp_profileShow and vp_gradColors == "Gradient Colors" ?
         color.from_gradient(LpM, 0, 1, color.new(inVA ? vp_vaDnColor : vp_dnVolColor, 95),
                                         color.new(inVA ? vp_vaDnColor : vp_dnVolColor, 0)) :
         inVA ? vp_vaDnColor : vp_dnVolColor

    if vp_profileShow and VP.boxes.size() < 490
        int sB = placementRight ?
             hOff + int(last_bar_index - bullV / math.max(vtMx, 1.) * profW) :
             VP.startIndex
        int eB = placementRight ?
             hOff + last_bar_index :
             int(VP.startIndex + bullV / math.max(vtMx, 1.) * profW)
        VP.boxes.push(box.new(sB, vpLo + (row + 0.1) * priceStep,
                              eB, vpLo + (row + 0.9) * priceStep,
                              color(na), bgcolor=upC))

        int sB2 = placementRight ? sB : eB
        int eB2 = placementRight ?
             sB - int((totV - bullV) / math.max(vtMx, 1.) * profW) :
             sB + int((totV - bullV) / math.max(vtMx, 1.) * profW)
        VP.boxes.push(box.new(sB2, vpLo + (row + 0.1) * priceStep,
                              eB2, vpLo + (row + 0.9) * priceStep,
                              color(na), bgcolor=dnC))
        vda.endProfileIndex.set(row, eB2)

// ─────────────────────────────────────────────────────────────────
//  RENDER:  POC / VAH / VAL lines
// ─────────────────────────────────────────────────────────────────
int lineEnd = placementRight ?
     (vp_profileShow ? hOff : 0) + last_bar_index : last_bar_index

if vp_pocShow == "Regular"
    renderLine(VP.startIndex, pocPrice, lineEnd, pocPrice,
         xloc.bar_index, extend.none, vp_pocColor, line.style_solid, vp_pocWidth)

if vp_vahShow
    renderLine(VP.startIndex, vahPrice, lineEnd, vahPrice,
         xloc.bar_index, extend.none, vp_vahColor, line.style_solid, 1)

if vp_valShow
    renderLine(VP.startIndex, valPrice, lineEnd, valPrice,
         xloc.bar_index, extend.none, vp_valColor, line.style_solid, 1)

if vp_pocShow == "Developing"
    VP.pocPolyline := polyline.new(VP.pocPoints, false, false,
         xloc.bar_index, vp_pocColor, color(na), line.style_solid, vp_pocWidth)

if vp_vaBG
    VP.boxes.push(box.new(VP.startIndex, valPrice, last_bar_index, vahPrice,
         vp_vaBGColor, 1, line.style_dotted, bgcolor=vp_vaBGColor))
if vp_prBG
    VP.boxes.push(box.new(VP.startIndex, vpLo, last_bar_index, vpHi,
         vp_prBGColor, 1, line.style_dotted, bgcolor=vp_prBGColor))

if vp_labelSz != "None" and VP.pocLevel != -1
    renderLabel(lineEnd, vpHi,    str.tostring(vpHi,    format.mintick),
         color.new(chart.fg_color, 89), label.style_label_down, chart.fg_color, labelSz, "Profile High")
    renderLabel(lineEnd, vahPrice, str.tostring(vahPrice, format.mintick),
         color.new(vp_vahColor,   89), label.style_label_left, vp_vahColor, labelSz, "Value Area High")
    renderLabel(lineEnd, pocPrice, str.tostring(pocPrice, format.mintick),
         color.new(vp_pocColor,   89), label.style_label_left, vp_pocColor, labelSz, "Point of Control")
    renderLabel(lineEnd, valPrice, str.tostring(valPrice, format.mintick),
         color.new(vp_valColor,   89), label.style_label_left, vp_valColor, labelSz, "Value Area Low")
    renderLabel(lineEnd, vpLo,    str.tostring(vpLo,    format.mintick),
         color.new(chart.fg_color, 89), label.style_label_up, chart.fg_color, labelSz, "Profile Low")

// ─────────────────────────────────────────────────────────────────
//  RENDER:  DELTA FLOW PROFILE
// ─────────────────────────────────────────────────────────────────
if dfpPSTP > 0
    float dfpVtMx = dfpVST.max()
    float dfpVdMx = 0.

    // Build delta magnitude array
    for l = 0 to dfp_numRows - 1
        float bbp = 2. * dfpVSB.get(l) - dfpVST.get(l)
        dfpVSD.set(l, math.abs(bbp))
        dfpVdMx := math.max(dfpVdMx, math.abs(bbp))

    for l = 0 to dfp_numRows - 1
        float vtLV = dfpVST.get(l)
        float LpM  = dfpVtMx > 0 ? vtLV / dfpVtMx : 0.
        float DpM  = dfpVdMx > 0 ? dfpVSD.get(l) / dfpVdMx : 0.
        float bbp  = 2. * dfpVSB.get(l) - vtLV

        // Money flow (normalized bars) — placed to the right of the VP
        if dfp_moneyShow and dfp_normalized and dfpBoxes.size() < 480
            color mfC = color.from_gradient(LpM, 0, 1, color.new(dfp_moneyColor, 93), color.new(dfp_moneyColor, 53))
            int sBI = db.i + int(4 * dfpLookback * dfp_profWidth / 3)
            dfpBoxes.push(box.new(
                 sBI + 1 + dfp_hOffset,
                 dfpLo + (l + 0.03) * dfpPSTP,
                 sBI + int(dfpLookback * dfp_profWidth / 3) + 3 + dfp_hOffset,
                 dfpLo + (l + 0.97) * dfpPSTP,
                 color(na), bgcolor=mfC))
            int sBI2 = sBI + int(dfpLookback * dfp_profWidth / 3) + 3 + dfp_hOffset
            int eBI2 = sBI2 - int(LpM * (int(dfpLookback * dfp_profWidth / 3) + 2))
            color mfC2 = color.from_gradient(LpM, 0, 1,
                 color.new(dfp_moneyColor, 53), color.new(chart.fg_color, 13))
            dfpBoxes.push(box.new(sBI2, dfpLo + (l + 0.1) * dfpPSTP,
                                  eBI2, dfpLo + (l + 0.9) * dfpPSTP,
                                  color(na), bgcolor=mfC2,
                                  text = dfp_labelSz != "None" ? f_pct(LpM) : "",
                                  text_color = LpM == 1 ? color.blue : LpM > 0.5 ? chart.bg_color : chart.fg_color,
                                  text_halign = text.align_right,
                                  text_size = LpM == 1 ? size.small : size.tiny))

        // Delta bars (bull/bear split)
        if dfp_deltaShow and dfpBoxes.size() < 480
            int sBI3  = dfpSI
            int eBI3  = sBI3 + int(DpM * dfpLookback * dfp_profWidth)
            color dC  = bbp > 0 ?
                 color.from_gradient(DpM, 0, 1, color.new(dfp_bullColor, 80), color.new(dfp_bullColor, 20)) :
                 color.from_gradient(DpM, 0, 1, color.new(dfp_bearColor, 80), color.new(dfp_bearColor, 20))
            dfpBoxes.push(box.new(sBI3 + 1, dfpLo + (l + 0.1) * dfpPSTP,
                                  eBI3 + 1, dfpLo + (l + 0.9) * dfpPSTP,
                                  color(na), bgcolor=dC,
                                  text = dfp_labelSz != "None" ?
                                         f_fmtV(bbp) + (dfp_showCcy ? " " + syminfo.currency : "") : "",
                                  text_halign = text.align_left,
                                  text_color = chart.fg_color,
                                  text_size = dfpLblSz))

    // DFP Level of Significance (POC)
    if dfp_pocShow
        if dfp_pocType == "Developing"
            dfpPocPoly := polyline.new(dfpPocPts, false, false,
                 xloc.bar_index, dfp_pocColor, color(na), line.style_solid, 2)
        else
            int dfpPocLvl = dfpVST.indexof(dfpVtMx)
            float dfpPocP = dfpLo + (dfpPocLvl + 0.5) * dfpPSTP
            renderLine(dfpSI, dfpPocP,
                 db.i + int(4 * dfpLookback * dfp_profWidth / 3) + dfp_hOffset,
                 dfpPocP, xloc.bar_index, extend.none, dfp_pocColor, line.style_solid, 2)

    if dfp_moneyShow
        dfpLines.push(line.new(
             db.i + int(4 * dfpLookback * dfp_profWidth / 3) + 1 + dfp_hOffset, dfpLo,
             db.i + int(4 * dfpLookback * dfp_profWidth / 3) + 1 + dfp_hOffset, dfpHi,
             color=dfp_moneyColor, width=2))
        if dfp_normalized
            dfpLines.push(line.new(
                 db.i + int(5 * dfpLookback * dfp_profWidth / 3) + 3 + dfp_hOffset, dfpLo,
                 db.i + int(5 * dfpLookback * dfp_profWidth / 3) + 3 + dfp_hOffset, dfpHi,
                 color=dfp_moneyColor, width=2))

    if dfp_priceLevels
        renderLabel(dfp_moneyShow ? db.i + int(4 * dfpLookback * dfp_profWidth / 3) + 1 + dfp_hOffset : db.i,
             dfpHi, "High · " + str.tostring(dfpHi, format.mintick),
             color.new(dfp_moneyColor, 89), label.style_label_down, dfp_moneyColor, dfpLblSz, "Delta Profile High")
        renderLabel(dfp_moneyShow ? db.i + int(4 * dfpLookback * dfp_profWidth / 3) + 1 + dfp_hOffset : db.i,
             dfpLo, "Low · " + str.tostring(dfpLo, format.mintick),
             color.new(dfp_moneyColor, 89), label.style_label_up, dfp_moneyColor, dfpLblSz, "Delta Profile Low")
// ─────────────────────────────────────────────────────────────────
//  HVN / LVN NODE DETECTION  (from Volume Profile with Node Detection)
//  Also populates hvn*/lvn* arrays for the key table
// ─────────────────────────────────────────────────────────────────
float vtMxN = vda.totalVolume.max()
float totalV = vda.totalVolume.sum()

if vn_peaksShow != "None"
    int peakN = int(vp_numRows * vn_peakNodes)
    if peakN > 0
        float[] tempPeak = vda.totalVolume.copy()
        for _ = 1 to peakN
            tempPeak.unshift(0.)
            tempPeak.push(0.)

        for lvl = 2 * peakN to vp_numRows - 1 + 2 * peakN
            bool upperOk = true
            bool lowerOk = true

            for cn = lvl - 2 * peakN to lvl - peakN - 1
                if tempPeak.get(lvl - peakN) <= tempPeak.get(cn)
                    upperOk := false
                    break

            for cn = lvl - peakN + 1 to lvl
                if tempPeak.get(lvl - peakN) <= tempPeak.get(cn)
                    lowerOk := false
                    break

            float peakV = tempPeak.get(lvl - peakN)
            if upperOk and lowerOk and vtMxN > 0 and peakV / vtMxN > vn_threshold

                int realRow = lvl - 2 * peakN
                float nodePrice = vpLo + (realRow + 0.5) * priceStep
                float nodeVolPct = totalV > 0 ? vda.totalVolume.get(realRow) / totalV : 0.
                float bullV2 = vda.bullVolume.get(realRow)
                float totV2  = vda.totalVolume.get(realRow)
                float nodeDeltaPct = totV2 > 0 ? (2. * bullV2 - totV2) / totV2 : 0.
                bool  nodeBullBias = bullV2 >= totV2 / 2.

                hvnPrice.push(nodePrice)
                hvnVolPct.push(nodeVolPct)
                hvnDeltaPct.push(nodeDeltaPct)
                hvnBullBias.push(nodeBullBias)

                if VP.boxes.size() < 490
                    int sVN = placementRight ? VP.startIndex : vda.endProfileIndex.get(realRow)
                    int eVN = placementRight ? vda.endProfileIndex.get(realRow) : last_bar_index
                    color hvnC = vn_peaksShow == "Peaks" ? vn_peakColor :
                         color.from_gradient(peakV / vtMxN, 0, 1,
                              color.new(vn_peakColor, 95), color.new(vn_peakColor, 65))
                    VP.boxes.push(box.new(sVN, vpLo + (realRow + 0.1) * priceStep,
                                          eVN, vpLo + (realRow + 0.9) * priceStep,
                                          color(na), bgcolor=hvnC))

        tempPeak.clear()

if vn_troughsShow != "None"
    int troughN = int(vp_numRows * vn_troughNodes)
    if troughN > 0
        float[] tempTrough = vda.totalVolume.copy()
        for _ = 1 to troughN
            tempTrough.unshift(vtMxN)
            tempTrough.push(vtMxN)

        for lvl = 2 * troughN to vp_numRows - 1 + 2 * troughN
            bool upperOk = true
            bool lowerOk = true

            for cn = lvl - 2 * troughN to lvl - troughN - 1
                if tempTrough.get(lvl - troughN) >= tempTrough.get(cn)
                    upperOk := false
                    break

            for cn = lvl - troughN + 1 to lvl
                if tempTrough.get(lvl - troughN) >= tempTrough.get(cn)
                    lowerOk := false
                    break

            float troughV = tempTrough.get(lvl - troughN)
            if upperOk and lowerOk and vtMxN > 0 and troughV / vtMxN > vn_threshold

                int realRow = lvl - 2 * troughN
                float nodePrice = vpLo + (realRow + 0.5) * priceStep
                float nodeVolPct = totalV > 0 ? vda.totalVolume.get(realRow) / totalV : 0.
                float bullV2 = vda.bullVolume.get(realRow)
                float totV2  = vda.totalVolume.get(realRow)
                float nodeDeltaPct = totV2 > 0 ? (2. * bullV2 - totV2) / totV2 : 0.
                bool  nodeBullBias = bullV2 >= totV2 / 2.

                lvnPrice.push(nodePrice)
                lvnVolPct.push(nodeVolPct)
                lvnDeltaPct.push(nodeDeltaPct)
                lvnBullBias.push(nodeBullBias)

                if VP.boxes.size() < 490
                    int sVN = placementRight ? VP.startIndex : vda.endProfileIndex.get(realRow)
                    int eVN = placementRight ? vda.endProfileIndex.get(realRow) : last_bar_index
                    color lvnC = vn_troughsShow == "Troughs" ? vn_troughColor :
                         color.from_gradient(troughV / vtMxN, 0, 1,
                              color.new(vn_troughColor, 95), color.new(vn_troughColor, 31))
                    VP.boxes.push(box.new(sVN, vpLo + (realRow + 0.1) * priceStep,
                                          eVN, vpLo + (realRow + 0.9) * priceStep,
                                          color(na), bgcolor=lvnC))

        tempTrough.clear()

// ═══════════════════════════════════════════════════════════════════════════════ // KEY LEGEND TABLE (bottom-left, always updated at islast) // ═══════════════════════════════════════════════════════════════════════════════ // Columns: Level | Price | Vol% | Δ% | Bias // Rows: Header → POC → VAH → VAL → [HVNs] → [LVNs] → Zone // ═══════════════════════════════════════════════════════════════════════════════

int hvnCount = hvnPrice.size() int lvnCount = lvnPrice.size() int topHVN = math.min(vn_topN, hvnCount) int topLVN = math.min(vn_topNLVN, lvnCount)

// Total rows: 1 header + 3 key levels + 1 hvn section header + topHVN // + 1 lvn section header + topLVN + 1 zone row + 1 delta bias row int totalRows = 1 + 3 + 1 + topHVN + 1 + topLVN + 1 + 1

var table keyTbl = table.new(position.bottom_left, 5, 30, bgcolor = color.new(color.black, 70), frame_color = color.new(color.white, 30), frame_width = 1, border_color = color.new(color.gray, 60), border_width = 1)

if barstate.islast and key_show

// ── Common colours ───────────────────────────────────────────────
color hdr_bg  = color.new(#1a1a3a, 20)
color sec_bg  = color.new(#0d0d1a, 40)
color sbg     = color.new(color.black, 55)
color w       = color.white
color sg      = color.silver

// ── Overall delta bias from DFP ──────────────────────────────────
float dfpTotalBull = dfpVSB.sum()
float dfpTotalAll  = dfpVST.sum()
float overallDelta = dfpTotalAll > 0 ? (2. * dfpTotalBull - dfpTotalAll) / dfpTotalAll : 0.
bool  bullBias     = overallDelta >= 0.
color biasCol      = bullBias ? color.lime : color.red

// ── POC / VAH / VAL prices ───────────────────────────────────────
float pocP = na(vpLo) or na(priceStep) ? na : vpLo + (VP.pocLevel + 0.5) * priceStep
float vahP = na(vpLo) or na(priceStep) ? na : vpLo + (VP.vahLevel + 1.0) * priceStep
float valP = na(vpLo) or na(priceStep) ? na : vpLo + (VP.valLevel + 0.0) * priceStep

float pocBullV = not na(pocP) and VP.pocLevel >= 0 ? vda.bullVolume.get(VP.pocLevel) : 0.
float pocTotV  = not na(pocP) and VP.pocLevel >= 0 ? vda.totalVolume.get(VP.pocLevel) : 0.
float pocDelta = pocTotV > 0 ? (2. * pocBullV - pocTotV) / pocTotV : 0.

// ── Zone: above VAH / in VA / below VAL ─────────────────────────
string zoneTxt = not na(vahP) and close > vahP ? "ABOVE VAH ▲" :
                 not na(valP) and close < valP ? "BELOW VAL ▼" :
                 not na(vahP)                  ? "In Value Area" : "—"
color  zoneCol = not na(vahP) and close > vahP ? color.lime :
                 not na(valP) and close < valP ? color.red  : color.gray

int r = 0  // row counter

// ── HEADER ───────────────────────────────────────────────────────
table.cell(keyTbl, 0, r, "KEY LEVELS",  text_color=w,  text_size=size.small, bgcolor=hdr_bg, text_halign=text.align_center)
table.cell(keyTbl, 1, r, "Price",       text_color=sg, text_size=size.tiny,  bgcolor=hdr_bg)
table.cell(keyTbl, 2, r, "Vol%",        text_color=sg, text_size=size.tiny,  bgcolor=hdr_bg)
table.cell(keyTbl, 3, r, "Δ%",          text_color=sg, text_size=size.tiny,  bgcolor=hdr_bg)
table.cell(keyTbl, 4, r, "Bias",        text_color=sg, text_size=size.tiny,  bgcolor=hdr_bg)
r += 1

// ── POC ──────────────────────────────────────────────────────────
float pocVolPct = not na(pocP) and vda.totalVolume.sum() > 0 ?
     vda.totalVolume.get(VP.pocLevel) / vda.totalVolume.sum() : 0.
table.cell(keyTbl, 0, r, "POC",          text_color=vp_pocColor, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 1, r, not na(pocP) ? str.tostring(pocP, format.mintick) : "—",
                          text_color=vp_pocColor, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 2, r, key_showVolPct ? f_pct(pocVolPct) : "—",
                          text_color=sg, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 3, r, key_showDelta  ? (pocDelta >= 0 ? "+" : "") + f_pct(pocDelta) : "—",
                          text_color=pocDelta >= 0 ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 4, r, pocDelta >= 0 ? "▲ Bull" : "▼ Bear",
                          text_color=pocDelta >= 0 ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
r += 1

// ── VAH ──────────────────────────────────────────────────────────
float vahVolPct = not na(vahP) and vda.totalVolume.sum() > 0 ?
     vda.totalVolume.get(VP.vahLevel) / vda.totalVolume.sum() : 0.
float vahBullV  = not na(vahP) ? vda.bullVolume.get(VP.vahLevel) : 0.
float vahTotV   = not na(vahP) ? vda.totalVolume.get(VP.vahLevel) : 0.
float vahDelta  = vahTotV > 0 ? (2. * vahBullV - vahTotV) / vahTotV : 0.
table.cell(keyTbl, 0, r, "VAH",  text_color=vp_vahColor, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 1, r, not na(vahP) ? str.tostring(vahP, format.mintick) : "—",
                          text_color=vp_vahColor, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 2, r, key_showVolPct ? f_pct(vahVolPct) : "—",
                          text_color=sg, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 3, r, key_showDelta  ? (vahDelta >= 0 ? "+" : "") + f_pct(vahDelta) : "—",
                          text_color=vahDelta >= 0 ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 4, r, vahDelta >= 0 ? "▲" : "▼",
                          text_color=vahDelta >= 0 ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
r += 1

// ── VAL ──────────────────────────────────────────────────────────
float valVolPct = not na(valP) and vda.totalVolume.sum() > 0 ?
     vda.totalVolume.get(VP.valLevel) / vda.totalVolume.sum() : 0.
float valBullV  = not na(valP) ? vda.bullVolume.get(VP.valLevel) : 0.
float valTotV   = not na(valP) ? vda.totalVolume.get(VP.valLevel) : 0.
float valDelta  = valTotV > 0 ? (2. * valBullV - valTotV) / valTotV : 0.
table.cell(keyTbl, 0, r, "VAL",  text_color=vp_valColor, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 1, r, not na(valP) ? str.tostring(valP, format.mintick) : "—",
                          text_color=vp_valColor, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 2, r, key_showVolPct ? f_pct(valVolPct) : "—",
                          text_color=sg, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 3, r, key_showDelta  ? (valDelta >= 0 ? "+" : "") + f_pct(valDelta) : "—",
                          text_color=valDelta >= 0 ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
table.cell(keyTbl, 4, r, valDelta >= 0 ? "▲" : "▼",
                          text_color=valDelta >= 0 ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
r += 1

// ── HVN SECTION HEADER ───────────────────────────────────────────
table.cell(keyTbl, 0, r, "── HVN ──", text_color=color.new(vn_peakColor, 0), text_size=size.tiny, bgcolor=sec_bg, text_halign=text.align_center)
table.cell(keyTbl, 1, r, "High Vol Node", text_color=sg,    text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 2, r, "Vol%",          text_color=sg,    text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 3, r, "Δ%",            text_color=sg,    text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 4, r, "Bias",          text_color=sg,    text_size=size.tiny, bgcolor=sec_bg)
r += 1

// ── HVN ROWS ─────────────────────────────────────────────────────
// Sort HVNs by volume% descending for the top-N display
if topHVN > 0
    // Simple insertion sort on hvnVolPct (descending)
    for i = 1 to hvnCount - 1
        float kP  = hvnPrice.get(i)
        float kV  = hvnVolPct.get(i)
        float kD  = hvnDeltaPct.get(i)
        bool  kB  = hvnBullBias.get(i)
        int   j   = i - 1
        while j >= 0 and hvnVolPct.get(j) < kV
            hvnPrice.set(j + 1,    hvnPrice.get(j))
            hvnVolPct.set(j + 1,   hvnVolPct.get(j))
            hvnDeltaPct.set(j + 1, hvnDeltaPct.get(j))
            hvnBullBias.set(j + 1, hvnBullBias.get(j))
            j -= 1
        hvnPrice.set(j + 1,    kP)
        hvnVolPct.set(j + 1,   kV)
        hvnDeltaPct.set(j + 1, kD)
        hvnBullBias.set(j + 1, kB)

    for n = 0 to topHVN - 1
        float hp  = hvnPrice.get(n)
        float hv  = hvnVolPct.get(n)
        float hd  = hvnDeltaPct.get(n)
        bool  hb  = hvnBullBias.get(n)
        color hc  = hb ? color.new(color.lime, 20) : color.new(color.red, 20)
        table.cell(keyTbl, 0, r, "HVN " + str.tostring(n + 1),
             text_color=color.new(vn_peakColor, 0), text_size=size.tiny, bgcolor=sbg)
        table.cell(keyTbl, 1, r, str.tostring(hp, format.mintick),
             text_color=w, text_size=size.tiny, bgcolor=sbg)
        table.cell(keyTbl, 2, r, key_showVolPct ? f_pct(hv) : "—",
             text_color=sg, text_size=size.tiny, bgcolor=sbg)
        table.cell(keyTbl, 3, r, key_showDelta ? (hd >= 0 ? "+" : "") + f_pct(hd) : "—",
             text_color=hd >= 0 ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
        table.cell(keyTbl, 4, r, hb ? "▲ Bull" : "▼ Bear",
             text_color=hb ? color.lime : color.red, text_size=size.tiny, bgcolor=color.new(hc, 55))
        r += 1
else
    table.cell(keyTbl, 0, r, "None detected", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    table.cell(keyTbl, 1, r, "—", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    table.cell(keyTbl, 2, r, "—", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    table.cell(keyTbl, 3, r, "—", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    table.cell(keyTbl, 4, r, "—", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    r += 1

// ── LVN SECTION HEADER ───────────────────────────────────────────
table.cell(keyTbl, 0, r, "── LVN ──", text_color=color.new(vn_troughColor, 0), text_size=size.tiny, bgcolor=sec_bg, text_halign=text.align_center)
table.cell(keyTbl, 1, r, "Low Vol Node",  text_color=sg, text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 2, r, "Vol%",          text_color=sg, text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 3, r, "Δ%",            text_color=sg, text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 4, r, "Bias",          text_color=sg, text_size=size.tiny, bgcolor=sec_bg)
r += 1

// ── LVN ROWS ─────────────────────────────────────────────────────
if topLVN > 0
    for n = 0 to topLVN - 1
        float lp  = lvnPrice.get(n)
        float lv  = lvnVolPct.get(n)
        float ld  = lvnDeltaPct.get(n)
        bool  lb  = lvnBullBias.get(n)
        table.cell(keyTbl, 0, r, "LVN " + str.tostring(n + 1),
             text_color=color.new(vn_troughColor, 0), text_size=size.tiny, bgcolor=sbg)
        table.cell(keyTbl, 1, r, str.tostring(lp, format.mintick),
             text_color=w, text_size=size.tiny, bgcolor=sbg)
        table.cell(keyTbl, 2, r, key_showVolPct ? f_pct(lv) : "—",
             text_color=sg, text_size=size.tiny, bgcolor=sbg)
        table.cell(keyTbl, 3, r, key_showDelta ? (ld >= 0 ? "+" : "") + f_pct(ld) : "—",
             text_color=ld >= 0 ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
        table.cell(keyTbl, 4, r, lb ? "▲ Bull" : "▼ Bear",
             text_color=lb ? color.lime : color.red, text_size=size.tiny, bgcolor=sbg)
        r += 1
else
    table.cell(keyTbl, 0, r, "None detected", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    table.cell(keyTbl, 1, r, "—", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    table.cell(keyTbl, 2, r, "—", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    table.cell(keyTbl, 3, r, "—", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    table.cell(keyTbl, 4, r, "—", text_color=color.gray, text_size=size.tiny, bgcolor=sbg)
    r += 1

// ── ZONE + OVERALL DELTA BIAS ─────────────────────────────────────
table.cell(keyTbl, 0, r, "Zone",    text_color=sg,      text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 1, r, zoneTxt,   text_color=zoneCol, text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 2, r, "Δ Bias",  text_color=sg,      text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 3, r, (overallDelta >= 0 ? "+" : "") + f_pct(overallDelta),
                          text_color=biasCol, text_size=size.tiny, bgcolor=sec_bg)
table.cell(keyTbl, 4, r, bullBias ? "▲ BULL" : "▼ BEAR",
                          text_color=biasCol, text_size=size.small, bgcolor=color.new(biasCol, 70))

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors