Whale Liquidity and Absorption Profile [AlgoAlpha]

AlgoAlpha · study · 406 行 · 点赞 5,758 · 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/
// © AlgoAlpha
 
//@version=6
indicator("Whale Liquidity and Absorption Profile [AlgoAlpha]", "AlgoAlpha - 🐳 Liquidity & Absorption", overlay = true, max_boxes_count = 500, max_labels_count = 20, behind_chart = false)
 
lookback = input.int(200, "Profile Lookback", group = "Main Settings", tooltip = "Number of chart bars used to build the active profiles, absorption zones, and stored intrabar sample. Higher values make the profiles smoother and more historically anchored, but increase calculation load and make zones less reactive. Lower values make the profiles more responsive to recent activity.")
intragran = input.int(10, "Bar Granularity", minval = 1, maxval = 100, group = "Main Settings", tooltip = "Controls the lower-timeframe sampling depth. The script requests approximately the chart timeframe divided by this value. Higher values request smaller intrabars for more detail, but may increase runtime load and can require seconds data on very low chart timeframes. Lower values are lighter but less granular.")
allowSeconds = input.bool(false, "Allow Seconds", group = "Main Settings", tooltip = "Allows the script to request seconds-based lower timeframes when the calculated granularity requires them. Disable this for accounts or symbols where seconds data may cause runtime errors. When disabled, requests are clamped to one minute or the current seconds chart timeframe.")
 
strengthFilter = input.int(97, "Strength Filter", minval = 0, maxval = 100, group = "Strength Settings", tooltip = "Percentile threshold used to classify strong buying and selling by absolute intrabar volume. A value of 97 means only the top 3% of intrabar volume samples are considered strong. Higher values make strong bars rarer; lower values include more activity as strong.")
strongOnly = input.bool(false, "Strong Only", group = "Strength Settings", tooltip = "When enabled, the volume profile hides weak buy and weak sell segments and normalizes only strong buying and strong selling. When disabled, the profile displays all four segments: strong buy, weak buy, weak sell, and strong sell.")
 
profileBins = input.int(35, "Profile Resolution", minval = 2, maxval = 70, group = "Profile Settings", tooltip = "Number of vertical price bins used for the volume, delta, absorption, and heatmap calculations. Higher values create finer price detail but use more boxes and increase visual/object load. Lower values create broader, smoother zones.")
profileWidth = input.int(100, "Profile Width", minval = 1, maxval = 450, group = "Profile Settings", tooltip = "Maximum horizontal width of the volume and absorption profiles measured in bars. Higher values make the profiles longer and easier to compare. Lower values make the profiles more compact.")
profileOffset = input.int(120, "Profile Offset", minval = 0, maxval = 550, group = "Profile Settings", tooltip = "Horizontal offset, in bars, between the current bar and the right edge of the volume profile. Higher values push the live profile further into the future/right side of the chart. Lower values keep it closer to price.")
 
showVolumeProfile = input.bool(true, "Show Volume Profile", group = "Appearance", tooltip = "Toggles the stacked buy/sell volume profile. This controls the main horizontal profile made of strong buy, weak buy, weak sell, and strong sell segments.")
showDeltaProfile = input.bool(true, "Show Delta Profile", group = "Appearance", tooltip = "Toggles the delta heatmap next to the volume profile. Delta is calculated per price bin as total buy volume minus total sell volume, with cell text showing the signed delta value.")
showAbsorptionProfile = input.bool(true, "Show Absorption Profile", group = "Appearance", tooltip = "Toggles the absorption profile next to the delta heatmap. Absorption is calculated from bullish volume in upper wicks and bearish volume in lower wicks, then aggregated by price bin.")
showLevels = input.bool(true, "Show Levels", group = "Appearance", tooltip = "Toggles absorption-derived support/resistance zones. Zones are drawn when an absorption bin is higher than both neighboring absorption bins, highlighting local absorption peaks across the lookback range.")
showBubbles = input.bool(false, "Show Bubbles", group = "Appearance", tooltip = "Toggles strong buy/sell bubbles on chart bars. Bubbles are plotted at the price of the strongest qualifying intrabar volume and scale by percentile tier above the Strength Filter.")
showAbsorptionHeatmap = input.bool(false, "Show Absorption Heatmap", group = "Appearance", tooltip = "Toggles the historical absorption heatmap. Every 5 bars, the script creates a past-5-bar heatmap snapshot from the current absorption profile and leaves it on the chart until TradingView's box limit removes older boxes.")
green = input.color(#00ffbb, "Bullish Colour", group = "Appearance", tooltip = "Color used for bullish/positive elements, including buy volume, positive delta, and the middle of the absorption heatmap gradient.")
red = input.color(#ff1100, "Bearish Colour", group = "Appearance", tooltip = "Color used for bearish/negative elements, including sell volume and negative delta.")
 
absorptionHeatmapColor(norm) =>
    clippedNorm = math.max(0.0, math.min(1.0, norm))
    clippedNorm <= 0.5 ? color.from_gradient(clippedNorm, 0.0, 0.5, color.rgb(140, 0, 255), green) : color.from_gradient(clippedNorm, 0.5, 1.0, green, color.rgb(255, 230, 0))
 
formatDelta(deltaValue) =>
    sign = deltaValue >= 0 ? "+" : "-"
    "△" + sign + str.tostring(math.abs(deltaValue), format.volume)
 
chartTfSeconds = timeframe.in_seconds()
targetTfSeconds = math.max(1, int(chartTfSeconds * 1.0 / intragran))
useChartTf = not allowSeconds and timeframe.isseconds
tfSeconds = useChartTf ? chartTfSeconds : allowSeconds ? targetTfSeconds : math.max(60, targetTfSeconds)
tf = useChartTf ? timeframe.period : timeframe.from_seconds(tfSeconds)
rowLimit = lookback * math.max(1, int(math.ceil(chartTfSeconds / tfSeconds)))
 
dir = close > open ? 1 : -1
upperWickBase = math.max(open, close)
lowerWickBase = math.min(open, close)
hasUpperWick = high > upperWickBase
hasLowerWick = low < lowerWickBase
 
var matrix<float> data = matrix.new<float>(0, 4)
var array<box> strongBullBoxes = array.new<box>()
var array<box> weakBullBoxes = array.new<box>()
var array<box> weakBearBoxes = array.new<box>()
var array<box> strongBearBoxes = array.new<box>()
var array<box> deltaHeatmapBoxes = array.new<box>()
var array<box> absorptionBoxes = array.new<box>()
var array<box> absorptionZoneBoxes = array.new<box>()
var label volumeProfileLabel = na
var label deltaProfileLabel = na
var label absorptionProfileLabel = na
 
array<int> dir_ = array.new<int>()
array<float> vol_ = array.new<float>()
array<float> close_ = array.new<float>()
 
if tfSeconds <= chartTfSeconds
    [dirData, volData, closeData] = request.security_lower_tf(syminfo.tickerid, tf, [dir, volume, close])
    dir_ := dirData
    vol_ := volData
    close_ := closeData
 
if tfSeconds <= chartTfSeconds and barstate.isconfirmed and dir_.size() > 0
    for i = 0 to dir_.size() - 1
        rowDir = dir_.get(i)
        rowVol = vol_.get(i)
        rowClose = close_.get(i)
        absorptionVol = (rowDir > 0 and hasUpperWick and rowClose >= upperWickBase and rowClose <= high) or (rowDir < 0 and hasLowerWick and rowClose <= lowerWickBase and rowClose >= low) ? rowVol : 0.0
        row = array.from(float(rowDir), rowVol, rowClose, absorptionVol)
        matrix.add_row(data, matrix.rows(data), row)
 
while matrix.rows(data) > rowLimit
    matrix.remove_row(data, 0)
 
if showAbsorptionHeatmap and barstate.isconfirmed and bar_index % 5 == 0 and matrix.rows(data) > 0
    absorptionHeatmapRows = matrix.rows(data)
    absorptionHeatmapMinPrice = matrix.get(data, 0, 2)
    absorptionHeatmapMaxPrice = matrix.get(data, 0, 2)
 
    for r = 0 to absorptionHeatmapRows - 1
        absorptionHeatmapPrice = matrix.get(data, r, 2)
        absorptionHeatmapMinPrice := math.min(absorptionHeatmapMinPrice, absorptionHeatmapPrice)
        absorptionHeatmapMaxPrice := math.max(absorptionHeatmapMaxPrice, absorptionHeatmapPrice)
 
    absorptionHeatmapPriceRange = math.max(absorptionHeatmapMaxPrice - absorptionHeatmapMinPrice, syminfo.mintick)
    absorptionHeatmapStep = absorptionHeatmapPriceRange / profileBins
    absorptionHeatmapBins = array.new<float>(profileBins, 0.0)
 
    for r = 0 to absorptionHeatmapRows - 1
        absorptionHeatmapClose = matrix.get(data, r, 2)
        absorptionHeatmapValue = matrix.get(data, r, 3)
        absorptionHeatmapBinIndex = int(math.floor((absorptionHeatmapClose - absorptionHeatmapMinPrice) / absorptionHeatmapStep))
        absorptionHeatmapBinIndex := math.max(0, math.min(profileBins - 1, absorptionHeatmapBinIndex))
        if absorptionHeatmapValue > 0
            array.set(absorptionHeatmapBins, absorptionHeatmapBinIndex, array.get(absorptionHeatmapBins, absorptionHeatmapBinIndex) + absorptionHeatmapValue)
 
    absorptionHeatmapMaxValue = 0.0
    for i = 0 to profileBins - 1
        absorptionHeatmapMaxValue := math.max(absorptionHeatmapMaxValue, array.get(absorptionHeatmapBins, i))
 
    absorptionHeatmapLeft = bar_index - 5
    absorptionHeatmapRight = bar_index
 
    if absorptionHeatmapMaxValue > 0
        for i = 0 to profileBins - 1
            absorptionHeatmapBinValue = array.get(absorptionHeatmapBins, i)
            absorptionHeatmapBinBottom = absorptionHeatmapMinPrice + absorptionHeatmapStep * i
            absorptionHeatmapBinTop = absorptionHeatmapMinPrice + absorptionHeatmapStep * (i + 1)
            isRangeTouched = false
            for barOffset = 0 to 4
                if not na(high[barOffset]) and not na(low[barOffset])
                    isRangeTouched := isRangeTouched or (absorptionHeatmapBinBottom <= high[barOffset] and absorptionHeatmapBinTop >= low[barOffset])
            if absorptionHeatmapBinValue > 0 and not isRangeTouched
                absorptionHeatmapNorm = absorptionHeatmapBinValue / absorptionHeatmapMaxValue
                box.new(absorptionHeatmapLeft, absorptionHeatmapBinTop, absorptionHeatmapRight, absorptionHeatmapBinBottom, border_color = na, bgcolor = color.new(absorptionHeatmapColor(absorptionHeatmapNorm), 33))
 
float strengthThreshold = na
float bubbleThreshold2 = na
float bubbleThreshold3 = na
float bubbleThreshold4 = na
if matrix.rows(data) > 0
    volumesForPercentile = array.new<float>()
    for r = 0 to matrix.rows(data) - 1
        array.push(volumesForPercentile, matrix.get(data, r, 1))
    array.sort(volumesForPercentile, order.ascending)
    percentileSpan = 100.0 - strengthFilter
    bubblePercentile1 = float(strengthFilter)
    bubblePercentile2 = strengthFilter + percentileSpan * 0.25
    bubblePercentile3 = strengthFilter + percentileSpan * 0.50
    bubblePercentile4 = strengthFilter + percentileSpan * 0.75
    percentileIndex1 = math.min(array.size(volumesForPercentile) - 1, math.max(0, int(math.floor(array.size(volumesForPercentile) * bubblePercentile1 / 100.0))))
    percentileIndex2 = math.min(array.size(volumesForPercentile) - 1, math.max(0, int(math.floor(array.size(volumesForPercentile) * bubblePercentile2 / 100.0))))
    percentileIndex3 = math.min(array.size(volumesForPercentile) - 1, math.max(0, int(math.floor(array.size(volumesForPercentile) * bubblePercentile3 / 100.0))))
    percentileIndex4 = math.min(array.size(volumesForPercentile) - 1, math.max(0, int(math.floor(array.size(volumesForPercentile) * bubblePercentile4 / 100.0))))
    strengthThreshold := array.get(volumesForPercentile, percentileIndex1)
    bubbleThreshold2 := array.get(volumesForPercentile, percentileIndex2)
    bubbleThreshold3 := array.get(volumesForPercentile, percentileIndex3)
    bubbleThreshold4 := array.get(volumesForPercentile, percentileIndex4)
 
strongBuyingPrice1 = float(na)
strongBuyingPrice2 = float(na)
strongBuyingPrice3 = float(na)
strongBuyingPrice4 = float(na)
strongSellingPrice1 = float(na)
strongSellingPrice2 = float(na)
strongSellingPrice3 = float(na)
strongSellingPrice4 = float(na)
maxStrongBuyingVolume1 = 0.0
maxStrongBuyingVolume2 = 0.0
maxStrongBuyingVolume3 = 0.0
maxStrongBuyingVolume4 = 0.0
maxStrongSellingVolume1 = 0.0
maxStrongSellingVolume2 = 0.0
maxStrongSellingVolume3 = 0.0
maxStrongSellingVolume4 = 0.0
if not na(strengthThreshold) and dir_.size() > 0
    for i = 0 to dir_.size() - 1
        intrabarVol = vol_.get(i)
        intrabarPrice = close_.get(i)
        intrabarDir = dir_.get(i)
        isStrongIntrabar = intrabarVol >= strengthThreshold
        if isStrongIntrabar
            bubbleTier = intrabarVol >= bubbleThreshold4 ? 4 : intrabarVol >= bubbleThreshold3 ? 3 : intrabarVol >= bubbleThreshold2 ? 2 : 1
            if intrabarDir > 0
                if bubbleTier == 4 and intrabarVol > maxStrongBuyingVolume4
                    maxStrongBuyingVolume4 := intrabarVol
                    strongBuyingPrice4 := intrabarPrice
                else if bubbleTier == 3 and intrabarVol > maxStrongBuyingVolume3
                    maxStrongBuyingVolume3 := intrabarVol
                    strongBuyingPrice3 := intrabarPrice
                else if bubbleTier == 2 and intrabarVol > maxStrongBuyingVolume2
                    maxStrongBuyingVolume2 := intrabarVol
                    strongBuyingPrice2 := intrabarPrice
                else if bubbleTier == 1 and intrabarVol > maxStrongBuyingVolume1
                    maxStrongBuyingVolume1 := intrabarVol
                    strongBuyingPrice1 := intrabarPrice
            else
                if bubbleTier == 4 and intrabarVol > maxStrongSellingVolume4
                    maxStrongSellingVolume4 := intrabarVol
                    strongSellingPrice4 := intrabarPrice
                else if bubbleTier == 3 and intrabarVol > maxStrongSellingVolume3
                    maxStrongSellingVolume3 := intrabarVol
                    strongSellingPrice3 := intrabarPrice
                else if bubbleTier == 2 and intrabarVol > maxStrongSellingVolume2
                    maxStrongSellingVolume2 := intrabarVol
                    strongSellingPrice2 := intrabarPrice
                else if bubbleTier == 1 and intrabarVol > maxStrongSellingVolume1
                    maxStrongSellingVolume1 := intrabarVol
                    strongSellingPrice1 := intrabarPrice
 
if array.size(strongBullBoxes) == 0
    for i = 0 to profileBins - 1
        array.push(strongBullBoxes, box.new(bar_index, close, bar_index, close, border_color = na, bgcolor = na))
        array.push(weakBullBoxes, box.new(bar_index, close, bar_index, close, border_color = na, bgcolor = na))
        array.push(weakBearBoxes, box.new(bar_index, close, bar_index, close, border_color = na, bgcolor = na))
        array.push(strongBearBoxes, box.new(bar_index, close, bar_index, close, border_color = na, bgcolor = na))
        array.push(deltaHeatmapBoxes, box.new(bar_index, close, bar_index, close, border_color = na, bgcolor = na))
        array.push(absorptionBoxes, box.new(bar_index, close, bar_index, close, border_color = na, bgcolor = na))
        array.push(absorptionZoneBoxes, box.new(bar_index, close, bar_index, close, border_color = na, bgcolor = na))
 
if barstate.islast and matrix.rows(data) > 0 and array.size(strongBullBoxes) == profileBins and array.size(weakBullBoxes) == profileBins and array.size(weakBearBoxes) == profileBins and array.size(strongBearBoxes) == profileBins and array.size(deltaHeatmapBoxes) == profileBins and array.size(absorptionBoxes) == profileBins and array.size(absorptionZoneBoxes) == profileBins
    rows = matrix.rows(data)
    minPrice = matrix.get(data, 0, 2)
    maxPrice = matrix.get(data, 0, 2)
 
    for r = 0 to rows - 1
        price = matrix.get(data, r, 2)
        minPrice := math.min(minPrice, price)
        maxPrice := math.max(maxPrice, price)
 
    priceRange = math.max(maxPrice - minPrice, syminfo.mintick)
    step = priceRange / profileBins
    strongBullBins = array.new<float>(profileBins, 0.0)
    weakBullBins = array.new<float>(profileBins, 0.0)
    weakBearBins = array.new<float>(profileBins, 0.0)
    strongBearBins = array.new<float>(profileBins, 0.0)
    absorptionBins = array.new<float>(profileBins, 0.0)
 
    for r = 0 to rows - 1
        rowDir = matrix.get(data, r, 0)
        rowVol = matrix.get(data, r, 1)
        rowClose = matrix.get(data, r, 2)
        rowAbsorption = matrix.get(data, r, 3)
        isStrong = rowVol >= strengthThreshold
        binIndex = int(math.floor((rowClose - minPrice) / step))
        binIndex := math.max(0, math.min(profileBins - 1, binIndex))
 
        if rowDir > 0 and isStrong
            array.set(strongBullBins, binIndex, array.get(strongBullBins, binIndex) + rowVol)
        else if rowDir > 0
            array.set(weakBullBins, binIndex, array.get(weakBullBins, binIndex) + rowVol)
        else if isStrong
            array.set(strongBearBins, binIndex, array.get(strongBearBins, binIndex) + rowVol)
        else
            array.set(weakBearBins, binIndex, array.get(weakBearBins, binIndex) + rowVol)
 
        if rowAbsorption > 0
            array.set(absorptionBins, binIndex, array.get(absorptionBins, binIndex) + rowAbsorption)
 
    maxBinVolume = 0.0
    maxAbsDelta = 0.0
    maxAbsorptionVolume = 0.0
    for i = 0 to profileBins - 1
        strongBullVolumeScan = array.get(strongBullBins, i)
        weakBullVolumeScan = array.get(weakBullBins, i)
        weakBearVolumeScan = array.get(weakBearBins, i)
        strongBearVolumeScan = array.get(strongBearBins, i)
        binVolume = strongOnly ? strongBullVolumeScan + strongBearVolumeScan : strongBullVolumeScan + weakBullVolumeScan + weakBearVolumeScan + strongBearVolumeScan
        buyVolumeScan = strongBullVolumeScan + weakBullVolumeScan
        sellVolumeScan = strongBearVolumeScan + weakBearVolumeScan
        deltaScan = buyVolumeScan - sellVolumeScan
        maxBinVolume := math.max(maxBinVolume, binVolume)
        maxAbsDelta := math.max(maxAbsDelta, math.abs(deltaScan))
        maxAbsorptionVolume := math.max(maxAbsorptionVolume, array.get(absorptionBins, i))
 
    profileRight = bar_index + profileOffset
    deltaWidth = math.max(1, int(math.round(profileWidth / 10.0)))
    deltaLeft = profileRight + 1
    deltaRight = deltaLeft + deltaWidth
    absorptionProfileWidth = profileWidth
    absorptionLeft = deltaRight + 1
    volumeLabelX = int(math.round(profileRight - profileWidth * 0.5))
    deltaLabelX = int(math.round((deltaLeft + deltaRight) * 0.5))
    absorptionLabelX = int(math.round(absorptionLeft + absorptionProfileWidth * 0.5))
    labelY = maxPrice + priceRange * 0.05
 
    if na(volumeProfileLabel)
        volumeProfileLabel := label.new(volumeLabelX, labelY, showVolumeProfile ? "Volume Profile" : "", style = label.style_label_down, textcolor = showVolumeProfile ? chart.fg_color : color.new(chart.fg_color, 100), color = showVolumeProfile ? chart.bg_color : color.new(chart.bg_color, 100), size = size.small)
    else
        label.set_x(volumeProfileLabel, volumeLabelX)
        label.set_y(volumeProfileLabel, labelY)
        label.set_text(volumeProfileLabel, showVolumeProfile ? "Volume Profile" : "")
        label.set_textcolor(volumeProfileLabel, showVolumeProfile ? chart.fg_color : color.new(chart.fg_color, 100))
        label.set_color(volumeProfileLabel, showVolumeProfile ? chart.bg_color : color.new(chart.bg_color, 100))
 
    if na(deltaProfileLabel)
        deltaProfileLabel := label.new(deltaLabelX, labelY, showDeltaProfile ? "Delta" : "", style = label.style_label_down, textcolor = showDeltaProfile ? chart.fg_color : color.new(chart.fg_color, 100), color = showDeltaProfile ? chart.bg_color : color.new(chart.bg_color, 100), size = size.small)
    else
        label.set_x(deltaProfileLabel, deltaLabelX)
        label.set_y(deltaProfileLabel, labelY)
        label.set_text(deltaProfileLabel, showDeltaProfile ? "Delta" : "")
        label.set_textcolor(deltaProfileLabel, showDeltaProfile ? chart.fg_color : color.new(chart.fg_color, 100))
        label.set_color(deltaProfileLabel, showDeltaProfile ? chart.bg_color : color.new(chart.bg_color, 100))
 
    if na(absorptionProfileLabel)
        absorptionProfileLabel := label.new(absorptionLabelX, labelY, showAbsorptionProfile ? "Absorption Profile" : "", style = label.style_label_down, textcolor = showAbsorptionProfile ? chart.fg_color : color.new(chart.fg_color, 100), color = showAbsorptionProfile ? chart.bg_color : color.new(chart.bg_color, 100), size = size.small)
    else
        label.set_x(absorptionProfileLabel, absorptionLabelX)
        label.set_y(absorptionProfileLabel, labelY)
        label.set_text(absorptionProfileLabel, showAbsorptionProfile ? "Absorption Profile" : "")
        label.set_textcolor(absorptionProfileLabel, showAbsorptionProfile ? chart.fg_color : color.new(chart.fg_color, 100))
        label.set_color(absorptionProfileLabel, showAbsorptionProfile ? chart.bg_color : color.new(chart.bg_color, 100))
 
    for i = 0 to profileBins - 1
        binBottom = minPrice + step * i
        binTop = minPrice + step * (i + 1)
        strongBullVolume = array.get(strongBullBins, i)
        weakBullVolumeRaw = array.get(weakBullBins, i)
        weakBearVolumeRaw = array.get(weakBearBins, i)
        strongBearVolume = array.get(strongBearBins, i)
        weakBullVolume = strongOnly ? 0.0 : weakBullVolumeRaw
        weakBearVolume = strongOnly ? 0.0 : weakBearVolumeRaw
        absorptionVolume = array.get(absorptionBins, i)
        totalVolume = strongBullVolume + weakBullVolume + weakBearVolume + strongBearVolume
        totalLength = maxBinVolume > 0 ? int(math.round((totalVolume / maxBinVolume) * profileWidth)) : 0
        strongBullLength = totalVolume > 0 ? int(math.round(totalLength * strongBullVolume / totalVolume)) : 0
        weakBullLength = totalVolume > 0 ? int(math.round(totalLength * weakBullVolume / totalVolume)) : 0
        weakBearLength = totalVolume > 0 ? int(math.round(totalLength * weakBearVolume / totalVolume)) : 0
        strongBearLength = totalLength - strongBullLength - weakBullLength - weakBearLength
        buyVolume = strongBullVolume + weakBullVolumeRaw
        sellVolume = strongBearVolume + weakBearVolumeRaw
        delta = buyVolume - sellVolume
        deltaAbs = math.abs(delta)
        deltaTransparency = maxAbsDelta > 0 ? 90 - int(math.round((deltaAbs / maxAbsDelta) * 90.0)) : 90
        deltaColor = delta >= 0 ? green : red
        absorptionLength = maxAbsorptionVolume > 0 ? int(math.round((absorptionVolume / maxAbsorptionVolume) * absorptionProfileWidth)) : 0
        absorptionRight = absorptionLeft + absorptionLength
 
        strongBullRight = profileRight
        strongBullLeft = strongBullRight - strongBullLength
        weakBullRight = strongBullLeft
        weakBullLeft = weakBullRight - weakBullLength
        weakBearRight = weakBullLeft
        weakBearLeft = weakBearRight - weakBearLength
        strongBearRight = weakBearLeft
        strongBearLeft = strongBearRight - strongBearLength
 
        strongBullBox = array.get(strongBullBoxes, i)
        box.set_left(strongBullBox, strongBullLeft)
        box.set_right(strongBullBox, strongBullRight)
        box.set_top(strongBullBox, binTop)
        box.set_bottom(strongBullBox, binBottom)
        box.set_bgcolor(strongBullBox, showVolumeProfile and strongBullLength > 0 ? color.new(green, 40) : na)
        box.set_border_color(strongBullBox, showVolumeProfile and strongBullLength > 0 ? color.new(green, 100) : na)
 
        weakBullBox = array.get(weakBullBoxes, i)
        box.set_left(weakBullBox, weakBullLeft)
        box.set_right(weakBullBox, weakBullRight)
        box.set_top(weakBullBox, binTop)
        box.set_bottom(weakBullBox, binBottom)
        box.set_bgcolor(weakBullBox, showVolumeProfile and not strongOnly and weakBullLength > 0 ? color.new(green, 70) : na)
        box.set_border_color(weakBullBox, showVolumeProfile and not strongOnly and weakBullLength > 0 ? color.new(green, 100) : na)
 
        weakBearBox = array.get(weakBearBoxes, i)
        box.set_left(weakBearBox, weakBearLeft)
        box.set_right(weakBearBox, weakBearRight)
        box.set_top(weakBearBox, binTop)
        box.set_bottom(weakBearBox, binBottom)
        box.set_bgcolor(weakBearBox, showVolumeProfile and not strongOnly and weakBearLength > 0 ? color.new(red, 70) : na)
        box.set_border_color(weakBearBox, showVolumeProfile and not strongOnly and weakBearLength > 0 ? color.new(red, 100) : na)
 
        strongBearBox = array.get(strongBearBoxes, i)
        box.set_left(strongBearBox, strongBearLeft)
        box.set_right(strongBearBox, strongBearRight)
        box.set_top(strongBearBox, binTop)
        box.set_bottom(strongBearBox, binBottom)
        box.set_bgcolor(strongBearBox, showVolumeProfile and strongBearLength > 0 ? color.new(red, 40) : na)
        box.set_border_color(strongBearBox, showVolumeProfile and strongBearLength > 0 ? color.new(red, 100) : na)
 
        deltaHeatmapBox = array.get(deltaHeatmapBoxes, i)
        box.set_left(deltaHeatmapBox, deltaLeft)
        box.set_right(deltaHeatmapBox, deltaRight)
        box.set_top(deltaHeatmapBox, binTop)
        box.set_bottom(deltaHeatmapBox, binBottom)
        box.set_bgcolor(deltaHeatmapBox, showDeltaProfile and deltaAbs > 0 ? color.new(deltaColor, deltaTransparency) : na)
        box.set_border_color(deltaHeatmapBox, na)
        box.set_text(deltaHeatmapBox, showDeltaProfile and deltaAbs > 0 ? formatDelta(delta) : "")
        box.set_text_color(deltaHeatmapBox, showDeltaProfile and deltaAbs > 0 ? chart.fg_color : color.new(chart.fg_color, 100))
        box.set_text_size(deltaHeatmapBox, size.tiny)
        box.set_text_halign(deltaHeatmapBox, text.align_center)
        box.set_text_valign(deltaHeatmapBox, text.align_center)
 
        absorptionBox = array.get(absorptionBoxes, i)
        box.set_left(absorptionBox, absorptionLeft)
        box.set_right(absorptionBox, absorptionRight)
        box.set_top(absorptionBox, binTop)
        box.set_bottom(absorptionBox, binBottom)
        box.set_bgcolor(absorptionBox, showAbsorptionProfile and absorptionLength > 0 ? color.new(chart.fg_color, 55) : na)
        box.set_border_color(absorptionBox, showAbsorptionProfile and absorptionLength > 0 ? color.new(chart.fg_color, 100) : na)
 
        isAbsorptionPeak = i > 0 and i < profileBins - 1 and absorptionVolume > array.get(absorptionBins, i - 1) and absorptionVolume > array.get(absorptionBins, i + 1)
        absorptionZoneBox = array.get(absorptionZoneBoxes, i)
        box.set_left(absorptionZoneBox, bar_index - lookback)
        box.set_right(absorptionZoneBox, bar_index)
        box.set_top(absorptionZoneBox, binTop)
        box.set_bottom(absorptionZoneBox, binBottom)
        box.set_bgcolor(absorptionZoneBox, showLevels and isAbsorptionPeak ? color.new(chart.fg_color, 90) : na)
        box.set_border_color(absorptionZoneBox, showLevels and isAbsorptionPeak ? color.new(chart.fg_color, 100) : na)
 
bubblesTransp = 90
 
plotchar(showBubbles ? strongBuyingPrice1 : na, title = "Strong Buying Tier 1", char = "●", location = location.absolute, color = color.new(green, bubblesTransp), size = size.small)
plotchar(showBubbles ? strongBuyingPrice2 : na, title = "Strong Buying Tier 2", char = "●", location = location.absolute, color = color.new(green, bubblesTransp), size = size.normal)
plotchar(showBubbles ? strongBuyingPrice3 : na, title = "Strong Buying Tier 3", char = "●", location = location.absolute, color = color.new(green, bubblesTransp), size = size.large)
plotchar(showBubbles ? strongBuyingPrice4 : na, title = "Strong Buying Tier 4", char = "●", location = location.absolute, color = color.new(green, bubblesTransp), size = size.huge)
plotchar(showBubbles ? strongSellingPrice1 : na, title = "Strong Selling Tier 1", char = "●", location = location.absolute, color = color.new(red, bubblesTransp), size = size.small)
plotchar(showBubbles ? strongSellingPrice2 : na, title = "Strong Selling Tier 2", char = "●", location = location.absolute, color = color.new(red, bubblesTransp), size = size.normal)
plotchar(showBubbles ? strongSellingPrice3 : na, title = "Strong Selling Tier 3", char = "●", location = location.absolute, color = color.new(red, bubblesTransp), size = size.large)
plotchar(showBubbles ? strongSellingPrice4 : na, title = "Strong Selling Tier 4", char = "●", location = location.absolute, color = color.new(red, bubblesTransp), size = size.huge)

← 返回列表