VWAP Supply & Demand Zones PRO

Finntech1 · study · 317 行 · 点赞 5,343 · TradingView 原页

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

Pine Script

//@version=5
indicator("VWAP Supply & Demand Zones PRO", overlay=true, max_lines_count=100, max_boxes_count=50)
 
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
//                                                        SETTINGS
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
 
vwapLength = input.int(20, "VWAP Length", minval=1, maxval=100, group="VWAP Settings")
zoneWidth = input.float(0.5, "Zone Width %", minval=0.1, maxval=3.0, step=0.1, group="Zone Settings")
maxZones = input.int(5, "Max Zones Per Type", minval=1, maxval=10, group="Zone Settings")
minTouches = input.int(2, "Min Touches to Validate", minval=1, maxval=5, group="Zone Settings")
zoneExtension = input.int(30, "Zone Extension (bars)", minval=10, maxval=100, group="Zone Settings")
 
showVWAP = input.bool(true, "Show VWAP", group="Visual Settings")
showZones = input.bool(true, "Show Supply/Demand Zones", group="Visual Settings")
showLabels = input.bool(true, "Show Zone Labels", group="Visual Settings")
showHUD = input.bool(true, "Show HUD", group="Visual Settings")
 
vwapColor = input.color(color.blue, "VWAP Color", group="Colors")
supplyColor = input.color(color.red, "Supply Zone Color", group="Colors")
demandColor = input.color(color.green, "Demand Zone Color", group="Colors")
neutralColor = input.color(color.gray, "Neutral Color", group="Colors")
 
alertOnNewZone = input.bool(true, "Alert on New Zone", group="Alerts")
alertOnZoneTest = input.bool(true, "Alert on Zone Test", group="Alerts")
alertOnZoneBreak = input.bool(true, "Alert on Zone Break", group="Alerts")
 
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
//                                                    DATA STORAGE
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
 
var array<box> supplyBoxes = array.new<box>()
var array<line> supplyLines = array.new<line>()
var array<label> supplyLabels = array.new<label>()
var array<float> supplyPrices = array.new<float>()
var array<float> supplyBottoms = array.new<float>()
var array<int> supplyTouches = array.new<int>()
var array<bool> supplyBroken = array.new<bool>()
 
var array<box> demandBoxes = array.new<box>()
var array<line> demandLines = array.new<line>()
var array<label> demandLabels = array.new<label>()
var array<float> demandPrices = array.new<float>()
var array<float> demandTops = array.new<float>()
var array<int> demandTouches = array.new<int>()
var array<bool> demandBroken = array.new<bool>()
 
var table hudTable = na
 
var int activeSupply = 0
var int activeDemand = 0
 
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
//                                                     FUNCTIONS
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
 
f_safeColor(baseColor, transparency) =>
    color.new(baseColor, transparency)
 
f_cleanupZones(boxes, lines, labels, prices1, prices2, touches, broken, maxCount) =>
    while array.size(boxes) > maxCount
        if array.size(boxes) > 0
            box oldBox = array.shift(boxes)
            box.delete(oldBox)
        if array.size(lines) > 0
            line oldLine = array.shift(lines)
            line.delete(oldLine)
        if array.size(labels) > 0
            label oldLabel = array.shift(labels)
            label.delete(oldLabel)
        if array.size(prices1) > 0
            array.shift(prices1)
        if array.size(prices2) > 0
            array.shift(prices2)
        if array.size(touches) > 0
            array.shift(touches)
        if array.size(broken) > 0
            array.shift(broken)
 
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
//                                                    CALCULATIONS
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
 
// Moving VWAP Calculation
vwapSource = hlc3
vwapNum = ta.sma(vwapSource * volume, vwapLength)
vwapDen = ta.sma(volume, vwapLength)
vwapValue = vwapDen != 0 ? vwapNum / vwapDen : na
 
// Condition Checks
hasEnoughHistory = bar_index > vwapLength * 2
isConfirmedBar = barstate.isconfirmed or barstate.islastconfirmedhistory
 
volMA = ta.sma(volume, 20)
atrValue = ta.atr(14)
 
vwapCrossUp = isConfirmedBar and ta.crossover(close, vwapValue)
vwapCrossDown = isConfirmedBar and ta.crossunder(close, vwapValue)
 
volumeConfirm = isConfirmedBar and volume > volMA * 1.2
momentumConfirm = isConfirmedBar and math.abs(close - open) > atrValue * 0.5
 
// Zone Creation Logic
createSupplyZone = hasEnoughHistory and vwapCrossDown and volumeConfirm and momentumConfirm
supplyZoneTop = createSupplyZone ? high * (1 + zoneWidth / 100) : na
supplyZoneBottom = createSupplyZone ? vwapValue : na
 
createDemandZone = hasEnoughHistory and vwapCrossUp and volumeConfirm and momentumConfirm
demandZoneTop = createDemandZone ? vwapValue : na
demandZoneBottom = createDemandZone ? low * (1 - zoneWidth / 100) : na
 
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
//                                                     ZONE MANAGEMENT
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
 
if createSupplyZone and showZones and not na(supplyZoneTop) and not na(supplyZoneBottom)
    f_cleanupZones(supplyBoxes, supplyLines, supplyLabels, supplyPrices, supplyBottoms, supplyTouches, supplyBroken, maxZones - 1)
 
    startBar = bar_index
    endBar = bar_index + zoneExtension
 
    box zoneBox = box.new(startBar, supplyZoneTop, endBar, supplyZoneBottom,
         border_color=supplyColor, bgcolor=f_safeColor(supplyColor, 90))
 
    line zoneLine = line.new(startBar, supplyZoneTop, endBar, supplyZoneTop,
         color=supplyColor, width=2, style=line.style_dotted)
 
    label zoneLabel = if showLabels
        label.new(startBar + 2, supplyZoneTop,
             "Supply\n" + str.tostring(supplyZoneTop, format.mintick),
             color=supplyColor, style=label.style_label_down, textcolor=color.white, size=size.small)
    else
        na
 
    array.push(supplyBoxes, zoneBox)
    array.push(supplyLines, zoneLine)
    array.push(supplyLabels, zoneLabel)
    array.push(supplyPrices, supplyZoneTop)
    array.push(supplyBottoms, supplyZoneBottom)
    array.push(supplyTouches, 0)
    array.push(supplyBroken, false)
 
    if alertOnNewZone
        alert("New Supply Zone Created at " + str.tostring(supplyZoneTop, format.mintick), alert.freq_once_per_bar)
 
if createDemandZone and showZones and not na(demandZoneTop) and not na(demandZoneBottom)
    f_cleanupZones(demandBoxes, demandLines, demandLabels, demandPrices, demandTops, demandTouches, demandBroken, maxZones - 1)
 
    startBar = bar_index
    endBar = bar_index + zoneExtension
 
    box zoneBox = box.new(startBar, demandZoneTop, endBar, demandZoneBottom,
         border_color=demandColor, bgcolor=f_safeColor(demandColor, 90))
 
    line zoneLine = line.new(startBar, demandZoneBottom, endBar, demandZoneBottom,
         color=demandColor, width=2, style=line.style_dotted)
 
    label zoneLabel = if showLabels
        label.new(startBar + 2, demandZoneBottom,
             "Demand\n" + str.tostring(demandZoneBottom, format.mintick),
             color=demandColor, style=label.style_label_up, textcolor=color.white, size=size.small)
    else
        na
 
    array.push(demandBoxes, zoneBox)
    array.push(demandLines, zoneLine)
    array.push(demandLabels, zoneLabel)
    array.push(demandPrices, demandZoneBottom)
    array.push(demandTops, demandZoneTop)
    array.push(demandTouches, 0)
    array.push(demandBroken, false)
 
    if alertOnNewZone
        alert("New Demand Zone Created at " + str.tostring(demandZoneBottom, format.mintick), alert.freq_once_per_bar)
 
// Update existing zones
activeSupply := 0
supplyCount = array.size(supplyBoxes)
if supplyCount > 0
    for i = 0 to supplyCount - 1
        isBroken = array.get(supplyBroken, i)
        if not isBroken
            zonePrice = array.get(supplyPrices, i) // Top of supply zone
            zoneBottom = array.get(supplyBottoms, i)
 
            // Extend zone lines and boxes
            box.set_right(array.get(supplyBoxes, i), bar_index + zoneExtension)
            line.set_x2(array.get(supplyLines, i), bar_index + zoneExtension)
 
            // Check for test
            if high >= zoneBottom and high <= zonePrice
                touches = array.get(supplyTouches, i) + 1
                array.set(supplyTouches, i, touches)
                if alertOnZoneTest and touches == minTouches
                    alert("Supply Zone Tested at " + str.tostring(zonePrice, format.mintick), alert.freq_once_per_bar_close)
 
            // Check for break
            if close > zonePrice
                array.set(supplyBroken, i, true)
                box zoneBox = array.get(supplyBoxes, i)
                box.set_bgcolor(zoneBox, f_safeColor(neutralColor, 95))
                if alertOnZoneBreak
                    alert("Supply Zone Broken at " + str.tostring(zonePrice, format.mintick), alert.freq_once_per_bar_close)
            else
                activeSupply += 1
 
activeDemand := 0
demandCount = array.size(demandBoxes)
if demandCount > 0
    for i = 0 to demandCount - 1
        isBroken = array.get(demandBroken, i)
        if not isBroken
            zonePrice = array.get(demandPrices, i) // Bottom of demand zone
            zoneTop = array.get(demandTops, i)
 
            // Extend zone lines and boxes
            box.set_right(array.get(demandBoxes, i), bar_index + zoneExtension)
            line.set_x2(array.get(demandLines, i), bar_index + zoneExtension)
 
            // Check for test
            if low <= zoneTop and low >= zonePrice
                touches = array.get(demandTouches, i) + 1
                array.set(demandTouches, i, touches)
                if alertOnZoneTest and touches == minTouches
                    alert("Demand Zone Tested at " + str.tostring(zonePrice, format.mintick), alert.freq_once_per_bar_close)
 
            // Check for break
            if close < zonePrice
                array.set(demandBroken, i, true)
                box zoneBox = array.get(demandBoxes, i)
                box.set_bgcolor(zoneBox, f_safeColor(neutralColor, 95))
                if alertOnZoneBreak
                    alert("Demand Zone Broken at " + str.tostring(zonePrice, format.mintick), alert.freq_once_per_bar_close)
            else
                activeDemand += 1
 
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
//                                                     PLOTTING & VISUALS
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
 
// Plot VWAP and bands
vwapPlot = plot(showVWAP ? vwapValue : na, "VWAP", color=vwapColor, linewidth=2)
upperBand = vwapValue * (1 + zoneWidth / 1000) // Thinner band for visualization
lowerBand = vwapValue * (1 - zoneWidth / 1000)
upperPlot = plot(showVWAP ? upperBand : na, "Upper Band", color=f_safeColor(vwapColor, 70), linewidth=1)
lowerPlot = plot(showVWAP ? lowerBand : na, "Lower Band", color=f_safeColor(vwapColor, 70), linewidth=1)
 
fill(upperPlot, vwapPlot, color=f_safeColor(supplyColor, 95), title="Upper Fill")
fill(vwapPlot, lowerPlot, color=f_safeColor(demandColor, 95), title="Lower Fill")
 
// In-Zone State for Alerts and BG
inSupplyZone = false
if supplyCount > 0
    for i = 0 to supplyCount - 1
        if not array.get(supplyBroken, i)
            zoneTop = array.get(supplyPrices, i)
            zoneBottom = array.get(supplyBottoms, i)
            if high >= zoneBottom and low <= zoneTop
                inSupplyZone := true
                break
 
inDemandZone = false
if demandCount > 0
    for i = 0 to demandCount - 1
        if not array.get(demandBroken, i)
            zoneBottom = array.get(demandPrices, i)
            zoneTop = array.get(demandTops, i)
            if high >= zoneBottom and low <= zoneTop
                inDemandZone := true
                break
 
bgcolor(inSupplyZone ? f_safeColor(supplyColor, 95) : inDemandZone ? f_safeColor(demandColor, 95) : na)
 
plotshape(createSupplyZone ? supplyZoneTop : na, title="Supply Zone Created", style=shape.triangledown,
     location=location.absolute, color=supplyColor, size=size.small)
plotshape(createDemandZone ? demandZoneBottom : na, title="Demand Zone Created", style=shape.triangleup,
     location=location.absolute, color=demandColor, size=size.small)
 
barcolor(showVWAP and not na(vwapValue) ? (close > vwapValue ? f_safeColor(demandColor, 70) : f_safeColor(supplyColor, 70)) : na)
 
// HUD
if barstate.isfirst and showHUD
    hudTable := table.new(position.bottom_right, 3, 6, bgcolor=color.new(color.black, 90), border_width=1)
 
if showHUD and barstate.islast and not na(hudTable)
    table.clear(hudTable, 0, 0, 2, 5)
    table.cell(hudTable, 0, 0, "VWAP S&D", text_color=color.white, bgcolor=color.new(color.blue, 50))
    table.merge_cells(hudTable, 0, 0, 2, 0)
 
    table.cell(hudTable, 0, 1, "Supply", text_color=color.white, text_size=size.tiny)
    table.cell(hudTable, 1, 1, str.tostring(activeSupply), text_color=supplyColor, text_size=size.tiny)
    table.cell(hudTable, 2, 1, activeSupply > 0 ? "▼" : "-", text_color=supplyColor, text_size=size.tiny)
 
    table.cell(hudTable, 0, 2, "Demand", text_color=color.white, text_size=size.tiny)
    table.cell(hudTable, 1, 2, str.tostring(activeDemand), text_color=demandColor, text_size=size.tiny)
    table.cell(hudTable, 2, 2, activeDemand > 0 ? "▲" : "-", text_color=demandColor, text_size=size.tiny)
 
    table.cell(hudTable, 0, 3, "VWAP", text_color=color.white, text_size=size.tiny)
    table.cell(hudTable, 1, 3, str.tostring(vwapValue, format.mintick), text_color=vwapColor, text_size=size.tiny)
    table.cell(hudTable, 2, 3, not na(vwapValue) ? (close > vwapValue ? "↑" : "↓") : "-",
         text_color=not na(vwapValue) ? (close > vwapValue ? demandColor : supplyColor) : neutralColor, text_size=size.tiny)
 
    marketBias = activeSupply > activeDemand ? "BEAR" : activeDemand > activeSupply ? "BULL" : "NEUTRAL"
    biasColor = marketBias == "BEAR" ? supplyColor : marketBias == "BULL" ? demandColor : neutralColor
 
    table.cell(hudTable, 0, 4, "Bias", text_color=color.white, text_size=size.tiny)
    table.cell(hudTable, 1, 4, marketBias, text_color=biasColor, text_size=size.tiny)
    table.cell(hudTable, 2, 4, marketBias == "BEAR" ? "🐻" : marketBias == "BULL" ? "🐂" : "➖", text_color=biasColor, text_size=size.tiny)
 
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
//                                                        ALERTS
// ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
 
alertcondition(inSupplyZone, title="In Supply Zone", message="Price entered supply zone - potential reversal")
alertcondition(inDemandZone, title="In Demand Zone", message="Price entered demand zone - potential reversal")
alertcondition(vwapCrossUp, title="VWAP Cross Up", message="Price crossed above VWAP")
alertcondition(vwapCrossDown, title="VWAP Cross Down", message="Price crossed below VWAP")

← 返回列表