Liquidity Grabs | Flux Charts

fluxchart · study · 171 行 · 点赞 9,259 · TradingView 原页

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

Pine Script

// 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()

← 返回列表