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