Indicator · Pine Script v6 · On the chart

VP RSD Multi-Resolution Consensus Diagnostic

by 345-strategies· code credited to TheStratSuite contributors

This overlay diagnostic builds three total-volume profiles from a fixed 15-minute source over a user-defined swing range.

View on GitHub

GitHub stars5on the repository
Pine versionv6current
Lines of code2,104
Settings20inputs
LicenceMPL-2.0
Last update2 days ago26 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

2,104 lines of Pine Script v6 from pine/research/VP_RSD_Multi_Resolution_Consensus_Diagnostic_v0.4.0.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

VP_RSD_Multi_Resolution_Consensus_Diagnostic_v0.4.0.pine
// This source code is subject to the terms of the Mozilla Public License 2.0
// at https://mozilla.org/MPL/2.0/
// © 2026 TheStratSuite contributors
//@version=6

// RESEARCH DIAGNOSTIC ONLY.
//
// This variant keeps the v0.3 native-like construction but fixes its source
// stream at 15-minute bars and builds three independent total-volume profiles:
//
//   fine   = round(base row ticks * 0.8)
//   base   = base row ticks
//   coarse = round(base row ticks * 1.2)
//
// Each profile allocates every source bar's total volume by exact high/low tick
// overlap. The existing provisional local log-jump gates are then applied to
// each grid independently. A shelf becomes a consensus shelf only when gated
// candidates from at least two distinct grids cluster within the configured
// base-row radius.
//
// Optional structural bounds can suppress otherwise-valid consensus shelves
// that are too close to the RSD range low or high. They are suppression inputs,
// not additional evidence.
//
// Endpoint contract:
//   [Start 15m bar time, End 15m bar time + 15 minutes)
//
// This script does not create entries, exits, or support/resistance claims.
// It exists to test whether a price-distribution boundary survives reasonable
// row-size perturbation before any production display policy is considered.
indicator(
     "VP RSD Multi-Resolution Consensus Diagnostic v0.4.0",
     shorttitle = "VP RSD MRC 0.4",
     overlay = true,
     max_bars_back = 5000,
     max_lines_count = 500,
     max_boxes_count = 10,
     max_labels_count = 100)


// ============================================================================
// INPUTS
// ============================================================================

string rangeGroup = "1. Immutable RSD range (15m chart time)"
int profileStartInput = input.time(
     timestamp("23 Jun 2026 12:45 -0700"),
     "Start swing bar",
     group = rangeGroup)
int profileEndBarInput = input.time(
     timestamp("14 Jul 2026 06:30 -0700"),
     "End swing bar (included)",
     group = rangeGroup)

string constructionGroup = "2. Multi-resolution construction"
int baseTicksInput = input.int(
     10,
     "Base ticks per row",
     minval = 1,
     maxval = 500,
     group = constructionGroup,
     tooltip = "The other grids are round(base × 0.8) and round(base × 1.2). All three rounded sizes must be positive and distinct.")
float valueAreaPercentInput = input.float(
     70.0,
     "Base-grid Value Area target (%)",
     minval = 1.0,
     maxval = 100.0,
     step = 0.5,
     group = constructionGroup)

string scoringGroup = "3. Provisional local geometry gates"
int candidateCapPerGridInput = input.int(
     12,
     "Candidate cap per grid",
     minval = 1,
     maxval = 20,
     group = scoringGroup)
float minimumRatioInput = input.float(
     1.75,
     "Minimum dense/thin ratio",
     minval = 1.0,
     step = 0.05,
     group = scoringGroup)
float maximumNoiseFractionInput = input.float(
     0.50,
     "Maximum W / J",
     minval = 0.0,
     step = 0.05,
     group = scoringGroup)
float minimumDenseFractionInput = input.float(
     0.20,
     "Minimum dense side / grid max row",
     minval = 0.0,
     maxval = 1.0,
     step = 0.05,
     group = scoringGroup)
float minimumRobustZInput = input.float(
     2.50,
     "Minimum robust z",
     minval = 0.0,
     step = 0.25,
     group = scoringGroup)
int localClusterRowsInput = input.int(
     2,
     "Within-grid cluster radius (grid rows)",
     minval = 0,
     maxval = 10,
     group = scoringGroup)

string consensusGroup = "4. Cross-grid consensus"
float consensusClusterBaseRowsInput = input.float(
     1.0,
     "Cross-grid cluster radius (base rows)",
     minval = 0.25,
     maxval = 5.0,
     step = 0.25,
     group = consensusGroup)
int consensusCountInput = input.int(
     8,
     "Maximum consensus shelves",
     minval = 1,
     maxval = 12,
     group = consensusGroup)
bool extendConsensusInput = input.bool(
     true,
     "Extend consensus shelves to current bar",
     group = consensusGroup)

string structureGroup = "5. Optional structural-bound suppression"
bool useStructuralBoundsInput = input.bool(
     false,
     "Suppress near structural low / high",
     group = structureGroup)
float structuralLowInput = input.price(
     0.0,
     "Structural low",
     group = structureGroup)
float structuralHighInput = input.price(
     0.0,
     "Structural high",
     group = structureGroup)
float structuralDistanceBaseRowsInput = input.float(
     1.0,
     "Suppression distance (base rows)",
     minval = 0.0,
     maxval = 20.0,
     step = 0.25,
     group = structureGroup)

string displayGroup = "6. Diagnostic display"
float histogramWidthInput = input.float(
     24.0,
     "Base histogram width (% of range)",
     minval = 5.0,
     maxval = 50.0,
     group = displayGroup)
color rangeColorInput = input.color(
     #26c6da,
     "Range / base histogram",
     group = displayGroup)
color consensusColorInput = input.color(
     #ffd54f,
     "Consensus shelves",
     group = displayGroup)


// ============================================================================
// CONSTANTS AND HELPERS
// ============================================================================

const int MINUTE_MS = 60000
const int PROFILE_BAR_MS = 15 * MINUTE_MS
const int SOURCE_BAR_MS = 15 * MINUTE_MS
const string SOURCE_TIMEFRAME = "15"
const int GRID_ORIGIN_TICKS = 0
const int MAX_PROFILE_BINS = 400
const int SHELF_SIDE_ROWS = 3
const int MAX_RANKED_DIAGNOSTICS = 60
const int MIN_CONSENSUS_GRIDS = 2

int profileEndExclusive = profileEndBarInput + PROFILE_BAR_MS
int fineTicks = math.max(
     1,
     int(math.round(baseTicksInput * 0.8)))
int coarseTicks = math.max(
     1,
     int(math.round(baseTicksInput * 1.2)))
bool gridConfigurationValid =
     fineTicks > 0 and
     baseTicksInput > 0 and
     coarseTicks > 0 and
     fineTicks != baseTicksInput and
     fineTicks != coarseTicks and
     baseTicksInput != coarseTicks

priceToTicks(float price) =>
    na(price) ? na : int(math.round(price / syminfo.mintick))

binVolume(map<int, float> bins, int bin) =>
    bins.contains(bin) ? bins.get(bin) : 0.0

median3(float a, float b, float c) =>
    a + b + c -
     math.min(a, math.min(b, c)) -
     math.max(a, math.max(b, c))

mad3(float a, float b, float c, float center) =>
    median3(
         math.abs(a - center),
         math.abs(b - center),
         math.abs(c - center))

medianSorted(array<float> values) =>
    int count = values.size()
    float result = na
    if count > 0
        int middle = int(math.floor(count / 2.0))
        result := count % 2 == 1 ?
             values.get(middle) :
             math.avg(
                 values.get(middle - 1),
                 values.get(middle))
    result

timeText(int value) =>
    na(value) ? "NA" :
     str.format_time(
         value,
         "yyyy-MM-dd HH:mm",
         syminfo.timezone)

floatText(float value) =>
    na(value) ? "NA" :
     str.tostring(value, "#.########")

clearLines(array<line> drawings) =>
    int count = drawings.size()
    if count > 0
        for index = 0 to count - 1
            line.delete(drawings.get(index))
    drawings.clear()

clearLabels(array<label> drawings) =>
    int count = drawings.size()
    if count > 0
        for index = 0 to count - 1
            label.delete(drawings.get(index))
    drawings.clear()

diagnosticRow(
     table target,
     int row,
     string key,
     string value,
     color background) =>
    table.cell(
         target,
         0,
         row,
         key,
         text_color = color.silver,
         text_size = size.tiny,
         bgcolor = background,
         text_halign = text.align_left)
    table.cell(
         target,
         1,
         row,
         value,
         text_color = color.white,
         text_size = size.tiny,
         bgcolor = background,
         text_halign = text.align_left)

boundedPut(
     map<int, float> bins,
     int key,
     float addedVolume,
     int currentMin,
     int currentMax) =>
    bool alreadyPresent = bins.contains(key)
    int candidateMin = na(currentMin) ?
         key : math.min(currentMin, key)
    int candidateMax = na(currentMax) ?
         key : math.max(currentMax, key)
    bool overflow =
         candidateMax - candidateMin + 1 >
          MAX_PROFILE_BINS or
         (not alreadyPresent and
          bins.size() >= MAX_PROFILE_BINS)
    int nextMin = currentMin
    int nextMax = currentMax
    if not overflow
        bins.put(
             key,
             binVolume(bins, key) + addedVolume)
        nextMin := candidateMin
        nextMax := candidateMax
    [nextMin, nextMax, overflow]

betterConsensusCombination(
     int support,
     float minimumZ,
     int totalDistance,
     int incumbentSupport,
     float incumbentMinimumZ,
     int incumbentDistance) =>
    support > incumbentSupport or
     (
         support == incumbentSupport and
         minimumZ > incumbentMinimumZ) or
     (
         support == incumbentSupport and
         minimumZ == incumbentMinimumZ and
         totalDistance < incumbentDistance)

calculateProfile(
     map<int, float> bins,
     int minBin,
     int maxBin,
     float totalVolume,
     int rowTicks) =>
    bool valid =
         not na(minBin) and
         not na(maxBin) and
         maxBin >= minBin and
         maxBin - minBin + 1 <= MAX_PROFILE_BINS and
         bins.size() > 0 and
         totalVolume > 0.0
    int pocBin = na
    int valueAreaLowBin = na
    int valueAreaHighBin = na
    float achievedPercent = na
    float maximumVolume = na

    if valid
        float weightedCenter2 = 0.0
        for bin = minBin to maxBin
            float volumeAtBin = binVolume(bins, bin)
            int center2 =
                 2 * (
                     GRID_ORIGIN_TICKS +
                     bin * rowTicks) +
                 rowTicks
            weightedCenter2 +=
                 volumeAtBin * center2
            maximumVolume := na(maximumVolume) ?
                 volumeAtBin :
                 math.max(
                     maximumVolume,
                     volumeAtBin)

        float meanCenter2 =
             weightedCenter2 / totalVolume
        float bestDistance = na
        float tieTolerance = math.max(
             0.000000001,
             math.abs(maximumVolume) *
              0.000000000001)
        for bin = minBin to maxBin
            float volumeAtBin =
                 binVolume(bins, bin)
            if math.abs(
                 volumeAtBin -
                 maximumVolume) <= tieTolerance
                int center2 =
                     2 * (
                         GRID_ORIGIN_TICKS +
                         bin * rowTicks) +
                     rowTicks
                float distance =
                     math.abs(center2 - meanCenter2)
                if na(bestDistance) or
                 distance <
                  bestDistance - 0.000000001 or
                 (math.abs(
                      distance -
                      bestDistance) <=
                   0.000000001 and
                  bin < pocBin)
                    pocBin := bin
                    bestDistance := distance

        if not na(pocBin)
            valueAreaLowBin := pocBin
            valueAreaHighBin := pocBin
            float includedVolume =
                 binVolume(bins, pocBin)
            float targetVolume =
                 totalVolume *
                 valueAreaPercentInput /
                 100.0
            float tolerance = math.max(
                 0.000000001,
                 math.abs(totalVolume) *
                  0.000000000001)
            int steps = 0
            while includedVolume <
             targetVolume - tolerance and
             steps < MAX_PROFILE_BINS
                bool hasLower =
                     valueAreaLowBin > minBin
                bool hasUpper =
                     valueAreaHighBin < maxBin
                if not hasLower and not hasUpper
                    break
                int lowerCandidate =
                     valueAreaLowBin - 1
                int upperCandidate =
                     valueAreaHighBin + 1
                float lowerVolume = hasLower ?
                     binVolume(
                         bins,
                         lowerCandidate) : na
                float upperVolume = hasUpper ?
                     binVolume(
                         bins,
                         upperCandidate) : na
                bool chooseUpper = not hasLower
                if hasLower and hasUpper
                    chooseUpper :=
                         upperVolume >
                          lowerVolume or
                         (upperVolume ==
                           lowerVolume and
                          math.abs(
                              upperCandidate -
                              pocBin) <=
                           math.abs(
                               lowerCandidate -
                               pocBin))
                float selectedVolume =
                     chooseUpper ?
                     upperVolume : lowerVolume
                if includedVolume +
                 selectedVolume >
                 targetVolume + tolerance
                    break
                if chooseUpper
                    valueAreaHighBin :=
                         upperCandidate
                else
                    valueAreaLowBin :=
                         lowerCandidate
                includedVolume += selectedVolume
                steps += 1
            achievedPercent :=
                 includedVolume /
                 totalVolume *
                 100.0

    [valid and not na(pocBin),
     pocBin,
     valueAreaLowBin,
     valueAreaHighBin,
     achievedPercent,
     maximumVolume]

scoreGatedCandidates(
     map<int, float> bins,
     int minBin,
     int maxBin,
     int rowTicks,
     float maximumVolume,
     float minimumRatio,
     float maximumNoiseFraction,
     float minimumDenseFraction,
     float minimumRobustZ,
     int localClusterRows,
     int candidateCap) =>
    array<int> rawBins = array.new_int()
    array<float> rawJumps = array.new_float()
    array<float> rawNoise = array.new_float()
    array<float> rawClean = array.new_float()
    array<float> rawRatios = array.new_float()
    array<float> rawDenseVolumes =
         array.new_float()
    array<float> rawZ = array.new_float()
    array<float> positiveVolumes =
         array.new_float()
    array<int> selectedTicks = array.new_int()
    array<float> selectedClean =
         array.new_float()
    array<float> selectedZ =
         array.new_float()
    float cleanMad = na

    if not na(minBin) and
     not na(maxBin) and
     not na(maximumVolume) and
     maximumVolume > 0.0
        for bin = minBin to maxBin
            float volumeAtBin =
                 binVolume(bins, bin)
            if volumeAtBin > 0.0
                positiveVolumes.push(
                     volumeAtBin)
        positiveVolumes.sort(order.ascending)
        float logEpsilon =
             positiveVolumes.size() > 0 ?
             math.max(
                 medianSorted(
                     positiveVolumes) *
                  0.01,
                 0.000000001) : na

        int firstBoundary =
             minBin + SHELF_SIDE_ROWS
        int lastBoundary =
             maxBin - SHELF_SIDE_ROWS + 1
        if not na(logEpsilon) and
         firstBoundary <= lastBoundary
            for boundary =
             firstBoundary to lastBoundary
                float l0 = math.log(
                     binVolume(
                         bins,
                         boundary - 3) +
                     logEpsilon)
                float l1 = math.log(
                     binVolume(
                         bins,
                         boundary - 2) +
                     logEpsilon)
                float l2 = math.log(
                     binVolume(
                         bins,
                         boundary - 1) +
                     logEpsilon)
                float r0 = math.log(
                     binVolume(
                         bins,
                         boundary) +
                     logEpsilon)
                float r1 = math.log(
                     binVolume(
                         bins,
                         boundary + 1) +
                     logEpsilon)
                float r2 = math.log(
                     binVolume(
                         bins,
                         boundary + 2) +
                     logEpsilon)
                float leftMedian =
                     median3(l0, l1, l2)
                float rightMedian =
                     median3(r0, r1, r2)
                float jump = math.abs(
                     rightMedian -
                     leftMedian)
                float noise =
                     mad3(
                         l0,
                         l1,
                         l2,
                         leftMedian) +
                     mad3(
                         r0,
                         r1,
                         r2,
                         rightMedian)
                float cleanJump =
                     math.max(
                         0.0,
                         jump - noise)
                float denseVolume =
                     math.max(
                         0.0,
                         math.exp(
                             math.max(
                                 leftMedian,
                                 rightMedian)) -
                         logEpsilon)
                rawBins.push(boundary)
                rawJumps.push(jump)
                rawNoise.push(noise)
                rawClean.push(cleanJump)
                rawRatios.push(
                     math.exp(jump))
                rawDenseVolumes.push(
                     denseVolume)

        if rawClean.size() > 0
            array<float> cleanSorted =
                 rawClean.copy()
            cleanSorted.sort(order.ascending)
            float cleanMedian =
                 medianSorted(cleanSorted)
            array<float> deviations =
                 array.new_float()
            for index =
             0 to rawClean.size() - 1
                deviations.push(
                     math.abs(
                         rawClean.get(index) -
                         cleanMedian))
            deviations.sort(order.ascending)
            cleanMad :=
                 medianSorted(deviations)
            float robustScale =
                 math.max(
                     1.4826 * cleanMad,
                     0.000000001)
            for index =
             0 to rawClean.size() - 1
                rawZ.push(
                     (
                         rawClean.get(index) -
                         cleanMedian) /
                     robustScale)

    array<int> rankedIndices =
         array.new_int()
    array<bool> consumed =
         array.new_bool(
             rawClean.size(),
             false)
    int rankLimit = math.min(
         rawClean.size(),
         MAX_RANKED_DIAGNOSTICS)
    if rankLimit > 0
        for rank = 0 to rankLimit - 1
            int bestIndex = -1
            float bestClean = na
            int bestBoundary = na
            for index =
             0 to rawClean.size() - 1
                if not consumed.get(index)
                    float candidateClean =
                         rawClean.get(index)
                    int candidateBoundary =
                         rawBins.get(index)
                    if na(bestClean) or
                     candidateClean > bestClean or
                     (candidateClean ==
                       bestClean and
                      candidateBoundary <
                       bestBoundary)
                        bestIndex := index
                        bestClean :=
                             candidateClean
                        bestBoundary :=
                             candidateBoundary
            if bestIndex >= 0
                consumed.set(
                     bestIndex,
                     true)
                rankedIndices.push(
                     bestIndex)

    if rankedIndices.size() > 0
        for rankedPosition =
         0 to rankedIndices.size() - 1
            int index =
                 rankedIndices.get(
                     rankedPosition)
            bool passes =
                 rawRatios.get(index) >=
                  minimumRatio and
                 rawNoise.get(index) <=
                  maximumNoiseFraction *
                  rawJumps.get(index) and
                 rawDenseVolumes.get(index) >=
                  minimumDenseFraction *
                  maximumVolume and
                 rawZ.get(index) >=
                  minimumRobustZ
            int boundary =
                 rawBins.get(index)
            bool clustered = false
            if passes and
             selectedTicks.size() > 0
                for selectedIndex =
                 0 to selectedTicks.size() - 1
                    int selectedBoundary =
                         int(math.round(
                             selectedTicks.get(
                                 selectedIndex) /
                             float(rowTicks)))
                    if math.abs(
                         boundary -
                         selectedBoundary) <=
                         localClusterRows
                        clustered := true
                        break
            if passes and
             not clustered and
             selectedTicks.size() <
              candidateCap
                selectedTicks.push(
                     GRID_ORIGIN_TICKS +
                     boundary * rowTicks)
                selectedClean.push(
                     rawClean.get(index))
                selectedZ.push(
                     rawZ.get(index))

    [selectedTicks,
     selectedClean,
     selectedZ,
     rawClean.size(),
     cleanMad]


// ============================================================================
// FIXED 15-MINUTE SOURCE BAR REQUEST
// ============================================================================

sourcePayload() =>
    [time,
     priceToTicks(high),
     priceToTicks(low),
     volume]

[sourceTimes,
 sourceHighTicks,
 sourceLowTicks,
 sourceVolumes] = request.security_lower_tf(
     syminfo.tickerid,
     SOURCE_TIMEFRAME,
     sourcePayload(),
     ignore_invalid_timeframe = true)

bool chartSupported =
     chart.is_standard and
     timeframe.isminutes and
     timeframe.in_seconds() == 15 * 60
bool inputWindowValid =
     profileStartInput <
      profileEndBarInput and
     profileStartInput % MINUTE_MS == 0 and
     profileEndBarInput % MINUTE_MS == 0

var map<int, float> fineBins =
     map.new<int, float>()
var map<int, float> baseBins =
     map.new<int, float>()
var map<int, float> coarseBins =
     map.new<int, float>()

var int fineMinBin = na
var int fineMaxBin = na
var int baseMinBin = na
var int baseMaxBin = na
var int coarseMinBin = na
var int coarseMaxBin = na

var float sourceTotalVolume = 0.0
var float fineAllocatedVolume = 0.0
var float baseAllocatedVolume = 0.0
var float coarseAllocatedVolume = 0.0
var int includedSourceBars = 0
var int firstIncludedSourceTime = na
var int lastIncludedSourceTime = na
var int previousIncludedSourceTime = na
var int invalidSourceBars = 0
var bool sourceArrayIssue = false
var bool sourceOrderIssue = false
var bool fineOverflow = false
var bool baseOverflow = false
var bool coarseOverflow = false

if chartSupported and
 inputWindowValid and
 gridConfigurationValid and
 barstate.isconfirmed
    int timeCount = sourceTimes.size()
    int highCount = sourceHighTicks.size()
    int lowCount = sourceLowTicks.size()
    int volumeCount = sourceVolumes.size()
    bool lengthsMatch =
         timeCount == highCount and
         timeCount == lowCount and
         timeCount == volumeCount
    sourceArrayIssue :=
         sourceArrayIssue or
         not lengthsMatch
    int safeCount = math.min(
         math.min(
             timeCount,
             highCount),
         math.min(
             lowCount,
             volumeCount))

    if safeCount > 0
        for sourceIndex =
         0 to safeCount - 1
            int sourceTime =
                 sourceTimes.get(
                     sourceIndex)
            int sourceHigh =
                 sourceHighTicks.get(
                     sourceIndex)
            int sourceLow =
                 sourceLowTicks.get(
                     sourceIndex)
            float sourceVolume =
                 sourceVolumes.get(
                     sourceIndex)
            bool useForProfile =
                 not na(sourceTime) and
                 sourceTime >=
                  profileStartInput and
                 sourceTime <
                  profileEndExclusive

            if useForProfile
                firstIncludedSourceTime :=
                     includedSourceBars == 0 ?
                     sourceTime :
                     firstIncludedSourceTime
                sourceOrderIssue :=
                     sourceOrderIssue or
                     (
                         not na(
                             previousIncludedSourceTime) and
                         sourceTime <=
                          previousIncludedSourceTime)
                previousIncludedSourceTime :=
                     sourceTime
                lastIncludedSourceTime :=
                     sourceTime
                includedSourceBars += 1

                bool sourceValid =
                     not na(sourceHigh) and
                     not na(sourceLow) and
                     not na(sourceVolume) and
                     sourceVolume >= 0.0 and
                     sourceHigh >= sourceLow
                invalidSourceBars +=
                     sourceValid ? 0 : 1

                if sourceValid
                    sourceTotalVolume +=
                         sourceVolume
                    int sourceTickCount =
                         sourceHigh -
                         sourceLow +
                         1

                    int sourceFineLowBin =
                         int(math.floor(
                             (
                                 sourceLow -
                                 GRID_ORIGIN_TICKS) /
                             float(
                                 fineTicks)))
                    int sourceFineHighBin =
                         int(math.floor(
                             (
                                 sourceHigh -
                                 GRID_ORIGIN_TICKS) /
                             float(
                                 fineTicks)))
                    for outputBin =
                     sourceFineLowBin to
                     sourceFineHighBin
                        int binLowTick =
                             GRID_ORIGIN_TICKS +
                             outputBin *
                              fineTicks
                        int binHighTick =
                             binLowTick +
                             fineTicks -
                             1
                        int overlapLow =
                             math.max(
                                 sourceLow,
                                 binLowTick)
                        int overlapHigh =
                             math.min(
                                 sourceHigh,
                                 binHighTick)
                        int overlapTicks =
                             math.max(
                                 0,
                                 overlapHigh -
                                 overlapLow +
                                 1)
                        float allocatedVolume =
                             sourceVolume *
                             overlapTicks /
                             sourceTickCount
                        if overlapTicks > 0
                            [nextMin,
                             nextMax,
                             putOverflow] =
                                 boundedPut(
                                     fineBins,
                                     outputBin,
                                     allocatedVolume,
                                     fineMinBin,
                                     fineMaxBin)
                            fineMinBin := nextMin
                            fineMaxBin := nextMax
                            fineOverflow :=
                                 fineOverflow or
                                 putOverflow
                            fineAllocatedVolume +=
                                 allocatedVolume

                    int sourceBaseLowBin =
                         int(math.floor(
                             (
                                 sourceLow -
                                 GRID_ORIGIN_TICKS) /
                             float(
                                 baseTicksInput)))
                    int sourceBaseHighBin =
                         int(math.floor(
                             (
                                 sourceHigh -
                                 GRID_ORIGIN_TICKS) /
                             float(
                                 baseTicksInput)))
                    for outputBin =
                     sourceBaseLowBin to
                     sourceBaseHighBin
                        int binLowTick =
                             GRID_ORIGIN_TICKS +
                             outputBin *
                              baseTicksInput
                        int binHighTick =
                             binLowTick +
                             baseTicksInput -
                             1
                        int overlapLow =
                             math.max(
                                 sourceLow,
                                 binLowTick)
                        int overlapHigh =
                             math.min(
                                 sourceHigh,
                                 binHighTick)
                        int overlapTicks =
                             math.max(
                                 0,
                                 overlapHigh -
                                 overlapLow +
                                 1)
                        float allocatedVolume =
                             sourceVolume *
                             overlapTicks /
                             sourceTickCount
                        if overlapTicks > 0
                            [nextMin,
                             nextMax,
                             putOverflow] =
                                 boundedPut(
                                     baseBins,
                                     outputBin,
                                     allocatedVolume,
                                     baseMinBin,
                                     baseMaxBin)
                            baseMinBin := nextMin
                            baseMaxBin := nextMax
                            baseOverflow :=
                                 baseOverflow or
                                 putOverflow
                            baseAllocatedVolume +=
                                 allocatedVolume

                    int sourceCoarseLowBin =
                         int(math.floor(
                             (
                                 sourceLow -
                                 GRID_ORIGIN_TICKS) /
                             float(
                                 coarseTicks)))
                    int sourceCoarseHighBin =
                         int(math.floor(
                             (
                                 sourceHigh -
                                 GRID_ORIGIN_TICKS) /
                             float(
                                 coarseTicks)))
                    for outputBin =
                     sourceCoarseLowBin to
                     sourceCoarseHighBin
                        int binLowTick =
                             GRID_ORIGIN_TICKS +
                             outputBin *
                              coarseTicks
                        int binHighTick =
                             binLowTick +
                             coarseTicks -
                             1
                        int overlapLow =
                             math.max(
                                 sourceLow,
                                 binLowTick)
                        int overlapHigh =
                             math.min(
                                 sourceHigh,
                                 binHighTick)
                        int overlapTicks =
                             math.max(
                                 0,
                                 overlapHigh -
                                 overlapLow +
                                 1)
                        float allocatedVolume =
                             sourceVolume *
                             overlapTicks /
                             sourceTickCount
                        if overlapTicks > 0
                            [nextMin,
                             nextMax,
                             putOverflow] =
                                 boundedPut(
                                     coarseBins,
                                     outputBin,
                                     allocatedVolume,
                                     coarseMinBin,
                                     coarseMaxBin)
                            coarseMinBin := nextMin
                            coarseMaxBin := nextMax
                            coarseOverflow :=
                                 coarseOverflow or
                                 putOverflow
                            coarseAllocatedVolume +=
                                 allocatedVolume


// ============================================================================
// DRAWING STATE
// ============================================================================

var box rangeBox = na
var line pocLine = na
var line valueAreaHighLine = na
var line valueAreaLowLine = na
var array<line> histogramLines =
     array.new_line()
var array<line> consensusLines =
     array.new_line()
var array<label> consensusLabels =
     array.new_label()
var table diagnosticTable = table.new(
     position.top_right,
     2,
     24,
     frame_color =
      color.new(color.gray, 35),
     frame_width = 1,
     border_color =
      color.new(color.gray, 70),
     border_width = 1)


// ============================================================================
// LAST-BAR PROFILE, CONSENSUS, AND VISUALS
// ============================================================================

if barstate.islast
    if not na(rangeBox)
        box.delete(rangeBox)
        rangeBox := na
    if not na(pocLine)
        line.delete(pocLine)
        pocLine := na
    if not na(valueAreaHighLine)
        line.delete(valueAreaHighLine)
        valueAreaHighLine := na
    if not na(valueAreaLowLine)
        line.delete(valueAreaLowLine)
        valueAreaLowLine := na
    clearLines(histogramLines)
    clearLines(consensusLines)
    clearLabels(consensusLabels)

    float outlineHigh = na
    float outlineLow = na
    int scanBars =
         math.min(bar_index, 4999)
    for offset = 0 to scanBars
        if time[offset] >=
         profileStartInput and
         time[offset] <
         profileEndExclusive
            outlineHigh :=
                 na(outlineHigh) ?
                 high[offset] :
                 math.max(
                     outlineHigh,
                     high[offset])
            outlineLow :=
                 na(outlineLow) ?
                 low[offset] :
                 math.min(
                     outlineLow,
                     low[offset])
    if inputWindowValid and
     not na(outlineHigh)
        rangeBox := box.new(
             left = profileStartInput,
             top = outlineHigh,
             right = profileEndExclusive,
             bottom = outlineLow,
             xloc = xloc.bar_time,
             border_color = rangeColorInput,
             border_width = 2,
             border_style =
              line.style_dashed,
             bgcolor =
              color.new(rangeColorInput, 97))

    bool endpointsComplete =
         firstIncludedSourceTime ==
          profileStartInput and
         lastIncludedSourceTime +
          SOURCE_BAR_MS ==
          profileEndExclusive
    float reconciliationTolerance =
         math.max(
             0.000001,
             math.abs(sourceTotalVolume) *
              0.000000001)
    bool fineReconciliationValid =
         math.abs(
             fineAllocatedVolume -
             sourceTotalVolume) <=
         reconciliationTolerance
    bool baseReconciliationValid =
         math.abs(
             baseAllocatedVolume -
             sourceTotalVolume) <=
         reconciliationTolerance
    bool coarseReconciliationValid =
         math.abs(
             coarseAllocatedVolume -
             sourceTotalVolume) <=
         reconciliationTolerance
    bool constructionComplete =
         chartSupported and
         inputWindowValid and
         gridConfigurationValid and
         time >= profileEndExclusive and
         endpointsComplete and
         includedSourceBars > 0 and
         invalidSourceBars == 0 and
         not sourceArrayIssue and
         not sourceOrderIssue and
         not fineOverflow and
         not baseOverflow and
         not coarseOverflow and
         fineReconciliationValid and
         baseReconciliationValid and
         coarseReconciliationValid

    [fineAlgorithmValid,
     finePocBin,
     fineValueAreaLowBin,
     fineValueAreaHighBin,
     fineAchievedValueAreaPercent,
     fineMaximumBinVolume] =
         calculateProfile(
             fineBins,
             fineMinBin,
             fineMaxBin,
             fineAllocatedVolume,
             fineTicks)
    [baseAlgorithmValid,
     basePocBin,
     baseValueAreaLowBin,
     baseValueAreaHighBin,
     baseAchievedValueAreaPercent,
     baseMaximumBinVolume] =
         calculateProfile(
             baseBins,
             baseMinBin,
             baseMaxBin,
             baseAllocatedVolume,
             baseTicksInput)
    [coarseAlgorithmValid,
     coarsePocBin,
     coarseValueAreaLowBin,
     coarseValueAreaHighBin,
     coarseAchievedValueAreaPercent,
     coarseMaximumBinVolume] =
         calculateProfile(
             coarseBins,
             coarseMinBin,
             coarseMaxBin,
             coarseAllocatedVolume,
             coarseTicks)
    bool multiProfileValid =
         constructionComplete and
         fineAlgorithmValid and
         baseAlgorithmValid and
         coarseAlgorithmValid

    float basePocPrice =
         multiProfileValid ?
         (
             GRID_ORIGIN_TICKS +
             basePocBin *
              baseTicksInput +
             baseTicksInput *
              0.5) *
          syminfo.mintick : na
    float baseValueAreaLowPrice =
         multiProfileValid ?
         (
             GRID_ORIGIN_TICKS +
             baseValueAreaLowBin *
              baseTicksInput) *
          syminfo.mintick : na
    float baseValueAreaHighPrice =
         multiProfileValid ?
         (
             GRID_ORIGIN_TICKS +
             (
                 baseValueAreaHighBin +
                 1) *
              baseTicksInput) *
          syminfo.mintick : na

    array<int> fineCandidateTicks =
         array.new_int()
    array<float> fineCandidateClean =
         array.new_float()
    array<float> fineCandidateZ =
         array.new_float()
    int fineRawCount = 0
    float fineCleanMad = na
    array<int> baseCandidateTicks =
         array.new_int()
    array<float> baseCandidateClean =
         array.new_float()
    array<float> baseCandidateZ =
         array.new_float()
    int baseRawCount = 0
    float baseCleanMad = na
    array<int> coarseCandidateTicks =
         array.new_int()
    array<float> coarseCandidateClean =
         array.new_float()
    array<float> coarseCandidateZ =
         array.new_float()
    int coarseRawCount = 0
    float coarseCleanMad = na

    if multiProfileValid
        [fineSelectedTicks,
         fineSelectedClean,
         fineSelectedZ,
         fineRaw,
         fineMad] =
             scoreGatedCandidates(
                 fineBins,
                 fineMinBin,
                 fineMaxBin,
                 fineTicks,
                 fineMaximumBinVolume,
                 minimumRatioInput,
                 maximumNoiseFractionInput,
                 minimumDenseFractionInput,
                 minimumRobustZInput,
                 localClusterRowsInput,
                 candidateCapPerGridInput)
        fineCandidateTicks :=
             fineSelectedTicks
        fineCandidateClean :=
             fineSelectedClean
        fineCandidateZ :=
             fineSelectedZ
        fineRawCount := fineRaw
        fineCleanMad := fineMad

        [baseSelectedTicks,
         baseSelectedClean,
         baseSelectedZ,
         baseRaw,
         baseMad] =
             scoreGatedCandidates(
                 baseBins,
                 baseMinBin,
                 baseMaxBin,
                 baseTicksInput,
                 baseMaximumBinVolume,
                 minimumRatioInput,
                 maximumNoiseFractionInput,
                 minimumDenseFractionInput,
                 minimumRobustZInput,
                 localClusterRowsInput,
                 candidateCapPerGridInput)
        baseCandidateTicks :=
             baseSelectedTicks
        baseCandidateClean :=
             baseSelectedClean
        baseCandidateZ :=
             baseSelectedZ
        baseRawCount := baseRaw
        baseCleanMad := baseMad

        [coarseSelectedTicks,
         coarseSelectedClean,
         coarseSelectedZ,
         coarseRaw,
         coarseMad] =
             scoreGatedCandidates(
                 coarseBins,
                 coarseMinBin,
                 coarseMaxBin,
                 coarseTicks,
                 coarseMaximumBinVolume,
                 minimumRatioInput,
                 maximumNoiseFractionInput,
                 minimumDenseFractionInput,
                 minimumRobustZInput,
                 localClusterRowsInput,
                 candidateCapPerGridInput)
        coarseCandidateTicks :=
             coarseSelectedTicks
        coarseCandidateClean :=
             coarseSelectedClean
        coarseCandidateZ :=
             coarseSelectedZ
        coarseRawCount := coarseRaw
        coarseCleanMad := coarseMad

    // Every consensus cluster is seeded by a passing base-grid candidate.
    // Neighbors are accepted only when every pair in the cluster is within Q.
    // This is complete-linkage: a fine candidate cannot chain through the base
    // to a coarse candidate that is more than Q ticks away from it.
    array<int> clusterBaseTicks =
         array.new_int()
    array<int> clusterFineTicks =
         array.new_int()
    array<int> clusterCoarseTicks =
         array.new_int()
    array<int> clusterSupportCounts =
         array.new_int()
    array<float> clusterMinimumZ =
         array.new_float()
    float consensusRadiusTicks =
         consensusClusterBaseRowsInput *
         baseTicksInput

    if baseCandidateTicks.size() > 0
        for baseIndex =
         0 to baseCandidateTicks.size() - 1
            int baseTick =
                 baseCandidateTicks.get(
                     baseIndex)
            float baseZ =
                 baseCandidateZ.get(
                     baseIndex)
            int bestFineIndex = -1
            int bestCoarseIndex = -1
            int bestSupport = 1
            float bestMinimumZ = baseZ
            int bestTotalDistance = 0

            if fineCandidateTicks.size() > 0
                for fineIndex =
                 0 to fineCandidateTicks.size() - 1
                    int fineTick =
                         fineCandidateTicks.get(
                             fineIndex)
                    int fineDistance =
                         math.abs(
                             fineTick -
                             baseTick)
                    if fineDistance <=
                     consensusRadiusTicks
                        float combinationMinimumZ =
                             math.min(
                                 baseZ,
                                 fineCandidateZ.get(
                                     fineIndex))
                        if betterConsensusCombination(
                             2,
                             combinationMinimumZ,
                             fineDistance,
                             bestSupport,
                             bestMinimumZ,
                             bestTotalDistance)
                            bestFineIndex :=
                                 fineIndex
                            bestCoarseIndex := -1
                            bestSupport := 2
                            bestMinimumZ :=
                                 combinationMinimumZ
                            bestTotalDistance :=
                                 fineDistance

            if coarseCandidateTicks.size() > 0
                for coarseIndex =
                 0 to coarseCandidateTicks.size() - 1
                    int coarseTick =
                         coarseCandidateTicks.get(
                             coarseIndex)
                    int coarseDistance =
                         math.abs(
                             coarseTick -
                             baseTick)
                    if coarseDistance <=
                     consensusRadiusTicks
                        float combinationMinimumZ =
                             math.min(
                                 baseZ,
                                 coarseCandidateZ.get(
                                     coarseIndex))
                        if betterConsensusCombination(
                             2,
                             combinationMinimumZ,
                             coarseDistance,
                             bestSupport,
                             bestMinimumZ,
                             bestTotalDistance)
                            bestFineIndex := -1
                            bestCoarseIndex :=
                                 coarseIndex
                            bestSupport := 2
                            bestMinimumZ :=
                                 combinationMinimumZ
                            bestTotalDistance :=
                                 coarseDistance

            if fineCandidateTicks.size() > 0 and
             coarseCandidateTicks.size() > 0
                for fineIndex =
                 0 to fineCandidateTicks.size() - 1
                    int fineTick =
                         fineCandidateTicks.get(
                             fineIndex)
                    int fineDistance =
                         math.abs(
                             fineTick -
                             baseTick)
                    if fineDistance <=
                     consensusRadiusTicks
                        for coarseIndex =
                         0 to coarseCandidateTicks.size() - 1
                            int coarseTick =
                                 coarseCandidateTicks.get(
                                     coarseIndex)
                            int coarseDistance =
                                 math.abs(
                                     coarseTick -
                                     baseTick)
                            int neighborDistance =
                                 math.abs(
                                     fineTick -
                                     coarseTick)
                            bool completeLink =
                                 coarseDistance <=
                                  consensusRadiusTicks and
                                 neighborDistance <=
                                  consensusRadiusTicks
                            if completeLink
                                float combinationMinimumZ =
                                     math.min(
                                         baseZ,
                                         math.min(
                                             fineCandidateZ.get(
                                                 fineIndex),
                                             coarseCandidateZ.get(
                                                 coarseIndex)))
                                int totalDistance =
                                     fineDistance +
                                     coarseDistance +
                                     neighborDistance
                                if betterConsensusCombination(
                                     3,
                                     combinationMinimumZ,
                                     totalDistance,
                                     bestSupport,
                                     bestMinimumZ,
                                     bestTotalDistance)
                                    bestFineIndex :=
                                         fineIndex
                                    bestCoarseIndex :=
                                         coarseIndex
                                    bestSupport := 3
                                    bestMinimumZ :=
                                         combinationMinimumZ
                                    bestTotalDistance :=
                                         totalDistance

            clusterBaseTicks.push(baseTick)
            clusterFineTicks.push(
                 bestFineIndex >= 0 ?
                 fineCandidateTicks.get(
                     bestFineIndex) : na)
            clusterCoarseTicks.push(
                 bestCoarseIndex >= 0 ?
                 coarseCandidateTicks.get(
                     bestCoarseIndex) : na)
            clusterSupportCounts.push(
                 bestSupport)
            clusterMinimumZ.push(
                 bestMinimumZ)

    bool structuralBoundsValid =
         not useStructuralBoundsInput or
         structuralHighInput >
          structuralLowInput
    int structuralLowTicks =
         priceToTicks(structuralLowInput)
    int structuralHighTicks =
         priceToTicks(structuralHighInput)
    float structuralDistanceTicks =
         structuralDistanceBaseRowsInput *
         baseTicksInput

    array<int> eligibleClusterIndices =
         array.new_int()
    array<bool> clusterDrawingSuppressed =
         array.new_bool(
             clusterBaseTicks.size(),
             false)
    int structurallySuppressedCount = 0
    if multiProfileValid and
     structuralBoundsValid and
     clusterBaseTicks.size() > 0
        for clusterIndex =
         0 to clusterBaseTicks.size() - 1
            int supportCount =
                 clusterSupportCounts.get(
                     clusterIndex)
            int consensusTick =
                 clusterBaseTicks.get(
                     clusterIndex)
            bool nearStructuralBound =
                 useStructuralBoundsInput and
                 (
                     math.abs(
                         consensusTick -
                         structuralLowTicks) <=
                      structuralDistanceTicks or
                     math.abs(
                         consensusTick -
                         structuralHighTicks) <=
                      structuralDistanceTicks)
            if supportCount >=
             MIN_CONSENSUS_GRIDS
                if nearStructuralBound
                    structurallySuppressedCount +=
                         1
                clusterDrawingSuppressed.set(
                     clusterIndex,
                     nearStructuralBound)
                eligibleClusterIndices.push(
                     clusterIndex)

    array<int> selectedClusterIndices =
         array.new_int()
    array<bool> eligibleConsumed =
         array.new_bool(
             eligibleClusterIndices.size(),
             false)
    int consensusLimit = math.min(
         consensusCountInput,
         eligibleClusterIndices.size())
    if consensusLimit > 0
        for rank = 0 to consensusLimit - 1
            int bestEligibleIndex = -1
            int bestSupport = -1
            float bestMinimumZ = na
            int bestBaseTick = na
            for eligibleIndex =
             0 to eligibleClusterIndices.size() - 1
                if not eligibleConsumed.get(
                 eligibleIndex)
                    int clusterIndex =
                         eligibleClusterIndices.get(
                             eligibleIndex)
                    int supportCount =
                         clusterSupportCounts.get(
                             clusterIndex)
                    float minimumZ =
                         clusterMinimumZ.get(
                             clusterIndex)
                    int baseTick =
                         clusterBaseTicks.get(
                             clusterIndex)
                    if supportCount >
                     bestSupport or
                     (
                         supportCount ==
                          bestSupport and
                         (
                             na(bestMinimumZ) or
                             minimumZ >
                              bestMinimumZ)) or
                     (
                         supportCount ==
                          bestSupport and
                         minimumZ ==
                          bestMinimumZ and
                         (
                             na(bestBaseTick) or
                             baseTick <
                              bestBaseTick))
                        bestEligibleIndex :=
                             eligibleIndex
                        bestSupport :=
                             supportCount
                        bestMinimumZ :=
                             minimumZ
                        bestBaseTick :=
                             baseTick
            if bestEligibleIndex >= 0
                eligibleConsumed.set(
                     bestEligibleIndex,
                     true)
                selectedClusterIndices.push(
                     eligibleClusterIndices.get(
                         bestEligibleIndex))

    if multiProfileValid
        int histogramWidthMs = int(
             (
                 profileEndExclusive -
                 profileStartInput) *
             histogramWidthInput /
             100.0)
        for bin =
         baseMinBin to baseMaxBin
            float volumeAtBin =
                 binVolume(baseBins, bin)
            float relativeWidth =
                 baseMaximumBinVolume > 0.0 ?
                 volumeAtBin /
                  baseMaximumBinVolume :
                 0.0
            float rowCenterPrice =
                 (
                     GRID_ORIGIN_TICKS +
                     bin *
                      baseTicksInput +
                     baseTicksInput *
                      0.5) *
                 syminfo.mintick
            line histogramLine = line.new(
                 profileEndExclusive -
                  int(
                      histogramWidthMs *
                      relativeWidth),
                 rowCenterPrice,
                 profileEndExclusive,
                 rowCenterPrice,
                 xloc = xloc.bar_time,
                 color = color.new(
                     rangeColorInput,
                     65),
                 width = 2)
            histogramLines.push(
                 histogramLine)

        pocLine := line.new(
             profileStartInput,
             basePocPrice,
             profileEndExclusive,
             basePocPrice,
             xloc = xloc.bar_time,
             color = color.orange,
             width = 2)
        valueAreaHighLine := line.new(
             profileStartInput,
             baseValueAreaHighPrice,
             profileEndExclusive,
             baseValueAreaHighPrice,
             xloc = xloc.bar_time,
             color =
              color.new(color.silver, 20),
             style = line.style_dotted)
        valueAreaLowLine := line.new(
             profileStartInput,
             baseValueAreaLowPrice,
             profileEndExclusive,
             baseValueAreaLowPrice,
             xloc = xloc.bar_time,
             color =
              color.new(color.silver, 20),
             style = line.style_dotted)

        int consensusEndTime =
             extendConsensusInput ?
             time :
             profileEndExclusive
        if selectedClusterIndices.size() > 0
            for selectedPosition =
             0 to selectedClusterIndices.size() - 1
                int clusterIndex =
                     selectedClusterIndices.get(
                         selectedPosition)
                int supportCount =
                     clusterSupportCounts.get(
                         clusterIndex)
                int consensusTick =
                     clusterBaseTicks.get(
                         clusterIndex)
                float consensusPrice =
                     consensusTick *
                     syminfo.mintick
                bool drawingSuppressed =
                     clusterDrawingSuppressed.get(
                         clusterIndex)
                int transparency =
                     supportCount == 3 ?
                     0 : 28
                if not drawingSuppressed
                    line consensusLine = line.new(
                         profileStartInput,
                         consensusPrice,
                         consensusEndTime,
                         consensusPrice,
                         xloc = xloc.bar_time,
                         color = color.new(
                             consensusColorInput,
                             transparency),
                         width =
                             supportCount == 3 ?
                             3 : 2)
                    consensusLines.push(
                         consensusLine)
                string gridText =
                     (
                         not na(
                             clusterFineTicks.get(
                                 clusterIndex)) ?
                         "F" : "") +
                     "B" +
                     (
                         not na(
                             clusterCoarseTicks.get(
                                 clusterIndex)) ?
                         "C" : "")
                string consensusLabelText =
                     "MRC " +
                     str.tostring(
                         consensusPrice,
                         format.mintick) +
                     " | " +
                     str.tostring(
                         supportCount) +
                     "/3 " +
                     gridText +
                     " | min z " +
                     str.tostring(
                         clusterMinimumZ.get(
                             clusterIndex),
                         "#.00")
                if not drawingSuppressed
                    label consensusLabel =
                         label.new(
                             consensusEndTime,
                             consensusPrice,
                             consensusLabelText,
                             xloc = xloc.bar_time,
                             style =
                              label.style_label_left,
                             color = color.new(
                                 color.black,
                                 78),
                             textcolor = color.new(
                                 consensusColorInput,
                                 transparency),
                             size = size.tiny)
                    consensusLabels.push(
                         consensusLabel)

    string state =
         multiProfileValid and
          structuralBoundsValid ?
         "VALID / EXPERIMENTAL" :
         not chartSupported ?
         "USE STANDARD 15m CHART" :
         not inputWindowValid ?
         "INVALID INPUT RANGE" :
         not gridConfigurationValid ?
         "ROW GRIDS NOT DISTINCT" :
         not endpointsComplete ?
         "ENDPOINT COVERAGE FAIL" :
         invalidSourceBars > 0 ?
         "INVALID SOURCE BAR" :
         sourceArrayIssue ?
         "SOURCE ARRAY FAIL" :
         sourceOrderIssue ?
         "SOURCE ORDER FAIL" :
         fineOverflow or
          baseOverflow or
          coarseOverflow ?
         "PROFILE CAP FAIL" :
         not fineReconciliationValid or
          not baseReconciliationValid or
          not coarseReconciliationValid ?
         "VOLUME RECONCILIATION FAIL" :
         not fineAlgorithmValid or
          not baseAlgorithmValid or
          not coarseAlgorithmValid ?
         "PROFILE ALGORITHM FAIL" :
         not structuralBoundsValid ?
         "INVALID STRUCTURAL BOUNDS" :
         "INCOMPLETE"

    string gridText =
         str.tostring(fineTicks) +
         " / " +
         str.tostring(baseTicksInput) +
         " / " +
         str.tostring(coarseTicks) +
         " ticks" +
         (
             gridConfigurationValid ?
             "" : " (INVALID)")
    string candidateCountText =
         str.tostring(
             fineCandidateTicks.size()) +
         " / " +
         str.tostring(
             baseCandidateTicks.size()) +
         " / " +
         str.tostring(
             coarseCandidateTicks.size())
    string rawCountText =
         str.tostring(fineRawCount) +
         " / " +
         str.tostring(baseRawCount) +
         " / " +
         str.tostring(coarseRawCount)
    string cleanMadText =
         floatText(fineCleanMad) +
         " / " +
         floatText(baseCleanMad) +
         " / " +
         floatText(coarseCleanMad)
    string structuralStateText =
         not useStructuralBoundsInput ?
         "OFF" :
         not structuralBoundsValid ?
         "INVALID: high must exceed low" :
         str.tostring(
             structuralLowInput,
             format.mintick) +
         " / " +
         str.tostring(
         structuralHighInput,
             format.mintick) +
         " ± " +
         str.tostring(
             structuralDistanceBaseRowsInput,
             "#.##") +
         " rows"
    string consensusSummary = ""
    int visibleConsensusCount = 0
    if selectedClusterIndices.size() > 0
        for selectedPosition =
         0 to selectedClusterIndices.size() - 1
            int clusterIndex =
                 selectedClusterIndices.get(
                     selectedPosition)
            int supportCount =
                 clusterSupportCounts.get(
                     clusterIndex)
            float consensusPrice =
                 clusterBaseTicks.get(
                     clusterIndex) *
                 syminfo.mintick
            bool drawingSuppressed =
                 clusterDrawingSuppressed.get(
                     clusterIndex)
            visibleConsensusCount +=
                 drawingSuppressed ? 0 : 1
            string item =
                 str.tostring(
                     consensusPrice,
                     format.mintick) +
                 " (" +
                 str.tostring(
                     supportCount) +
                 "/3; min z " +
                 str.tostring(
                     clusterMinimumZ.get(
                         clusterIndex),
                     "#.00") +
                 (
                     drawingSuppressed ?
                     "; HIDDEN" : "") +
                 ")"
            consensusSummary :=
                 consensusSummary == "" ?
                 item :
                 consensusSummary +
                 ", " +
                 item
    if consensusSummary == ""
        consensusSummary := "NONE"

    color headerBg =
         color.new(#1565c0, 10)
    color rowBg =
         color.new(color.black, 20)
    color warningBg =
         color.new(#8d6e63, 15)
    diagnosticRow(
         diagnosticTable,
         0,
         "VP RSD MRC 0.4",
         state,
         headerBg)
    diagnosticRow(
         diagnosticTable,
         1,
         "Source stream",
         "one 15m total-volume stream",
         warningBg)
    diagnosticRow(
         diagnosticTable,
         2,
         "Input start",
         timeText(profileStartInput),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         3,
         "Input end incl.",
         timeText(profileEndBarInput),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         4,
         "Fine / base / coarse",
         gridText,
         gridConfigurationValid ?
          rowBg : warningBg)
    diagnosticRow(
         diagnosticTable,
         5,
         "Source bars",
         str.tostring(
             includedSourceBars),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         6,
         "Invalid bars",
         str.tostring(
             invalidSourceBars),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         7,
         "Source volume",
         floatText(
             sourceTotalVolume),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         8,
         "Alloc diffs F/B/C",
         floatText(
             fineAllocatedVolume -
             sourceTotalVolume) +
         " / " +
         floatText(
             baseAllocatedVolume -
             sourceTotalVolume) +
         " / " +
         floatText(
             coarseAllocatedVolume -
             sourceTotalVolume),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         9,
         "Base profile bins",
         multiProfileValid ?
         str.tostring(
             baseMaxBin -
             baseMinBin +
             1) :
         "NA",
         rowBg)
    diagnosticRow(
         diagnosticTable,
         10,
         "Base POC",
         str.tostring(
             basePocPrice,
             format.mintick),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         11,
         "Base VAL / VAH",
         str.tostring(
             baseValueAreaLowPrice,
             format.mintick) +
         " / " +
         str.tostring(
             baseValueAreaHighPrice,
             format.mintick),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         12,
         "Base VA achieved",
         str.tostring(
             baseAchievedValueAreaPercent,
             "#.0") +
         "%",
         rowBg)
    diagnosticRow(
         diagnosticTable,
         13,
         "Raw boundaries F/B/C",
         rawCountText,
         rowBg)
    diagnosticRow(
         diagnosticTable,
         14,
         "Gated candidates F/B/C",
         candidateCountText,
         rowBg)
    diagnosticRow(
         diagnosticTable,
         15,
         "Clean MAD F/B/C",
         cleanMadText,
         rowBg)
    diagnosticRow(
         diagnosticTable,
         16,
         "Base-seeded clusters",
         str.tostring(
             clusterBaseTicks.size()),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         17,
         "Consensus metadata",
         str.tostring(
             selectedClusterIndices.size()) +
         " found / " +
         str.tostring(
             visibleConsensusCount) +
         " drawn",
         rowBg)
    diagnosticRow(
         diagnosticTable,
         18,
         "Suppressed at bounds",
         str.tostring(
             structurallySuppressedCount),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         19,
         "Consensus",
         consensusSummary,
         rowBg)
    diagnosticRow(
         diagnosticTable,
         20,
         "Structural bounds",
         structuralStateText,
         useStructuralBoundsInput ?
          warningBg : rowBg)
    diagnosticRow(
         diagnosticTable,
         21,
         "Local / cross radii",
         str.tostring(
             localClusterRowsInput) +
         " grid rows / " +
         str.tostring(
             consensusClusterBaseRowsInput,
             "#.##") +
         " base rows",
         rowBg)
    diagnosticRow(
         diagnosticTable,
         22,
         "Geometry gates",
         str.tostring(
             minimumRatioInput,
             "#.00") +
         "x; W/J≤" +
         str.tostring(
             maximumNoiseFractionInput,
             "#.00") +
         "; dense≥" +
         str.tostring(
             minimumDenseFractionInput,
             "#.00") +
         "; z≥" +
         str.tostring(
             minimumRobustZInput,
             "#.00"),
         rowBg)
    diagnosticRow(
         diagnosticTable,
         23,
         "Warning",
         "Research only; compare expert marks",
         warningBg)

Overview

Fine, base, and coarse grids use row sizes near the base tick setting and assign each bar's volume by high-low tick overlap. Local density gates mark candidates on each grid, and a consensus shelf appears only when candidates from at least two grids cluster. Settings control the value-area size, gate thresholds, cluster radii, shelf count, extension to the current bar, and optional suppression near structural highs and lows. It draws a base histogram and consensus shelves on the price chart and does not generate entries or exits.

Details

Published by

345-strategies on GitHub

Code credit

TheStratSuite contributors, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Volume

Markets

Any symbol

Timeframes

M15-H1

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Topics

indicatorpine-scriptthestrattradingtradingviewprice-actionstratpine-script-librarythestrat-suite

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Start swing bartimetimestamp("23 Jun 2026 12:45 -0700")profileStartInput
End swing bar (included)timetimestamp("14 Jul 2026 06:30 -0700")profileEndBarInput
Base ticks per rowint10baseTicksInput
Base-grid Value Area target (%)float70.0valueAreaPercentInput
Candidate cap per gridint12candidateCapPerGridInput
Minimum dense/thin ratiofloat1.75minimumRatioInput
Maximum W / Jfloat0.50maximumNoiseFractionInput
Minimum dense side / grid max rowfloat0.20minimumDenseFractionInput
Minimum robust zfloat2.50minimumRobustZInput
Within-grid cluster radius (grid rows)int2localClusterRowsInput
Cross-grid cluster radius (base rows)float1.0consensusClusterBaseRowsInput
Maximum consensus shelvesint8consensusCountInput
Show all 20 settings
SettingTypeDefaultVariable
Extend consensus shelves to current barbooltrueextendConsensusInput
Suppress near structural low / highboolfalseuseStructuralBoundsInput
Structural lowprice0.0structuralLowInput
Structural highprice0.0structuralHighInput
Suppression distance (base rows)float1.0structuralDistanceBaseRowsInput
Base histogram width (% of range)float24.0histogramWidthInput
Range / base histogramcolor#26c6darangeColorInput
Consensus shelvescolor#ffd54fconsensusColorInput
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

Calculates an Elastic Volume Weighted Moving Average (EVWMA), incorporating trading volume to dynamically adjust moving average responsiveness over a specified lookback length. It plots directly on the price chart as a single moving average line. Optional colo

Indicator v4 Moving averagesVolume
Updated 10 days ago · GPL-3.0 GitHub repository of Elastic Volume Weighted Moving Average

This overlay indicator screens a pasted list of up to forty symbols and draws a table on the price chart. It classifies bullish or bearish setups from the percent change between the current candle and the candle 250 bars back, then shows RSI with a simple movi

Indicator v5 Oscillators & momentumVolume
Updated 1 year ago · MPL-2.0 GitHub repository of Momentum Setup - Ankush Bajaj Momentum Investing Setup

This overlay indicator draws a fixed-range volume profile on the price chart, splitting traded activity into up and down volume at each price level. It also plots a sentiment profile, a developing point of control, and value-area high and low around a configur

Indicator v5 VolumeSupport & resistance
Updated 2 months ago · MIT GitHub repository of Volume Profile and Indicator by DGT

META-tracker

by Allen1212
303 stars

This indicator overlays historical ARK Invest buy and sell events for META on the price chart. It places green labels below bars for purchases and red labels above bars for sales, with a same-day count of how many selected ARK ETFs traded the stock. The ETF in

Indicator v4 Volume
Updated 2 years ago · None stated GitHub repository of META-tracker
219 stars

An educational Pine Script v4 indicator demonstrating how to plot volume histograms on the main price chart using unscaled axes. The code includes examples and commented templates for creating vertical line markers at specific dates or time zones, such as mark

Indicator v4 Sessions & timeVolume
Updated 5 years ago · None stated GitHub repository of 教學-畫垂直線1

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers