Liquidity Structure & Order Flow [LuxAlgo]

LuxAlgo · study · 462 行 · 点赞 5,978 · TradingView 原页

本页源码来自 TradingView 公开发布的开源脚本,版权归原作者所有, 请遵循其原始许可(Pine 脚本常见 CC BY-NC-SA / MPL-2.0 / MIT)。 本项目仅用于研究检索与许可范围内的移植。

Pine Script

// 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 Structure & Order Flow [LuxAlgo]", "LuxAlgo - Liquidity Structure & Order Flow", overlay = true, max_labels_count = 500, max_boxes_count = 500, max_lines_count = 500)
 
//---------------------------------------------------------------------------------------------------------------------}
// Constants
//---------------------------------------------------------------------------------------------------------------------{
color BULL_COLOR = #089981
color BEAR_COLOR = #f23645
color POC_COLOR  = #ff9800
 
string G1 = "Volume Profile Settings"
string G2 = "Visualization"
string G3 = "Dashboard"
string G4 = "Unusual Volume Bubbles"
 
// Table 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'
 
int BINS_COUNT = 150
//---------------------------------------------------------------------------------------------------------------------}
// Inputs
//---------------------------------------------------------------------------------------------------------------------{
lookbackInput     = input.int(300, "Lookback Period", minval = 50, group = G1)
weightedInput     = input.bool(false, "Weight Recent Volume", group = G1)
vaPercentInput    = input.float(70.0, "Value Area %", minval = 1, maxval = 100, step = 5, group = G1)
smoothingInput    = input.int(3, "Profile Smoothing", minval = 0, maxval = 10, group = G1)
 
showHeatmapInput  = input.bool(true, "Show Heatmap", group = G2)
heatmapTranspInput= input.int(40, "Heatmap Transparency", minval = 0, maxval = 100, group = G2)
 
// --- Volume Delta Panel Inputs ---
G_DELTA           = "Volume Delta Backdrop"
showPriceDeltaInput      = input.bool(true, "Show Volume Delta Panel", group = G_DELTA)
priceDeltaIntensityInput = input.int(50, "Delta Intensity", minval = 0, maxval = 100, group = G_DELTA)
priceDeltaWidthInput     = input.int(100, "Delta Panel Width", minval = 10, maxval = 300, group = G_DELTA)
deltaEmaLenInput         = input.int(5, "Delta Smoothing Length", minval = 1, group = G_DELTA)
 
showPocInput      = input.bool(true, "Show Point of Control (POC)", group = G2)
showVoidInput     = input.bool(true, "Highlight Widest Void", group = G2)
showVaInput       = input.bool(true, "Show Value Area (VAH/VAL)", group = G2)
curveResolutionInput = input.int(2, "Curve Resolution (Sampling)", minval = 1, maxval = 10, group = G2)
curvesOffsetInput = input.int(100, "Curves X-Offset (Future Bars)", minval = 20, group = G2)
 
bullColorInput    = input.color(BULL_COLOR, "Lower Depth Color", group = G2, inline = "c1")
bearColorInput    = input.color(BEAR_COLOR, "Upper Depth Color", group = G2, inline = "c1")
 
// --- Unusual Volume Bubbles ---
showBubblesInput  = input.bool(true, "Show Bubbles", group = G4)
volThresholdInput = input.float(2.0, "Sensitivity (Z-Score)", minval = 0.5, maxval = 10.0, step = 0.5, group = G4, tooltip = "Standard deviations above mean volume to trigger a bubble.")
bubbleMinSize     = input.string(size.tiny, "Min Bubble Size", options = [size.tiny, size.small, size.normal], group = G4, inline = "sz")
bubbleMaxSize     = input.string(size.large, "Max Bubble Size", options = [size.small, size.normal, size.large, size.huge], group = G4, inline = "sz")
 
showDashInput     = input.bool(true, "Show Dashboard", group = G3)
dashPosInput      = input.string(TOP_RIGHT, "Position", options = [TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT], group = G3)
dashSizeInput     = input.string(SMALL, "Size", options = [TINY, SMALL, NORMAL], group = G3)
//---------------------------------------------------------------------------------------------------------------------}
// Types & Variables
//---------------------------------------------------------------------------------------------------------------------{
var float[] rawBins = array.new_float(BINS_COUNT, 0.0)
var float[] smoothBins = array.new_float(BINS_COUNT, 0.0)
var box[] heatmapBoxes = array.new<box>()
var box[] deltaPanelBoxes = array.new<box>()
var line[] pocGlowLines = array.new<line>()
var label[] bubbleLabels = array.new<label>()
var line[] bubbleLines = array.new<line>()
 
var parsedDashPos = switch dashPosInput
    TOP_RIGHT    => position.top_right
    BOTTOM_RIGHT => position.bottom_right
    BOTTOM_LEFT  => position.bottom_left
 
var parsedDashSize = switch dashSizeInput
    TINY   => size.tiny
    SMALL  => size.small
    NORMAL => size.normal
//---------------------------------------------------------------------------------------------------------------------}
// Functions
//---------------------------------------------------------------------------------------------------------------------{
cell(table t, int col, int row, string txt, color c = #FFFFFF, string halign = text.align_right, color bg = na) => 
    t.cell(col, row, txt, text_color = c, text_size = parsedDashSize, text_halign = halign, bgcolor = bg)
 
divider(table t, int row, int lastCol) =>    
    t.merge_cells(0, row, lastCol, row)
    cell(t, 0, row, '━━━━━━━━━━━━━━━━━━', c = BORDERS, halign = text.align_center)
 
// --- Volume Delta Logic ---
getDeltaColor(float val, float maxMag, int intensity) =>
    float norm = maxMag > 0 ? math.abs(val) / maxMag : 0.0
    color base = val >= 0 ? BULL_COLOR : BEAR_COLOR
    int transp = 100 - int(norm * (intensity / 100.0) * 85)
    color.new(base, transp)
 
//---------------------------------------------------------------------------------------------------------------------}
// Calculations
//---------------------------------------------------------------------------------------------------------------------{
highs      = ta.highest(lookbackInput)
lows       = ta.lowest(lookbackInput)
priceRange = highs - lows
mid        = math.avg(highs, lows)
binStep    = priceRange / BINS_COUNT
 
// --- Unusual Volume Detection (Dynamic Baseline) ---
// Using a 200-period lookback for a more stable "Normal" volume mean across different assets
volMa      = ta.sma(volume, 200)
volStd     = ta.stdev(volume, 200)
 
// Safety Buffer for Historical Data
max_bars_back(volume, 500), max_bars_back(close, 500), max_bars_back(open, 500)
max_bars_back(low, 500), max_bars_back(high, 500), max_bars_back(volMa, 500), max_bars_back(volStd, 500)
 
// Continuous stream for EMA calculation
float rawBarDelta = close > open ? volume : close < open ? -volume : 0.0
float smoothDelta = ta.ema(rawBarDelta, deltaEmaLenInput)
max_bars_back(smoothDelta, 500)
 
if barstate.islast
    array.fill(rawBins, 0.0)
    array.fill(smoothBins, 0.0)
    
    float[] dProfile = array.new_float(BINS_COUNT, 0.0)
    float buyAgg = 0.0, sellAgg = 0.0
 
    // 1. Calculate Raw Profile & Delta Distribution
    for i = 0 to lookbackInput - 1
        float w = weightedInput ? (1.0 - (float(i) / lookbackInput)) : 1.0
        float p = close[i], v = nz(volume[i]) * w
        
        if close[i] > open[i] 
            buyAgg += v 
        else if close[i] < open[i] 
            sellAgg += v
            
        int bIdx = math.min(BINS_COUNT - 1, math.max(0, int((p - lows) / binStep)))
        array.set(rawBins, bIdx, array.get(rawBins, bIdx) + v)
        array.set(dProfile, bIdx, array.get(dProfile, bIdx) + smoothDelta[i])
 
    // 2. Smooth Liquidity Heatmap
    for k = 0 to BINS_COUNT - 1
        float sum = 0.0, wSum = 0.0
        for s = -smoothingInput to smoothingInput
            int idx = k + s
            if idx >= 0 and idx < BINS_COUNT
                float sw = 1.0 / (1.0 + math.abs(s))
                sum += array.get(rawBins, idx) * sw
                wSum += sw
        array.set(smoothBins, k, sum / wSum)
 
    float maxVol   = array.max(smoothBins)
    float totalVol = array.sum(rawBins)
    int   pocIdx   = array.indexof(smoothBins, maxVol)
    float pocPrice = lows + (binStep * pocIdx) + (binStep / 2)
    
    // 3. Widest Void Logic
    float vThres = maxVol * 0.15
    int lVoidStart = 0, lVoidEnd = 0, cVoidStart = -1, mVoidLen = 0
    for k = 0 to BINS_COUNT - 1
        if array.get(smoothBins, k) < vThres
            if cVoidStart == -1 
                cVoidStart := k
            int len = k - cVoidStart + 1
            if len > mVoidLen
                mVoidLen := len
                lVoidStart := cVoidStart
                lVoidEnd := k
        else
            cVoidStart := -1
    
    float voidTop = lows + (lVoidEnd + 1) * binStep, voidBot = lows + lVoidStart * binStep, voidMid = math.avg(voidTop, voidBot)
 
    // 4. Value Area
    float vaTarget = totalVol * (vaPercentInput / 100.0)
    float curVaVol = array.get(smoothBins, pocIdx)
    int upIdx = pocIdx, dnIdx = pocIdx
    while curVaVol < vaTarget and (upIdx < BINS_COUNT - 1 or dnIdx > 0)
        float uV = upIdx < BINS_COUNT - 1 ? array.get(smoothBins, upIdx + 1) : 0.0
        float dV = dnIdx > 0 ? array.get(smoothBins, dnIdx - 1) : 0.0
        if uV >= dV and upIdx < BINS_COUNT - 1
            upIdx += 1
            curVaVol += uV
        else if dnIdx > 0
            dnIdx -= 1
            curVaVol += dV
        else 
            break
    float vah = lows + (upIdx + 1) * binStep, val = lows + (dnIdx) * binStep
 
    float upperVolTotal = 0.0, lowerVolTotal = 0.0
    for k = 0 to BINS_COUNT - 1
        if (lows + k * binStep) >= mid 
            upperVolTotal += array.get(smoothBins, k) 
        else 
            lowerVolTotal += array.get(smoothBins, k)
    
    float upperPerc = (upperVolTotal / totalVol) * 100, lowerPerc = (lowerVolTotal / totalVol) * 100
    float deltaAgg  = buyAgg - sellAgg
    float skewRatio = upperVolTotal / math.max(0.1, lowerVolTotal)
    float distToPoc = (close - pocPrice) / close * 100
    float rangeLoc  = (close - lows) / math.max(0.0001, priceRange) * 100
 
    //---------------------------------------------------------------------------------------------------------------------}
    // Visuals
    //---------------------------------------------------------------------------------------------------------------------{
    // Cleanup
    for b in heatmapBoxes 
        box.delete(b)
    array.clear(heatmapBoxes)
    for b in deltaPanelBoxes 
        box.delete(b)
    array.clear(deltaPanelBoxes)
    for l in bubbleLabels
        label.delete(l)
    array.clear(bubbleLabels)
    for l in bubbleLines
        line.delete(l)
    array.clear(bubbleLines)
 
    // Calculate Polyline Offsets Early to avoid Overlap
    float maxCum = 0.0, tempCum = 0.0
    for k = 0 to BINS_COUNT - 1
        tempCum += array.get(smoothBins, k) / maxVol
        if k == int(BINS_COUNT/2) or k == BINS_COUNT - 1
            maxCum := math.max(maxCum, tempCum), tempCum := 0.0
    float scaleFactor = (curvesOffsetInput * 0.9) / math.max(0.1, maxCum)
 
    float[] curveX = array.new_float(BINS_COUNT, float(bar_index + curvesOffsetInput))
    int midBin = int(BINS_COUNT/2)
    // Upper Curve
    for k = midBin to BINS_COUNT - 1
        float localCum = 0.0
        for s = midBin to k 
            localCum += array.get(smoothBins, s) / maxVol
        array.set(curveX, k, bar_index + curvesOffsetInput - (localCum * scaleFactor))
    // Lower Curve
    for k = midBin - 1 to 0
        float localCum = 0.0
        for s = k to midBin - 1 
            localCum += array.get(smoothBins, s) / maxVol
        array.set(curveX, k, bar_index + curvesOffsetInput - (localCum * scaleFactor))
 
    // 5. Volume Delta Panel (LAYER 1: BACK)
    if showPriceDeltaInput
        float maxD = 0.0
        for dVal in dProfile 
            maxD := math.max(maxD, math.abs(dVal))
        for k = 0 to BINS_COUNT - 1
            float dVal = array.get(dProfile, k)
            float bBot = lows + (binStep * k), bTop = bBot + binStep
            
            // Only render delta if the bin is within the Value Area (VAL to VAH)
            if math.abs(dVal) > 0 and bBot >= val and bTop <= vah
                color dCol = getDeltaColor(dVal, maxD, priceDeltaIntensityInput)
                // Stop box at polyline edge
                int xLimit = int(array.get(curveX, k))
                if xLimit > bar_index
                    array.push(deltaPanelBoxes, box.new(bar_index, bTop, xLimit, bBot, na, bgcolor = dCol))
 
    // 6. Liquidity Heatmap (LAYER 2)
    if showHeatmapInput
        for k = 0 to BINS_COUNT - 1
            float norm = array.get(smoothBins, k) / maxVol
            if norm > 0.01
                float bBot = lows + (binStep * k), bTop = bBot + binStep
                int dyTr = int(heatmapTranspInput + (100 - heatmapTranspInput) * (1.0 - math.pow(norm, 0.7)))
                color bCl = (bBot + binStep/2) > mid ? bearColorInput : bullColorInput
                array.push(heatmapBoxes, box.new(bar_index - lookbackInput, bTop, bar_index, bBot, na, bgcolor = color.new(bCl, dyTr)))
        array.push(heatmapBoxes, box.new(bar_index - lookbackInput, highs, bar_index, lows, border_color = color.new(chart.fg_color, 70), border_style = line.style_dotted, bgcolor = na))
 
    // 6.1 Unusual Volume Bubbles (Smart Lane Anti-Overlap System)
    if showBubblesInput
        float bearBaseY = highs + (priceRange * 0.08)
        float bullBaseY = lows  - (priceRange * 0.08)
        float laneGap   = priceRange * 0.06 
        
        float[] bearLanesX = array.from(1e10, 1e10, 1e10) 
        float[] bullLanesX = array.from(1e10, 1e10, 1e10)
 
        for i = 0 to lookbackInput - 1
            float v  = nz(volume[i])
            float ma = nz(volMa[i])
            float sd = nz(volStd[i])
            
            // DYNAMIC CALCULATION: 
            // 1. Z-Score identifies statistical outliers (Unusual spikes).
            // 2. RVOL filter ensures the spike is at least 1.5x the average volume.
            // This dual-check prevents false positives in low-volatility assets (Forex/Quiet Stocks).
            float z    = sd > 0 ? (v - ma) / sd : 0.0
            float rvol = ma > 0 ? v / ma : 0.0
            
            if z >= volThresholdInput and rvol > 1.5
                bool isBull = close[i] > open[i]
                color base  = isBull ? bullColorInput : bearColorInput
                int x       = bar_index - i
                
                int clearance = z >= volThresholdInput + 5 ? 14 : z >= volThresholdInput + 3 ? 10 : 6
                
                // --- Smart Lane Selection ---
                int lane = 0
                float[] activeLanesX = isBull ? bullLanesX : bearLanesX
                
                // Find the first lane that has enough horizontal clearance
                bool foundLane = false
                for l = 0 to 2
                    if array.get(activeLanesX, l) - x >= clearance
                        lane := l
                        array.set(activeLanesX, l, float(x))
                        foundLane := true
                        break
                
                // If no lane is clear, use the one with the largest distance
                if not foundLane
                    lane := array.indexof(activeLanesX, array.max(activeLanesX))
                    array.set(activeLanesX, lane, float(x))
 
                float targetY = isBull ? bullBaseY - (lane * laneGap) : bearBaseY + (lane * laneGap)
                
                // --- Vertical Connector Line ---
                float startY = isBull ? low[i] : high[i]
                line l = line.new(x, startY, x, targetY, 
                     color = color.new(base, 75), 
                     style = line.style_dashed, 
                     width = 1)
                array.push(bubbleLines, l)
 
                // --- Bubble Rendering ---
                string bSize = z < volThresholdInput + 1 ? size.tiny : 
                               z < volThresholdInput + 3 ? size.small : 
                               z < volThresholdInput + 5 ? size.normal : 
                               z < volThresholdInput + 7 ? size.large : size.huge
                
                float normZ = math.min(1.0, (z - volThresholdInput) / 6.0)
                color bCol  = color.new(base, 20 + int((1.0 - normZ) * 60))
                string volTxt = z >= volThresholdInput + 3 ? str.tostring(nz(volume[i]), format.volume) : ""
 
                // Glow Layer
                if z >= volThresholdInput + 4
                    label glowLbl = label.new(x, targetY, "", 
                         color = color.new(base, 85), 
                         style = label.style_circle, 
                         size  = size.huge)
                    array.push(bubbleLabels, glowLbl)
                
                // Main Bubble
                label mainLbl = label.new(x, targetY, volTxt, 
                     color     = bCol, 
                     textcolor = color.white,
                     style     = volTxt != "" ? label.style_label_center : label.style_circle, 
                     size      = bSize)
                array.push(bubbleLabels, mainLbl)
 
    var label pocL = na, label.delete(pocL), var label voidL = na, label.delete(voidL)
    var box voidZ = na, box.delete(voidZ), var line vHL = na, line.delete(vHL), var line vLL = na, line.delete(vLL), var line mLL = na, line.delete(mLL)
    for l in pocGlowLines 
        line.delete(l)
    array.clear(pocGlowLines)
 
    int boxEnd = bar_index, drawEnd = bar_index + curvesOffsetInput
    if showPocInput
        array.push(pocGlowLines, line.new(bar_index-lookbackInput, pocPrice, boxEnd, pocPrice, color = color.new(POC_COLOR, 85), width = 10))
        array.push(pocGlowLines, line.new(bar_index-lookbackInput, pocPrice, boxEnd, pocPrice, color = color.new(POC_COLOR, 60), width = 4))
        array.push(pocGlowLines, line.new(bar_index-lookbackInput, pocPrice, boxEnd, pocPrice, color = POC_COLOR, width = 1))
        pocL := label.new(boxEnd + 2, pocPrice, "POC", color = POC_COLOR, style = label.style_label_left, textcolor = color.white, size = size.small)
    
    if showVoidInput
        voidZ := box.new(bar_index + 2, voidTop, bar_index + 6, voidBot, border_color = color.new(chart.fg_color, 70), bgcolor = color.new(chart.fg_color, 90))
        voidL := label.new(bar_index + 6, voidMid, "Void", color = color.new(chart.fg_color, 30), style = label.style_label_left, textcolor = chart.bg_color, size = size.small)
    
    if showVaInput
        vHL := line.new(boxEnd, vah, drawEnd, vah, color = bearColorInput, style = line.style_dotted)
        vLL := line.new(boxEnd, val, drawEnd, val, color = bullColorInput, style = line.style_dotted)
    mLL := line.new(boxEnd, mid, drawEnd, mid, color = color.new(chart.fg_color, 70), style = line.style_dotted)
 
    // 7. Cumulative Smooth Curves (LAYER 3: TOP)
    chart.point[] cpS_f = array.new<chart.point>(), chart.point[] cpB_f = array.new<chart.point>()
    chart.point[] cpS_l = array.new<chart.point>(), chart.point[] cpB_l = array.new<chart.point>()
    
    // Setup Upper Curve
    array.push(cpS_f, chart.point.from_index(bar_index + curvesOffsetInput, mid))
    for k = midBin to BINS_COUNT - 1 by curveResolutionInput
        chart.point p = chart.point.from_index(int(array.get(curveX, k)), lows + (binStep * k) + (binStep / 2))
        array.push(cpS_f, p)
        array.push(cpS_l, p)
    array.push(cpS_f, chart.point.from_index(bar_index + curvesOffsetInput, highs))
 
    // Setup Lower Curve
    array.push(cpB_f, chart.point.from_index(bar_index + curvesOffsetInput, mid))
    for i = 0 to (midBin - 1) by curveResolutionInput
        int k = midBin - 1 - i
        chart.point p = chart.point.from_index(int(array.get(curveX, k)), lows + (binStep * k) + (binStep / 2))
        array.push(cpB_f, p)
        array.push(cpB_l, p)
    array.push(cpB_f, chart.point.from_index(bar_index + curvesOffsetInput, lows))
 
    var polyline pS = na, polyline.delete(pS), var polyline pB = na, polyline.delete(pB)
    var polyline pSL = na, polyline.delete(pSL), var polyline pBL = na, polyline.delete(pBL)
    
    pS  := polyline.new(cpS_f, closed = true,  fill_color = color.new(bearColorInput, 80), line_color = na)
    pB  := polyline.new(cpB_f, closed = true,  fill_color = color.new(bullColorInput, 80), line_color = na)
    pSL := polyline.new(cpS_l, closed = false, line_color = bearColorInput, line_width = 2)
    pBL := polyline.new(cpB_l, closed = false, line_color = bullColorInput, line_width = 2)
 
    // --- Dynamic Label Positioning for Polylines ---
    float xHeatmapEnd = float(bar_index)
    float xSpine      = bar_index + curvesOffsetInput
    float xMid        = bar_index + (curvesOffsetInput / 2)
    
    // Get actual bulge depth at the outer-most bins
    float xUpperTip = array.get(curveX, BINS_COUNT - 1)
    float xLowerTip = array.get(curveX, 0)
    
    // Logic: 
    // - If bulge is missing/zero: Anchor to Heatmap Edge (bar_index)
    // - If bulge is large (past mid): Anchor to Midpoint
    // - If bulge is small (present but before mid): Anchor to the Bulge Tip Vertex
    float labelXUp = upperVolTotal == 0 or xUpperTip >= xSpine ? xHeatmapEnd : (xUpperTip <= xMid ? xMid : xUpperTip)
    float labelXDn = lowerVolTotal == 0 or xLowerTip >= xSpine ? xHeatmapEnd : (xLowerTip <= xMid ? xMid : xLowerTip)
 
    var label lblUp = na, label.delete(lblUp), var label lblDn = na, label.delete(lblDn)
    lblUp := label.new(int(labelXUp), highs, "Upper Vol: " + str.tostring(upperVolTotal, format.volume), 
         color     = bearColorInput, 
         style     = labelXUp == xMid ? label.style_label_down : label.style_label_right, 
         textcolor = color.white, 
         size      = size.small)
         
    lblDn := label.new(int(labelXDn), lows,  "Lower Vol: " + str.tostring(lowerVolTotal, format.volume), 
         color     = bullColorInput, 
         style     = labelXDn == xMid ? label.style_label_up : label.style_label_right,   
         textcolor = color.white, 
         size      = size.small)
 
    if showDashInput
        var table dash = table.new(parsedDashPos, 2, 13, bgcolor = BACKGROUND, frame_color = BORDERS, frame_width = 1)
        table.merge_cells(dash, 0, 0, 1, 0), cell(dash, 0, 0, "Liquidity Depth", DATA, text.align_center), divider(dash, 1, 1)
        cell(dash, 0, 2, "POC Price", HEADERS, text.align_left), cell(dash, 1, 2, str.tostring(pocPrice, format.mintick), DATA)
        cell(dash, 0, 3, "Dist. to POC", HEADERS, text.align_left), cell(dash, 1, 3, str.tostring(distToPoc, "#.##") + "%", DATA)
        cell(dash, 0, 4, "VA Range", HEADERS, text.align_left), cell(dash, 1, 4, str.tostring(val, format.mintick) + " - " + str.tostring(vah, format.mintick), DATA)
        divider(dash, 5, 1)
        cell(dash, 0, 6, "Mass Skew", HEADERS, text.align_left), cell(dash, 1, 6, str.tostring(skewRatio, "#.##") + "x", DATA)
        cell(dash, 0, 7, "Upper Mass", bearColorInput, text.align_left), cell(dash, 1, 7, str.tostring(upperPerc, "#.##") + "%", DATA)
        cell(dash, 0, 8, "Lower Mass", bullColorInput, text.align_left), cell(dash, 1, 8, str.tostring(lowerPerc, "#.##") + "%", DATA)
        divider(dash, 9, 1)
        cell(dash, 0, 10, "Agg. Delta", HEADERS, text.align_left), cell(dash, 1, 10, str.tostring(deltaAgg, format.volume), deltaAgg >= 0 ? BULL_COLOR : BEAR_COLOR)
        cell(dash, 0, 11, "Range Loc.", HEADERS, text.align_left), cell(dash, 1, 11, str.tostring(rangeLoc, "#.##") + "%", DATA)
        cell(dash, 0, 12, "Total Volume", HEADERS, text.align_left), cell(dash, 1, 12, str.tostring(totalVol, format.volume), DATA)
 
// 8. Unusual Volume Alert
isUnusual = volume > volMa + (volThresholdInput * volStd)
alertcondition(isUnusual, "Unusual Volume Detected", "Extreme volume spike detected on {{ticker}}")
//---------------------------------------------------------------------------------------------------------------------}

← 返回列表