Volume-Weighted Order Block Zones [BigBeluga]

BigBeluga · study · 348 行 · 点赞 6,111 · 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/
// © BigBeluga
 
//@version=6
indicator("Volume-Weighted Order Block Zones [BigBeluga]", overlay=true, max_boxes_count=500, max_lines_count=500, max_labels_count=500, calc_bars_count = 3000)
 
// INPUTS ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――{
 
// Order Block Detection Parameters
swLen          = input.int(10, title="Swing Detection Length", group="Order Block Settings", tooltip="Number of bars before and after a pivot high/low required to confirm a swing structure.")
maxOB          = input.int(10, title="Max Active Order Blocks", group="Order Block Settings", tooltip="Maximum number of active order block zones allowed on the chart simultaneously.")
displacement   = input.float(4.0, title="Displacement Multiplier", step=0.1, group="Order Block Settings", tooltip="ATR multiplier added to or subtracted from the pivot to establish the displacement trigger level.")
minVolStrength = input.int(0, title="Min Volume Strength (%)", minval=0, maxval=100, group="Order Block Settings", tooltip="Filters out pivot points and order blocks with volume strength lower than this percentage.")
 
// Label Size & Typography Settings
lblSize        = input.string("small", title="Label Size", options=["tiny", "small", "normal", "large"], group="Display Settings", tooltip="Controls the font size for PL, PH, B, and S labels.")
 
// Map label size selection to secondary sizes for retest signals
lblSize1 = switch lblSize
    "tiny"   => "small"
    "small"  => "normal"
    "normal" => "large"
    "large"  => "huge"
 
// Color Scheme Definitions
c_bullOb   = input.color(#4bffa5, title="Bullish/Bearish OB Colors", group="Color Settings", tooltip="", inline="ob1")
c_bearOb   = input.color(#ff784f, title="", group="Color Settings", tooltip="Base color used for Bullish Order Block background zones.\nBase color used for Bearish Order Block background zones.", inline="ob1")
c_bullLine = input.color(#4bffa5, title="Bullish/Bearish Trigger Line", group="Color Settings", tooltip="", inline="ob2")
c_bearLine = input.color(#ff784f, title="", group="Color Settings", tooltip="Color of the Bullish Displacement Trigger Line.\nColor of the Bearish Displacement Trigger Line.", inline="ob2")
c_plText   = input.color(#4bffa5, title="Pivot Low/High Text Color", group="Color Settings", tooltip="", inline="ob3")
c_phText   = input.color(#ff784f, title="", group="Color Settings", tooltip="Base color of the Pivot Low (PL) label text.\nBase color of the Pivot High (PH) label text.", inline="ob3")
 
// }
// CALCULATIONS――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――{
 
// Custom User Defined Type to store Order Block elements
type OB 
    box body 
    line triggerLEVEL
    bool isBullish
 
// Dynamic Array storing active Order Block instances
var OB[] obBoxes = array.new<OB>()
 
// Structure Pivot Detection
pHigh = ta.pivothigh(high, swLen, swLen)
pLow  = ta.pivotlow(low, swLen, swLen)
 
// Volume and Volatility Metrics
volMA = ta.sma(volume, 20)
atr   = ta.atr(100)
 
// Maximum high / minimum low lookbacks since pivot to detect retroactive breakouts
highestSincePivot = ta.highest(high, swLen)
lowestSincePivot  = ta.lowest(low, swLen)
 
// Bullish Order Block Tracking Variables
var float bullObHigh    = na
var float bullObLow     = na
var int   bullObBar     = na
var float bullObVol     = na
var float bullLevel     = na
var bool  bullTriggered = true
 
// Bearish Order Block Tracking Variables
var float bearObHigh    = na
var float bearObLow     = na
var int   bearObBar     = na
var float bearObVol     = na
var float bearLevel     = na
var bool  bearTriggered = true
 
// Cooldown tracking for retest signal labels
var lastBullRetest = 0
var lastBearRetest = 0
 
// Pending Un-triggered Breakout Trigger Lines
var line liveBullLine = na
var line liveBearLine = na
 
// Custom function calculating volume strength percentage and mapping dynamic alpha transparency
f_getVolumeColor(vol, sma, baseColor) =>
    float volRatio = sma > 0 ? (vol / sma) : 1.0
    int rawAlpha    = int(96 - (volRatio * 12))
    int alpha       = math.min(math.max(rawAlpha, 40), 92)
    int strength    = 100 - alpha
    [color.new(baseColor, alpha), strength]
 
// }
// PLOT ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――{
 
// 1. SWING LOW & BULLISH ORDER BLOCK LOGIC
if not na(pLow)
    float tempObVol = volume[swLen]
    [_, plStrength] = f_getVolumeColor(tempObVol, volMA[swLen], c_plText)
 
    if plStrength >= minVolStrength
        // Store Pivot Low metrics
        bullObHigh    := high[swLen]
        bullObLow     := low[swLen]
        bullObBar     := bar_index[swLen]
        bullObVol     := tempObVol
        bullLevel     := bullObLow + atr * displacement
        
        // Check if price already displaced above trigger level during confirmation lookback
        bool alreadyCrossed = highestSincePivot >= bullLevel
        bullTriggered := alreadyCrossed
 
        color plTextColor = color.new(c_plText, 80 - plStrength)
 
        // Clear previous live pending trigger line
        line.delete(liveBullLine)
        
        if not bullTriggered
            // Draw active dashed trigger line awaiting displacement
            liveBullLine := line.new(bullObBar, bullLevel, bar_index, bullLevel, style=line.style_dashed, color=c_bullLine)
        else
            // Retroactive OB creation if breakout occurred prior to pivot confirmation
            liveBullLine := na
            
            // Find exact historical bar offset where displacement trigger was crossed
            int crossOffset = 0
            for offset = swLen - 1 to 0
                if close[offset] >= bullLevel or high[offset] >= bullLevel
                    crossOffset := offset
                    break
            
            int crossBar = bar_index - crossOffset
            
            // Ensure zone was not invalidated/mitigated before confirmation
            bool isMitigated = false
            for offset = crossOffset to 0
                if low[offset] < bullObLow
                    isMitigated := true
                    break
            
            if not isMitigated
                [bullColor, bullStrength] = f_getVolumeColor(bullObVol, volMA[swLen], c_bullOb)
                
                // Remove overlapping active bullish OBs
                if array.size(obBoxes) > 0
                    for i = array.size(obBoxes) - 1 to 0 
                        OB currentOB = array.get(obBoxes, i)
                        if currentOB.isBullish
                            float oldHigh = box.get_top(currentOB.body)
                            float oldLow  = box.get_bottom(currentOB.body)
                            if bullObHigh >= oldLow and bullObLow <= oldHigh
                                box.delete(currentOB.body)
                                line.delete(currentOB.triggerLEVEL)
                                array.remove(obBoxes, i)
                
                // Plot displacement trigger line and OB zone box
                l = line.new(bullObBar, bullLevel, crossBar, bullLevel, style=line.style_solid, color=c_bullLine)
                b = box.new(bullObBar, bullObHigh, bar_index, bullObLow, bgcolor=bullColor, border_color=color.new(c_bullOb, 100), text=str.tostring(bullStrength) + "%", text_color=chart.fg_color, text_size=size.small, text_halign=text.align_center, text_valign=text.align_center)
                
                // Store new OB and enforce maximum allowed count
                obBoxes.push(OB.new(b, l, true))
                if array.size(obBoxes) > maxOB
                    oldOB = array.shift(obBoxes)
                    box.delete(oldOB.body)
                    line.delete(oldOB.triggerLEVEL)
 
        // Plot Pivot Low label with volume-weighted text intensity
        label.new(bar_index[swLen], pLow, "PL", color=color.new(color.black, 100), textcolor=plTextColor, style=label.style_label_up, size=lblSize, tooltip="Pivot Low: " + str.tostring(pLow, format.mintick) + "\nVolume Strength: " + str.tostring(plStrength) + "%")
 
 
// 2. SWING HIGH & BEARISH ORDER BLOCK LOGIC
if not na(pHigh)
    float tempObVol = volume[swLen]
    [_, phStrength] = f_getVolumeColor(tempObVol, volMA[swLen], c_phText)
 
    if phStrength >= minVolStrength
        // Store Pivot High metrics
        bearObHigh    := high[swLen]
        bearObLow     := low[swLen]
        bearObBar     := bar_index[swLen]
        bearObVol     := tempObVol
        bearLevel     := bearObHigh - atr * displacement
        
        // Check if price already displaced below trigger level during confirmation lookback
        bool alreadyCrossed = lowestSincePivot <= bearLevel
        bearTriggered := alreadyCrossed
 
        color phTextColor = color.new(c_phText, 80 - phStrength)
 
        // Clear previous live pending trigger line
        line.delete(liveBearLine)
 
        if not bearTriggered
            // Draw active dashed trigger line awaiting displacement
            liveBearLine := line.new(bearObBar, bearLevel, bar_index, bearLevel, style=line.style_dashed, color=c_bearLine)
        else
            // Retroactive OB creation if breakout occurred prior to pivot confirmation
            liveBearLine := na
 
            // Find exact historical bar offset where displacement trigger was crossed
            int crossOffset = 0
            for offset = swLen - 1 to 0
                if close[offset] <= bearLevel or low[offset] <= bearLevel
                    crossOffset := offset
                    break
 
            int crossBar = bar_index - crossOffset
 
            // Ensure zone was not invalidated/mitigated before confirmation
            bool isMitigated = false
            for offset = crossOffset to 0
                if high[offset] > bearObHigh
                    isMitigated := true
                    break
 
            if not isMitigated
                [bearColor, bearStrength] = f_getVolumeColor(bearObVol, volMA[swLen], c_bearOb)
 
                // Remove overlapping active bearish OBs
                if array.size(obBoxes) > 0
                    for i = array.size(obBoxes) - 1 to 0 
                        OB currentOB = array.get(obBoxes, i)
                        if not currentOB.isBullish
                            float oldHigh = box.get_top(currentOB.body)
                            float oldLow  = box.get_bottom(currentOB.body)
                            if bearObHigh >= oldLow and bearObLow <= oldHigh
                                box.delete(currentOB.body)
                                line.delete(currentOB.triggerLEVEL)
                                array.remove(obBoxes, i)
 
                // Plot displacement trigger line and OB zone box
                l = line.new(bearObBar, bearLevel, crossBar, bearLevel, style=line.style_solid, color=c_bearLine)
                b = box.new(bearObBar, bearObHigh, bar_index, bearObLow, bgcolor=bearColor, border_color=color.new(c_bearOb, 100), text=str.tostring(bearStrength) + "%", text_color=chart.fg_color, text_size=size.small, text_halign=text.align_center, text_valign=text.align_center)
 
                // Store new OB and enforce maximum allowed count
                obBoxes.push(OB.new(b, l, false))
                if array.size(obBoxes) > maxOB
                    oldOB = array.shift(obBoxes)
                    box.delete(oldOB.body)
                    line.delete(oldOB.triggerLEVEL)
 
        // Plot Pivot High label with volume-weighted text intensity
        label.new(bar_index[swLen], pHigh, "PH", color=color.new(color.black, 100), textcolor=phTextColor, style=label.style_label_down, size=lblSize, tooltip="Pivot High: " + str.tostring(pHigh, format.mintick) + "\nVolume Strength: " + str.tostring(phStrength) + "%")
 
 
// 3. DYNAMICALLY EXTEND PENDING TRIGGER LINES
if not bullTriggered and not na(liveBullLine)
    line.set_x2(liveBullLine, bar_index)
 
if not bearTriggered and not na(liveBearLine)
    line.set_x2(liveBearLine, bar_index)
 
 
// 4. ACTIVE ZONE MANAGEMENT, MITIGATION & RETEST SIGNALS
if array.size(obBoxes) > 0
    for i = array.size(obBoxes) - 1 to 0 
        OB currentOB = array.get(obBoxes, i)
        
        // Dynamically extend active OB box right boundary to current bar
        box.set_right(currentOB.body, bar_index)
        
        float obTop    = box.get_top(currentOB.body)
        float obBottom = box.get_bottom(currentOB.body)
        
        // Invalidation check (Price breaches opposite side of the Order Block)
        bool isInvalidated = false
        if currentOB.isBullish and low < obBottom
            isInvalidated := true
        else if not currentOB.isBullish and high > obTop
            isInvalidated := true
 
        // Delete invalidated zones
        if isInvalidated
            box.delete(currentOB.body)
            line.delete(currentOB.triggerLEVEL)
            array.remove(obBoxes, i)
        else
            // Check for Bullish OB retest/bounce
            if currentOB.isBullish
                bool bullCrossUp = (low[1] <= obTop and low > obTop) or (close[1] <= obTop and close > obTop)
                if bullCrossUp and barstate.isconfirmed and bar_index - lastBullRetest > 5
                    label.new(bar_index, low, "B", color=color.new(c_bullLine, 80), textcolor=chart.fg_color, style=label.style_label_up, size=lblSize1, tooltip="Bullish OB Retest")
                    lastBullRetest := bar_index
            else
                // Check for Bearish OB retest/rejection
                bool bearCrossDown = (high[1] >= obBottom and high < obBottom) or (close[1] >= obBottom and close < obBottom)
                if bearCrossDown and barstate.isconfirmed and bar_index - lastBearRetest > 5
                    label.new(bar_index, high, "S", color=color.new(c_bearLine, 80), textcolor=chart.fg_color, style=label.style_label_down, size=lblSize1, tooltip="Bearish OB Retest")
                    lastBearRetest := bar_index
 
 
// 5. REAL-TIME BREAKOUT TRIGGER DETECTION
// Real-time Bullish Displacement Breakout Trigger
if ta.crossover(close, bullLevel) and not na(bullObBar) and not bullTriggered and barstate.isconfirmed
    bullTriggered := true
    [bullColor, bullStrength] = f_getVolumeColor(bullObVol, volMA[bar_index - bullObBar], c_bullOb)
    
    line.delete(liveBullLine)
    liveBullLine := na
 
    // Clear overlapping bullish OBs
    if array.size(obBoxes) > 0
        for i = array.size(obBoxes) - 1 to 0 
            OB currentOB = array.get(obBoxes, i)
            if currentOB.isBullish
                float oldHigh = box.get_top(currentOB.body)
                float oldLow  = box.get_bottom(currentOB.body)
                if bullObHigh >= oldLow and bullObLow <= oldHigh
                    box.delete(currentOB.body)
                    line.delete(currentOB.triggerLEVEL)
                    array.remove(obBoxes, i)
 
    b = box.new(bullObBar, bullObHigh, bar_index, bullObLow, bgcolor=bullColor, border_color=color.new(c_bullOb, 100), text=str.tostring(bullStrength) + "%", text_color=chart.fg_color, text_size=size.small, text_halign=text.align_center, text_valign=text.align_center)
    l = line.new(bullObBar, bullLevel, bar_index, bullLevel, style=line.style_solid, color=c_bullLine)
 
    obBoxes.push(OB.new(b, l, true))
    if array.size(obBoxes) > maxOB
        oldOB = array.shift(obBoxes)
        box.delete(oldOB.body)
        line.delete(oldOB.triggerLEVEL)
 
// Real-time Bearish Displacement Breakout Trigger
if ta.crossunder(close, bearLevel) and not na(bearObBar) and not bearTriggered and barstate.isconfirmed
    bearTriggered := true
    [bearColor, bearStrength] = f_getVolumeColor(bearObVol, volMA[bar_index - bearObBar], c_bearOb)
    
    line.delete(liveBearLine)
    liveBearLine := na
 
    // Clear overlapping bearish OBs
    if array.size(obBoxes) > 0
        for i = array.size(obBoxes) - 1 to 0 
            OB currentOB = array.get(obBoxes, i)
            if not currentOB.isBullish
                float oldHigh = box.get_top(currentOB.body)
                float oldLow  = box.get_bottom(currentOB.body)
                if bearObHigh >= oldLow and bearObLow <= oldHigh
                    box.delete(currentOB.body)
                    line.delete(currentOB.triggerLEVEL)
                    array.remove(obBoxes, i)
 
    b = box.new(bearObBar, bearObHigh, bar_index, bearObLow, bgcolor=bearColor, border_color=color.new(c_bearOb, 100), text=str.tostring(bearStrength) + "%", text_color=chart.fg_color, text_size=size.small, text_halign=text.align_center, text_valign=text.align_center)
    l = line.new(bearObBar, bearLevel, bar_index, bearLevel, style=line.style_solid, color=c_bearLine)
 
    obBoxes.push(OB.new(b, l, false))
    if array.size(obBoxes) > maxOB
        oldOB = array.shift(obBoxes)
        box.delete(oldOB.body)
        line.delete(oldOB.triggerLEVEL)
 
// }

← 返回列表