本页源码来自 TradingView 公开发布的开源脚本,版权归原作者所有, 请遵循其原始许可(Pine 脚本常见 CC BY-NC-SA / MPL-2.0 / MIT)。 本项目仅用于研究检索与许可范围内的移植。
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © fluxchart
//@version=5
const bool DEBUG = false
const int maxBoxesCount = 500
const int showLastXLiqs = 500
const int maxDistanceToLastBar = 5000 // Affects Running Time
indicator(title = 'Liquidity Grabs | Flux Charts', overlay = true, max_boxes_count = maxBoxesCount, max_labels_count = maxBoxesCount, max_lines_count = maxBoxesCount, max_bars_back = 5000)
pivotLenLiq = input.int(25, "Pivot Length", group = "General Configuration", display = display.none)
WBR = input.float(0.5, "Wick-Body Ratio", step = 0.1, group = "General Configuration", display = display.none)
sellsideColor = input(#08998180, 'Bullish', inline = 'bsColor', group = 'General Configuration', display = display.none)
buysideColor = input(#f2364680, 'Bearish', inline = 'bsColor', group = 'General Configuration', display = display.none)
showInvalidated = DEBUG ? input.bool(true, "Show Historic Zones", group = "General Configuration", display = display.none) : true
bubbleSize = DEBUG ? input.int(5, "[DBG] Base Bubble Size", group = "Style", display = display.none) : 5
minDist = DEBUG ? input.int(0, "[DBG] Min Distance", group = "Style", display = display.none) : 0
grabsPerZone = DEBUG ? input.int(1, "[DBG] Max Grabs Per Zone", group = "Style", display = display.none) : 1
minATRBetweenPivots = DEBUG ? input.float(1, "[DBG] Min ATR Between Pivots", group = "Style", display = display.none) : 1
bubbleOffset = DEBUG ? input.float(20, "[DBG] Bubble Offset", group = "Style", display = display.none) : 20
textColor = DEBUG ? input.color(#ffffff80, "[DBG] Text Color", group = "Style", inline = "BBcolors") : #ffffff80
renderLiquidityZones = DEBUG ? input.bool(false, "[DBG] Render Liquidity Zones", group = "Style") : false
type liqInfo
int startTime
int endTime
float price
string liqType
int grabsFound = 0
type liq
liqInfo info
bool isRendered = false
line liqZone
type bubble
int barIndex
float price
float bodySize
float wickSize
string liqType
safeDeleteLiq (liq liqF) =>
liqF.isRendered := false
line.delete(liqF.liqZone)
var liq[] allLiqList = array.new<liq>(0)
highestTALiq = ta.pivothigh(pivotLenLiq, pivotLenLiq)
lowestTALiq = ta.pivotlow(pivotLenLiq, pivotLenLiq)
curATR = ta.atr(5)
renderLiq (liq liqF) =>
if renderLiquidityZones
liqF.isRendered := true
liqF.liqZone := line.new(liqF.info.startTime, liqF.info.price, nz(liqF.info.endTime, time + 1), liqF.info.price, xloc = xloc.bar_time, color = liqF.info.liqType == "Buyside" ? buysideColor : sellsideColor)
handleLiqsFinal () =>
if DEBUG
log.info("Liqudities Count " + str.tostring(allLiqList.size()))
if allLiqList.size() > 0
for i = 0 to allLiqList.size() - 1
curLiq = allLiqList.get(i)
safeDeleteLiq(curLiq)
if not curLiq.isRendered and not (not showInvalidated and not na(curLiq.info.endTime))
renderLiq(curLiq)
if bar_index > last_bar_index - maxDistanceToLastBar
if allLiqList.size() > 0
for i = 0 to allLiqList.size() - 1
curLiq = allLiqList.get(i)
if na(curLiq.info.endTime) and (curLiq.info.liqType == "Buyside" ? close > curLiq.info.price : close < curLiq.info.price)
curLiq.info.endTime := time
if not na(highestTALiq)
newLiqInfo = liqInfo.new(time[pivotLenLiq], na, highestTALiq, "Buyside")
addLiq = true
if allLiqList.size() > 0
for i = 0 to allLiqList.size() - 1
liqInfo curInfo = allLiqList.get(i).info
if na(curInfo.endTime) and curInfo.liqType == "Buyside"
if math.abs(highestTALiq - curInfo.price) < curATR * minATRBetweenPivots
addLiq := false
break
if addLiq
allLiqList.unshift(liq.new(newLiqInfo))
if allLiqList.size () > showLastXLiqs
safeDeleteLiq(allLiqList.pop())
if not na(lowestTALiq)
newLiqInfo = liqInfo.new(time[pivotLenLiq], na, lowestTALiq, "Sellside")
addLiq = true
if allLiqList.size() > 0
for i = 0 to allLiqList.size() - 1
liqInfo curInfo = allLiqList.get(i).info
if na(curInfo.endTime) and curInfo.liqType == "Sellside"
if math.abs(lowestTALiq - curInfo.price) < curATR * minATRBetweenPivots
addLiq := false
break
if addLiq
allLiqList.unshift(liq.new(newLiqInfo))
if allLiqList.size () > showLastXLiqs
safeDeleteLiq(allLiqList.pop())
var lastHigh = 0
var lastLow = 0
renderHighCircle5 = false
renderHighCircle10 = false
renderHighCircle15 = false
float highCirclePrice = na
renderLowCircle5 = false
renderLowCircle10 = false
renderLowCircle15 = false
float lowCirclePrice = na
if allLiqList.size() > 0
for i = 0 to allLiqList.size() - 1
curLiq = allLiqList.get(i)
if not na(curLiq.info.endTime)
continue
if curLiq.info.grabsFound == grabsPerZone
continue
bodySize = math.abs(close - open)
wickSize = curLiq.info.liqType == "Buyside" ? (high - math.max(close, open)) : (math.min(close, open) - low)
curWBR = wickSize / bodySize
if curLiq.info.liqType == "Buyside"
if high >= curLiq.info.price and (bar_index - lastHigh > minDist)
lastHigh := bar_index
if curWBR >= WBR * 3
renderHighCircle15 := true
else if curWBR >= WBR * 2
renderHighCircle10 := true
else if curWBR >= WBR
renderHighCircle5 := true
highCirclePrice := high + curATR * bubbleOffset / 100.0
curLiq.info.grabsFound += 1
else
if low <= curLiq.info.price and (bar_index - lastLow > minDist)
lastLow := bar_index
if curWBR >= WBR * 3
renderLowCircle15 := true
else if curWBR >= WBR * 2
renderLowCircle10 := true
else if curWBR >= WBR
renderLowCircle5 := true
lowCirclePrice := low - curATR * bubbleOffset / 100.0
curLiq.info.grabsFound += 1
plot(renderHighCircle5 and barstate.isconfirmed ? highCirclePrice : na, "", buysideColor, bubbleSize, plot.style_circles, false, 0)
plot(renderHighCircle10 and barstate.isconfirmed ? highCirclePrice : na, "", buysideColor, int(bubbleSize * 1.5), plot.style_circles, false, 0)
plot(renderHighCircle15 and barstate.isconfirmed ? highCirclePrice : na, "", buysideColor, bubbleSize * 2, plot.style_circles, false, 0)
plot(renderLowCircle5 and barstate.isconfirmed ? lowCirclePrice : na, "", sellsideColor, bubbleSize, plot.style_circles, false, 0)
plot(renderLowCircle10 and barstate.isconfirmed ? lowCirclePrice : na, "", sellsideColor, int(bubbleSize * 1.5), plot.style_circles, false, 0)
plot(renderLowCircle15 and barstate.isconfirmed ? lowCirclePrice : na, "", sellsideColor, bubbleSize * 2, plot.style_circles, false, 0)
alertcondition((renderHighCircle5 or renderHighCircle10 or renderHighCircle15) and barstate.isconfirmed, "Buyside Liquidity Grab @ {{ticker}}", "Buyside Liquidity Grab @ {{ticker}}")
alertcondition((renderLowCircle5 or renderLowCircle10 or renderLowCircle15) and barstate.isconfirmed, "Sellside Liquidity Grab @ {{ticker}}", "Sellside Liquidity Grab @ {{ticker}}")
if barstate.islast
handleLiqsFinal()