Volume Gaps & Imbalances (Zeiierman)

Zeiierman · study · 304 行 · 点赞 8,276 · TradingView 原页

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

Pine Script

// This work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International  
// https://creativecommons.org/licenses/by-nc-sa/4.0/
// © Zeiierman {
//@version=6
indicator("Volume Gaps & Imbalances (Zeiierman)", overlay = true, max_boxes_count = 500)
// ~~ Tooltips {
var string t1  = "Number of historical bars used to build the volume profile and zero-volume gaps.\nHigher = smoother, more stable profile but heavier on performance."
var string t2  = "Number of price rows (bins) between the highest and lowest price in the lookback range.\nHigher = more detailed profile, lower = more compact."
var string t3  = "Price source used when assigning each bar to a price row in the profile (e.g. HLC3, Close)."
var string t4  = "Horizontal width in bars for the main volume profile drawn to the right of price."
var string t5  = "Fill color for the bullish portion of each price row (bars where Close > Open)."
var string t6  = "Fill color for the bearish portion of each price row (bars where Close <= Open)."
var string t7  = "Background color used to highlight zero-volume price gaps (no traded volume in that row)."
var string t8  = "Number of stacked sections in the delta panel.\nEach section aggregates Buy/Sell delta for a vertical slice of the full profile."
var string t9  = "Horizontal width in bars of the delta summary panel."
var string t10 = "Horizontal gap, in bars, between the main volume profile and the delta panel."
var string t11 = "Show or hide the Δ (delta) percentage text inside each delta bar."
var string t12 = "Color used when delta is positive in a section (Buy volume > Sell volume)."
var string t13 = "Color used when delta is negative in a section (Sell volume > Buy volume)."
var string t14 = "Background color of the delta panel and neutral areas when there is little or no delta."
var string t15 = "Text color for the Δ percentage labels inside the delta bars."
var string t16 = "Minimum visual size of any non-zero delta bar as a fraction of the panel width.\nUse a larger value to keep small deltas visible."
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
 
// ~~ Settings {
// ~~ Settings {
prd         = input.int(200, "Lookback", minval = 50, maxval = 2000, tooltip = t1, group = "Profile Settings")
rows        = input.int(50, "Rows", minval = 10, maxval = 100, inline = "a", tooltip = t2, group = "Profile Settings")
src         = input.source(hlc3, "", inline = "a", tooltip = t1 + "\n\n" + t2 + "\n\n" + t3, group = "Profile Settings")
 
width       = input.int(100, "Profile Placement", minval = 1, maxval = 500, tooltip = t4, group = "Profile Styling")
bull_color  = input.color(color.new(color.blue, 30), "Bull Color", inline = "prof_set", tooltip = t5, group = "Profile Styling")
bear_color  = input.color(color.new(color.orange, 30), "Bear Color", inline = "prof_set", tooltip = t6, group = "Profile Styling")
zone_color  = input.color(color.new(color.navy, 50), "Zero-Volume Zone", inline = "prof_set", tooltip = t5 + "\n\n" + t6 + "\n\n" + t7, group = "Profile Styling")
 
sum_sections   = input.int(20, "Summary Sections", minval = 1, maxval = 100, tooltip = t8, group = "Delta Summary")
sum_panel_w    = input.int(40, "Summary Width", minval = 10, maxval = 200, tooltip = t9, group = "Delta Summary")
sum_gap_x      = input.int(4,  "Gap From Profile", minval = 1, maxval = 50, tooltip = t10, group = "Delta Summary")
sum_show_label = input.bool(true, "Show Delta Text", tooltip = t11, group = "Delta Summary")
 
delta_pos_color  = input.color(color.new(color.lime, 20), "Delta Buy Color", tooltip = t12, group = "Delta Styling")
delta_neg_color  = input.color(color.new(color.red,  20), "Delta Sell Color", tooltip = t13, group = "Delta Styling")
delta_neutral_bg = input.color(color.new(color.gray, 90), "Delta Neutral BG", tooltip = t14, group = "Delta Styling")
delta_text_color = input.color(color.white, "Delta Text Color", tooltip = t15, group = "Delta Styling")
delta_min_frac   = input.float(0.2, "Delta Min Size (fraction of width)", minval = 0.0, maxval = 1.0, step = 0.1, tooltip = t16, group = "Delta Styling")
//~~}
 
// ~~ Variables {
b = bar_index
 
lvls        = array.new<float>()
bull_vols   = array.new<float>()
bear_vols   = array.new<float>()
zone        = array.new<box>()
//~~}
 
// ~~ Main {
hi = ta.highest(high, prd)
lo = ta.lowest(low, prd)
 
if barstate.islast
    // Clear old boxes & labels
    for e in box.all
        e.delete()
    for l in label.all
        label.delete(l)
 
    //Profile Range Box
    box.new(
         chart.point.from_index(b-prd,hi),
         chart.point.from_index(b+width,lo),
         color.new(chart.fg_color,50),
         2,
         line.style_dotted,
         bgcolor = color(na)
     )
 
    // Build levels
    step = (hi - lo) / rows
    for i = lo to hi by step
        lvls.push(i)
        bull_vols.push(0.0)
        bear_vols.push(0.0)
 
    // Volume assignment
    for i = prd to 0
        price   = src[i]
        is_bull = close[i] > open[i]
        for j = 0 to lvls.size() - 2
            levelLow  = lvls.get(j)
            levelHigh = lvls.get(j + 1)
            if price > levelLow and price <= levelHigh
                if is_bull
                    bull_vols.set(j, bull_vols.get(j) + volume[i])
                else
                    bear_vols.set(j, bear_vols.get(j) + volume[i])
                break
 
    // Calculate maxVol based on total volumes
    maxVol = 0.0
    for i = 0 to lvls.size() - 2
        vol_i = bull_vols.get(i) + bear_vols.get(i)
        if vol_i > maxVol
            maxVol := vol_i
 
    // Create profile
    for i = 0 to lvls.size() - 2
        bull_i = bull_vols.get(i)
        bear_i = bear_vols.get(i)
        vol_i  = bull_i + bear_i
        norm_v = maxVol > 0 ? math.round((vol_i / maxVol) * width) : 0
 
        if norm_v > 0
            norm_bull  = bull_i > 0 ? math.round((bull_i / vol_i) * norm_v) : 0
            norm_bear  = norm_v - norm_bull
            left_start = b + width - norm_v
 
            // Draw bull box
            if norm_bull > 0
                box.new(
                     left   = left_start,
                     top    = lvls.get(i + 1),
                     right  = left_start + norm_bull,
                     bottom = lvls.get(i),
                     border_color = color.new(chart.fg_color,90),
                     bgcolor = bull_color
                 )
 
            // Draw bear box
            if norm_bear > 0
                box.new(
                     left   = left_start + norm_bull,
                     top    = lvls.get(i + 1),
                     right  = b + width,
                     bottom = lvls.get(i),
                     border_color = color.new(chart.fg_color,90),
                     bgcolor = bear_color
                 )
 
        // Zero-volume zone
        if vol_i == 0
            zeroBox = box.new(
                 left   = b - prd,
                 top    = lvls.get(i + 1),
                 right  = b,
                 bottom = lvls.get(i),
                 border_color = na,
                 bgcolor = zone_color
             )
            zone.push(zeroBox)
 
    //Zone merge
    if zone.size() > 1
        i = 0
        while i < zone.size()
            currentBox = zone.get(i)
            currentTop = currentBox.get_top()
 
            // Look ahead to find the end of consecutive touching zones
            j = i + 1
            while j < zone.size()
                nextBox = zone.get(j)
                if math.abs(nextBox.get_bottom() - currentTop) > 1e-10
                    break
                currentTop := nextBox.get_top()
                j += 1
 
            chainLength = j - i
 
            if chainLength > 1
                // Merge all boxes into one big box
                firstBox = zone.get(i)
                lastBox  = zone.get(j - 1)
 
                mergedBox = box.new(
                     left   = b - prd,
                     top    = lastBox.get_top(),
                     right  = b,
                     bottom = firstBox.get_bottom(),
                     border_color = na,
                     bgcolor = zone_color
                 )
 
                // Delete old boxes (from last to first to preserve indices while removing)
                for k = j - 1 to i by 1
                    boxToDelete = zone.get(k)
                    boxToDelete.delete()
                    zone.remove(k)
 
                // Insert the merged box
                zone.insert(i, mergedBox)
 
                // Next iteration starts right after the newly inserted merged box
                i += 1
            else
                // No merge needed, move to next
                i += 1
    
    // Adjust left edges of final zones
    if zone.size()>0
        float epsilon = 1e-10
        for ii = 0 to zone.size() - 1
            currentBox = zone.get(ii)
            ztop = currentBox.get_top()
            zbot = currentBox.get_bottom()
            left_idx = b - prd
            for off = prd to 0 by 1
                if low[off] <= ztop + epsilon and high[off] >= zbot - epsilon or
                   high[off] >= zbot - epsilon and low[off] <= zbot + epsilon or
                   low[off] <= zbot + epsilon and high[off] >= ztop - epsilon
                    left_idx := b - off
                    break
            currentBox.set_left(left_idx)
 
    // Delta
    lvlCount = lvls.size()
    rowsUsed = lvlCount > 0 ? lvlCount - 1 : 0
 
    if rowsUsed > 0 and sum_sections > 0
        // rows per section
        secRows = math.max(1, math.floor(rowsUsed / sum_sections))
 
        baseLeft  = b + width + sum_gap_x
        baseRight = baseLeft + sum_panel_w
 
        for s = 0 to sum_sections - 1
            startIdx = s * secRows
            endIdx   = s == sum_sections - 1 ? rowsUsed - 1 : math.min(rowsUsed - 1, (s + 1) * secRows - 1)
 
            if startIdx > endIdx
                continue
 
            segBull = 0.0
            segBear = 0.0
 
            for j = startIdx to endIdx
                segBull += bull_vols.get(j)
                segBear += bear_vols.get(j)
 
            segTot = segBull + segBear
            if segTot <= 0
                continue
 
            // vertical range of this section
            segTop    = lvls.get(endIdx + 1)
            segBottom = lvls.get(startIdx)
 
            box.new(
                 left   = baseLeft,
                 top    = segTop,
                 right  = baseRight,
                 bottom = segBottom,
                 border_color = color.new(chart.fg_color, 70),
                 bgcolor = delta_neutral_bg
             )
 
            // Delta: (Bull - Bear) as % of total => [-100, +100]
            deltaPct = (segBull - segBear) / segTot * 100.0
 
            // bar length: abs(delta), with minimum visual size
            float barLenFrac = 0.0
            float barLen     = 0.0
 
            if deltaPct != 0
                norm       = math.abs(deltaPct) / 100.0
                barLenFrac := math.max(delta_min_frac, math.min(1.0, norm))
                barLen     := sum_panel_w * barLenFrac
            else
                barLenFrac := 0.0
                barLen     := 0.0
 
            // coordinates for the delta bar (always from the left)
            float dLeft  = baseLeft
            float dRight = baseLeft + barLen
 
            // choose color by sign
            colDelta = delta_neutral_bg
            if deltaPct > 0
                colDelta := delta_pos_color
            else if deltaPct < 0
                colDelta := delta_neg_color
 
            // if delta is exactly 0, still draw a very small neutral bar
            if deltaPct == 0
                dRight := baseLeft + 1
 
            // Delta bar (cast x-coordinates to int for box.new)
            deltaBox = box.new(
                 left   = int(math.round(dLeft)),
                 top    = segTop,
                 right  = int(math.round(dRight)),
                 bottom = segBottom,
                 border_color = color.new(chart.fg_color, 20),
                 bgcolor = colDelta
             )
 
            // Text inside the delta bar
            if sum_show_label
                txt = "Δ " + str.tostring(deltaPct, "#.0") + "%"
                box.set_text(deltaBox, txt)
                box.set_text_color(deltaBox, delta_text_color)
                box.set_text_halign(deltaBox, text.align_center)
                box.set_text_valign(deltaBox, text.align_center)
//~~}

← 返回列表