Indicator · Pine Script v6 · On the chart

VP RSD Fixed Range Shelf Diagnostic

by 345-strategies· code credited to TheStratSuite contributors

This indicator builds a frozen volume profile over a fixed, half-open timestamp range and draws it on the price chart.

View on GitHub

GitHub stars5on the repository
Pine versionv6current
Lines of code1,035
Settings7inputs
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

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

VP_RSD_Fixed_Range_Shelf_Diagnostic_v0.2.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

// Static-range research diagnostic only.
//
// The start and end-exclusive timestamps are fixed inputs, so Pine knows the
// complete interval when it replays chart history. The script deliberately has
// no RSD wall selection, route gating, alerts, orders, or trading claims.
indicator(
     "VP RSD Fixed Range Shelf Diagnostic v0.2.0",
     shorttitle = "VP RSD Shelf v0.2",
     overlay = true,
     max_bars_back = 5000,
     max_lines_count = 10,
     max_labels_count = 10,
     max_boxes_count = 2)


// ============================================================================
// FIXED PROFILE CONTRACT
// ============================================================================

const string SCRIPT_VERSION = "0.2.0"
const string SOURCE_TIMEFRAME = "1"
const int MINUTE_MS = 60000
const int PRIMITIVE_TICKS = 1
const int GRID_ORIGIN_TICKS = 0
const int MAX_PROFILE_BINS = 2048
const int TOP_CANDIDATES = 5
const int VOLUME_SCALE = 1000

const int HASH_MODULUS = 2147483629
const int HASH_MULTIPLIER = 1103515245
const int HASH_INCREMENT = 12345
const int HASH_SEED = 146959810
const int HASH_NA = 2147483587

const int ERR_INVALID_CONFIG = 1
const int ERR_UNSUPPORTED_CHART = 2
const int ERR_SOURCE_ARRAYS = 4
const int ERR_SOURCE_SEQUENCE = 8
const int ERR_FOOTPRINT_UNAVAILABLE = 16
const int ERR_PRIMITIVE_ROW = 32
const int ERR_VOLUME_RECONCILIATION = 64
const int ERR_BIN_CAP = 128
const int ERR_ENDPOINT_COVERAGE = 256
const int ERR_PROFILE_INVALID = 512


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

string rangeGroup = "Frozen RSD interval (half-open)"
int rangeStartInput = input.time(
     timestamp("08 Jul 2026 13:30 +0000"),
     "Start timestamp (included)",
     confirm = false,
     group = rangeGroup,
     tooltip = "Must equal the open time of an available one-minute source bar.")
int rangeEndExclusiveInput = input.time(
     timestamp("23 Jul 2026 13:45 +0000"),
     "End-exclusive timestamp",
     confirm = false,
     group = rangeGroup,
     tooltip = "The source minute opening at this timestamp is excluded. For an inclusive 15-minute endpoint, use that bar's close time.")

string constructionGroup = "Profile construction"
int outputTicksInput = input.int(
     4,
     "Output-row ticks",
     minval = 1,
     maxval = 1000,
     group = constructionGroup,
     tooltip = "Fixed number of minimum ticks per range-wide output row. Choose this for the instrument before interpreting shelves.")
float valueAreaPercentInput = input.float(
     70.0,
     "Value Area target (%)",
     minval = 1.0,
     maxval = 100.0,
     step = 0.5,
     group = constructionGroup)

string displayGroup = "Diagnostic display"
bool showRangeBoxInput = input.bool(
     true,
     "Show frozen source-range box",
     group = displayGroup)
bool showRawCliffsInput = input.bool(
     true,
     "Show raw top-5 shelf boundaries",
     group = displayGroup,
     tooltip = "Unfiltered research candidates ranked only by a 3-row-median log-volume change. They are not trade levels.")
bool showAuditTableInput = input.bool(
     true,
     "Show audit table",
     group = displayGroup)


// ============================================================================
// PROVEN PHASE 0 PROFILE CORE
// ============================================================================

hashFold(int state, int value) =>
    int normalized = na(value) ? HASH_NA : math.abs(value % HASH_MODULUS)
    int mixed = int((state * HASH_MULTIPLIER + normalized + HASH_INCREMENT) % HASH_MODULUS)
    mixed < 0 ? -mixed : mixed

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

volumeToFingerprintInt(float value) =>
    na(value) ? na : int(math.round(value * VOLUME_SCALE))

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)
    int candidateSpan = candidateMax - candidateMin + 1
    bool overflow = candidateSpan > MAX_PROFILE_BINS or
     (not alreadyPresent and bins.size() >= MAX_PROFILE_BINS)
    int nextMin = currentMin
    int nextMax = currentMax
    if not overflow
        float previousVolume = alreadyPresent ? bins.get(key) : 0.0
        bins.put(key, previousVolume + addedVolume)
        nextMin := candidateMin
        nextMax := candidateMax
    [nextMin, nextMax, overflow]

// POC ties: nearest bin center to the volume-weighted mean, then lower bin.
// VA: contiguous expansion, larger next row first, then nearer POC, then upper.
// Do not add a row that would overshoot the requested target.
calculateProfile(
     map<int, float> bins,
     int minBin,
     int maxBin,
     float totalVolume) =>
    bool valid = not na(minBin) and not na(maxBin) and
     maxBin >= minBin and maxBin - minBin + 1 <= MAX_PROFILE_BINS and
     bins.size() > 0 and not na(totalVolume) and totalVolume > 0.0
    int pocBin = na
    int vaLowBin = na
    int vaHighBin = na
    float achievedPercent = na
    float pocVolume = na

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

        float meanCenter2 = weightedCenter2 / totalVolume
        float bestDistance = na
        float pocTieTolerance = math.max(
             0.000000001,
             math.abs(maximumVolume) * 0.000000000001)
        for bin = minBin to maxBin
            float binVolume = bins.contains(bin) ? bins.get(bin) : 0.0
            if math.abs(binVolume - maximumVolume) <= pocTieTolerance
                int center2 =
                     2 * (GRID_ORIGIN_TICKS + bin * outputTicksInput) +
                     outputTicksInput
                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

        valid := not na(pocBin)
        if valid
            pocVolume := bins.contains(pocBin) ? bins.get(pocBin) : 0.0
            vaLowBin := pocBin
            vaHighBin := pocBin
            float includedVolume = pocVolume
            float targetVolume =
                 totalVolume * valueAreaPercentInput / 100.0
            float volumeTolerance = math.max(
                 0.000000001,
                 math.abs(totalVolume) * 0.000000000001)
            int expansionSteps = 0

            while includedVolume < targetVolume - volumeTolerance and
             expansionSteps < MAX_PROFILE_BINS
                bool hasLower = vaLowBin > minBin
                bool hasUpper = vaHighBin < maxBin
                if not hasLower and not hasUpper
                    break

                int lowerCandidate = vaLowBin - 1
                int upperCandidate = vaHighBin + 1
                float lowerVolume = hasLower ?
                     (bins.contains(lowerCandidate) ?
                      bins.get(lowerCandidate) : 0.0) : na
                float upperVolume = hasUpper ?
                     (bins.contains(upperCandidate) ?
                      bins.get(upperCandidate) : 0.0) : na
                float comparisonTolerance = math.max(
                     0.000000001,
                     math.max(
                         hasLower ? math.abs(lowerVolume) : 0.0,
                         hasUpper ? math.abs(upperVolume) : 0.0) *
                         0.000000000001)

                bool chooseUpper = not hasLower
                if hasLower and hasUpper
                    if upperVolume > lowerVolume + comparisonTolerance
                        chooseUpper := true
                    else if lowerVolume >
                     upperVolume + comparisonTolerance
                        chooseUpper := false
                    else
                        int lowerDistance =
                             math.abs(lowerCandidate - pocBin)
                        int upperDistance =
                             math.abs(upperCandidate - pocBin)
                        chooseUpper := upperDistance < lowerDistance or
                             upperDistance == lowerDistance

                float selectedVolume =
                     chooseUpper ? upperVolume : lowerVolume
                if includedVolume + selectedVolume >
                 targetVolume + volumeTolerance
                    break

                if chooseUpper
                    vaHighBin := upperCandidate
                else
                    vaLowBin := lowerCandidate
                includedVolume += selectedVolume
                expansionSteps += 1

            achievedPercent := includedVolume / totalVolume * 100.0

    [valid, pocBin, vaLowBin, vaHighBin, achievedPercent, pocVolume]

freezeProfile(
     map<int, float> bins,
     int minBin,
     int maxBin,
     float totalVolume,
     bool baseComplete) =>
    [algorithmValid, pocBin, vaLowBin, vaHighBin,
     achievedPercent, pocVolume] =
         calculateProfile(bins, minBin, maxBin, totalVolume)
    bool complete = baseComplete and algorithmValid
    int pocTicks = complete ?
         GRID_ORIGIN_TICKS + pocBin * outputTicksInput +
         int(outputTicksInput / 2) : na
    int vahTicks = complete ?
         GRID_ORIGIN_TICKS + (vaHighBin + 1) * outputTicksInput : na
    int valTicks = complete ?
         GRID_ORIGIN_TICKS + vaLowBin * outputTicksInput : na
    [complete, pocTicks, vahTicks, valTicks,
     complete ? achievedPercent : na,
     complete ? pocVolume : na]


// ============================================================================
// SHELF-CANDIDATE HELPERS
// ============================================================================

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

median3(float first, float second, float third) =>
    first + second + third -
     math.min(first, math.min(second, third)) -
     math.max(first, math.max(second, third))

resetCandidates(
     array<float> scores,
     array<int> boundaryTicks,
     array<float> lowerVolumes,
     array<float> upperVolumes) =>
    for rank = 0 to TOP_CANDIDATES - 1
        scores.set(rank, na)
        boundaryTicks.set(rank, na)
        lowerVolumes.set(rank, na)
        upperVolumes.set(rank, na)

insertCandidate(
     array<float> scores,
     array<int> boundaryTicks,
     array<float> lowerVolumes,
     array<float> upperVolumes,
     float score,
     int candidateBoundaryTicks,
     float candidateLowerVolume,
     float candidateUpperVolume) =>
    int insertAt = na
    for rank = 0 to TOP_CANDIDATES - 1
        float incumbent = scores.get(rank)
        if na(insertAt) and (na(incumbent) or score > incumbent)
            insertAt := rank

    bool inserted = not na(insertAt)
    if inserted
        int shiftRank = TOP_CANDIDATES - 1
        while shiftRank > insertAt
            scores.set(shiftRank, scores.get(shiftRank - 1))
            boundaryTicks.set(
                 shiftRank,
                 boundaryTicks.get(shiftRank - 1))
            lowerVolumes.set(
                 shiftRank,
                 lowerVolumes.get(shiftRank - 1))
            upperVolumes.set(
                 shiftRank,
                 upperVolumes.get(shiftRank - 1))
            shiftRank -= 1
        scores.set(insertAt, score)
        boundaryTicks.set(insertAt, candidateBoundaryTicks)
        lowerVolumes.set(insertAt, candidateLowerVolume)
        upperVolumes.set(insertAt, candidateUpperVolume)
    inserted

calculateRawCliffs(
     map<int, float> bins,
     int minBin,
     int maxBin,
     float totalVolume,
     array<float> scores,
     array<int> boundaryTicks,
     array<float> lowerVolumes,
     array<float> upperVolumes) =>
    resetCandidates(
         scores,
         boundaryTicks,
         lowerVolumes,
         upperVolumes)
    bool enoughRows = not na(minBin) and not na(maxBin) and
         maxBin - minBin + 1 >= 4
    if enoughRows
        float epsilon = math.max(
             1.0,
             math.abs(totalVolume) * 0.000000000001)
        for lowerBin = minBin + 1 to maxBin - 2
            float lowerMedian = median3(
                 binVolume(bins, lowerBin - 1),
                 binVolume(bins, lowerBin),
                 binVolume(bins, lowerBin + 1))
            float upperMedian = median3(
                 binVolume(bins, lowerBin),
                 binVolume(bins, lowerBin + 1),
                 binVolume(bins, lowerBin + 2))
            float signedLogChange = math.log(
                 (upperMedian + epsilon) /
                 (lowerMedian + epsilon))
            float score = math.abs(signedLogChange)
            int candidateBoundaryTicks =
                 GRID_ORIGIN_TICKS +
                 (lowerBin + 1) * outputTicksInput
            insertCandidate(
                 scores,
                 boundaryTicks,
                 lowerVolumes,
                 upperVolumes,
                 score,
                 candidateBoundaryTicks,
                 lowerMedian,
                 upperMedian)


// ============================================================================
// ONE FIXED ONE-MINUTE FOOTPRINT REQUEST
// ============================================================================

oneMinutePayload() =>
    footprint fp = request.footprint(
         PRIMITIVE_TICKS,
         va_percent = valueAreaPercentInput)
    [time, time_close, fp]

int chartSeconds = timeframe.in_seconds()
bool chartSupported = chart.is_standard and timeframe.isminutes and
     chartSeconds >= 60 and chartSeconds <= 3600
bool endpointsAligned =
     rangeStartInput % MINUTE_MS == 0 and
     rangeEndExclusiveInput % MINUTE_MS == 0
bool configurationValid =
     rangeStartInput < rangeEndExclusiveInput and
     endpointsAligned and
     outputTicksInput > 0 and
     valueAreaPercentInput > 0.0 and
     valueAreaPercentInput <= 100.0
bool requestSupported = chartSupported and configurationValid

array<int> sourceTimes = na
array<int> sourceCloseTimes = na
array<footprint> sourceFootprints = na

if requestSupported
    [requestedTimes, requestedCloseTimes, requestedFootprints] =
         request.security_lower_tf(
             syminfo.tickerid,
             SOURCE_TIMEFRAME,
             oneMinutePayload(),
             ignore_invalid_timeframe = true)
    sourceTimes := requestedTimes
    sourceCloseTimes := requestedCloseTimes
    sourceFootprints := requestedFootprints


// ============================================================================
// FROZEN-RANGE ACCUMULATOR AND INTEGRITY STATE
// ============================================================================

var map<int, float> rangeBins = map.new<int, float>()
var int rangeMinBin = na
var int rangeMaxBin = na
var float rangeTotalVolume = 0.0
var int rangeSourceCount = 0
var int rangeRowCount = 0
var int rangeFirstTime = na
var int rangeLastTime = na
var int rangeLastCloseTime = na
var int rangeSessionBreaks = 0
var int rangeMissingFootprints = 0
var int rangeSourceFingerprint = HASH_SEED

var bool sourceArrayIssue = false
var bool sourceSequenceIssue = false
var bool sourceTimestampIssue = false
var bool primitiveRowIssue = false
var bool reconciliationIssue = false
var bool binOverflow = false
var int previousRequestedTime = na

var bool rangeFrozen = false
var bool profileComplete = false
var int frozenErrorMask = na
var int frozenPocTicks = na
var int frozenVahTicks = na
var int frozenValTicks = na
var float frozenAchievedPercent = na
var float frozenPocVolume = na

var array<float> topScores =
     array.new<float>(TOP_CANDIDATES, na)
var array<int> topBoundaryTicks =
     array.new<int>(TOP_CANDIDATES, na)
var array<float> topLowerVolumes =
     array.new<float>(TOP_CANDIDATES, na)
var array<float> topUpperVolumes =
     array.new<float>(TOP_CANDIDATES, na)

if requestSupported and barstate.isconfirmed and not rangeFrozen
    int timeCount = sourceTimes.size()
    int closeTimeCount = sourceCloseTimes.size()
    int footprintCount = sourceFootprints.size()
    bool lengthsMatch =
         timeCount == closeTimeCount and
         timeCount == footprintCount
    sourceArrayIssue := sourceArrayIssue or not lengthsMatch
    int safeCount =
         math.min(timeCount, math.min(closeTimeCount, footprintCount))

    if safeCount > 0
        for sourceIndex = 0 to safeCount - 1
            int sourceTime = sourceTimes.get(sourceIndex)
            int sourceCloseTime =
                 sourceCloseTimes.get(sourceIndex)
            footprint sourceFootprint =
                 sourceFootprints.get(sourceIndex)

            bool timestampValid =
                 not na(sourceTime) and
                 not na(sourceCloseTime) and
                 sourceTime % MINUTE_MS == 0 and
                 sourceCloseTime == sourceTime + MINUTE_MS
            bool sequenceValid =
                 na(previousRequestedTime) or
                 sourceTime > previousRequestedTime
            sourceSequenceIssue :=
                 sourceSequenceIssue or not sequenceValid
            sourceTimestampIssue :=
                 sourceTimestampIssue or not timestampValid
            previousRequestedTime := sourceTime

            bool included =
                 timestampValid and
                 sourceTime >= rangeStartInput and
                 sourceTime < rangeEndExclusiveInput

            if included
                rangeFirstTime := rangeSourceCount == 0 ?
                     sourceTime : rangeFirstTime
                if rangeSourceCount > 0 and
                 sourceTime != rangeLastCloseTime
                    // Informational only. Multi-session ranges legitimately
                    // contain overnight, weekend, and scheduled closure gaps.
                    rangeSessionBreaks += 1
                rangeLastTime := sourceTime
                rangeLastCloseTime := sourceCloseTime
                rangeSourceCount += 1

                bool footprintAvailable =
                     not na(sourceFootprint)
                rangeMissingFootprints +=
                     footprintAvailable ? 0 : 1
                int minuteFingerprint =
                     hashFold(HASH_SEED, sourceTime)
                int minuteRowCount = 0

                if footprintAvailable
                    array<volume_row> rows =
                         sourceFootprint.rows()
                    minuteRowCount := rows.size()
                    float footprintTotal =
                         sourceFootprint.total_volume()
                    float summedRowVolume = 0.0
                    minuteFingerprint := hashFold(
                         minuteFingerprint,
                         minuteRowCount)
                    minuteFingerprint := hashFold(
                         minuteFingerprint,
                         volumeToFingerprintInt(footprintTotal))

                    for row in rows
                        float rowDown = row.down_price()
                        float rowUp = row.up_price()
                        float rowVolume = row.total_volume()
                        int downTicks = priceToTicks(rowDown)
                        int upTicks = priceToTicks(rowUp)
                        minuteFingerprint := hashFold(
                             minuteFingerprint,
                             downTicks)
                        minuteFingerprint := hashFold(
                             minuteFingerprint,
                             upTicks)
                        minuteFingerprint := hashFold(
                             minuteFingerprint,
                             volumeToFingerprintInt(rowVolume))

                        bool rowValuesValid =
                             not na(rowDown) and
                             not na(rowUp) and
                             not na(rowVolume) and
                             rowVolume >= 0.0
                        float rowSpanTicks = rowValuesValid ?
                             (rowUp - rowDown) /
                             syminfo.mintick : na
                        bool rowBoundsAligned =
                             rowValuesValid and
                             math.abs(
                                 rowDown / syminfo.mintick -
                                 downTicks) <= 0.000001 and
                             math.abs(
                                 rowUp / syminfo.mintick -
                                 upTicks) <= 0.000001
                        bool primitiveSpanValid =
                             rowValuesValid and
                             rowBoundsAligned and
                             math.abs(
                                 rowSpanTicks -
                                 PRIMITIVE_TICKS) <= 0.000001
                        primitiveRowIssue :=
                             primitiveRowIssue or
                             not primitiveSpanValid

                        if rowValuesValid
                            summedRowVolume += rowVolume
                            float rowMidpoint =
                                 math.avg(rowDown, rowUp)
                            int primitiveMidpointTick =
                                 int(math.round(
                                     rowMidpoint /
                                     syminfo.mintick))
                            int outputBin = int(math.floor(
                                 (primitiveMidpointTick -
                                  GRID_ORIGIN_TICKS) /
                                 float(outputTicksInput)))
                            [nextMinBin, nextMaxBin,
                             putOverflow] =
                                 boundedPut(
                                     rangeBins,
                                     outputBin,
                                     rowVolume,
                                     rangeMinBin,
                                     rangeMaxBin)
                            rangeMinBin := nextMinBin
                            rangeMaxBin := nextMaxBin
                            binOverflow :=
                                 binOverflow or
                                 putOverflow
                            rangeTotalVolume += rowVolume

                    float reconciliationTolerance =
                         math.max(
                             0.000001,
                             math.abs(footprintTotal) *
                             0.000000001)
                    bool minuteReconciliationIssue =
                         na(footprintTotal) or
                         math.abs(
                             summedRowVolume -
                             footprintTotal) >
                             reconciliationTolerance
                    reconciliationIssue :=
                         reconciliationIssue or
                         minuteReconciliationIssue
                else
                    minuteFingerprint := hashFold(
                         minuteFingerprint,
                         HASH_NA)

                rangeRowCount += minuteRowCount
                rangeSourceFingerprint := hashFold(
                     rangeSourceFingerprint,
                     minuteFingerprint)

    bool endReached =
         time_close >= rangeEndExclusiveInput
    if endReached
        int errorMask = 0
        errorMask += configurationValid ?
             0 : ERR_INVALID_CONFIG
        errorMask += chartSupported ?
             0 : ERR_UNSUPPORTED_CHART
        errorMask += sourceArrayIssue ?
             ERR_SOURCE_ARRAYS : 0
        errorMask +=
             sourceSequenceIssue or sourceTimestampIssue ?
             ERR_SOURCE_SEQUENCE : 0
        errorMask += rangeMissingFootprints > 0 ?
             ERR_FOOTPRINT_UNAVAILABLE : 0
        errorMask += primitiveRowIssue ?
             ERR_PRIMITIVE_ROW : 0
        errorMask += reconciliationIssue ?
             ERR_VOLUME_RECONCILIATION : 0
        errorMask += binOverflow ?
             ERR_BIN_CAP : 0

        bool endpointCoverage =
             rangeSourceCount > 0 and
             rangeFirstTime == rangeStartInput and
             rangeLastCloseTime ==
                 rangeEndExclusiveInput
        errorMask += endpointCoverage ?
             0 : ERR_ENDPOINT_COVERAGE

        bool baseComplete = errorMask == 0
        [snapshotComplete, snapshotPocTicks,
         snapshotVahTicks, snapshotValTicks,
         snapshotAchievedPercent, snapshotPocVolume] =
             freezeProfile(
                 rangeBins,
                 rangeMinBin,
                 rangeMaxBin,
                 rangeTotalVolume,
                 baseComplete)

        if not snapshotComplete
            errorMask += ERR_PROFILE_INVALID

        rangeFrozen := true
        profileComplete := snapshotComplete
        frozenErrorMask := errorMask
        frozenPocTicks := snapshotPocTicks
        frozenVahTicks := snapshotVahTicks
        frozenValTicks := snapshotValTicks
        frozenAchievedPercent :=
             snapshotAchievedPercent
        frozenPocVolume := snapshotPocVolume

        if profileComplete
            calculateRawCliffs(
                 rangeBins,
                 rangeMinBin,
                 rangeMaxBin,
                 rangeTotalVolume,
                 topScores,
                 topBoundaryTicks,
                 topLowerVolumes,
                 topUpperVolumes)


// Freeze an unsupported/invalid configuration into an auditable blank result
// once the chart has reached the requested endpoint.
if not rangeFrozen and barstate.isconfirmed and
 (barstate.islastconfirmedhistory or
  (barstate.islast and time_close >= rangeEndExclusiveInput)) and
 not requestSupported
    int errorMask = 0
    errorMask += configurationValid ?
         0 : ERR_INVALID_CONFIG
    errorMask += chartSupported ?
         0 : ERR_UNSUPPORTED_CHART
    rangeFrozen := true
    profileComplete := false
    frozenErrorMask :=
         errorMask + ERR_PROFILE_INVALID


// ============================================================================
// LAST-BAR DIAGNOSTIC RENDERER
// ============================================================================

ticksToPrice(int ticks) =>
    na(ticks) ? na :
     float(ticks) * syminfo.mintick

profileLowPrice() =>
    na(rangeMinBin) ? na :
     float(
         GRID_ORIGIN_TICKS +
         rangeMinBin * outputTicksInput) *
         syminfo.mintick

profileHighPrice() =>
    na(rangeMaxBin) ? na :
     float(
         GRID_ORIGIN_TICKS +
         (rangeMaxBin + 1) *
         outputTicksInput) *
         syminfo.mintick

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

var box sourceRangeBox = na
var array<line> cliffLines = array.new<line>()
var array<label> cliffLabels = array.new<label>()
var table auditTable = table.new(
     position.top_right,
     2,
     10,
     border_width = 1,
     border_color = color.new(color.gray, 70))

if barstate.islast
    if not na(sourceRangeBox)
        box.delete(sourceRangeBox)
        sourceRangeBox := na
    for candidateLine in cliffLines
        line.delete(candidateLine)
    for candidateLabel in cliffLabels
        label.delete(candidateLabel)
    cliffLines.clear()
    cliffLabels.clear()

    bool renderProfile =
         rangeFrozen and profileComplete

    if renderProfile and showRangeBoxInput
        sourceRangeBox := box.new(
             left = rangeStartInput,
             top = profileHighPrice(),
             right = rangeEndExclusiveInput,
             bottom = profileLowPrice(),
             xloc = xloc.bar_time,
             border_color = color.new(color.aqua, 55),
             border_width = 1,
             bgcolor = color.new(color.aqua, 96))

    if renderProfile and showRawCliffsInput
        int rightTime =
             math.max(time_close, rangeEndExclusiveInput)
        for rank = 0 to TOP_CANDIDATES - 1
            float candidateScore =
                 topScores.get(rank)
            int candidateTicks =
                 topBoundaryTicks.get(rank)
            if not na(candidateScore) and
             not na(candidateTicks)
                float candidatePrice =
                     ticksToPrice(candidateTicks)
                float lowerVolume =
                     topLowerVolumes.get(rank)
                float upperVolume =
                     topUpperVolumes.get(rank)
                string volumeDirection =
                     upperVolume > lowerVolume ?
                     "VOL UP" : "VOL DOWN"
                color candidateColor = color.new(
                     color.orange,
                     math.min(80, 20 + rank * 12))
                line candidateLine = line.new(
                     x1 = rangeEndExclusiveInput,
                     y1 = candidatePrice,
                     x2 = rightTime,
                     y2 = candidatePrice,
                     xloc = xloc.bar_time,
                     extend = extend.right,
                     color = candidateColor,
                     style = line.style_dotted,
                     width = rank == 0 ? 2 : 1)
                label candidateLabel = label.new(
                     x = rightTime,
                     y = candidatePrice,
                     text =
                         "RAW CLIFF " +
                         str.tostring(rank + 1) +
                         "  " +
                         str.tostring(
                             candidateScore,
                             "#.###") +
                         "  " +
                         volumeDirection,
                     xloc = xloc.bar_time,
                     style = label.style_label_left,
                     color = color.new(color.black, 80),
                     textcolor = candidateColor,
                     size = size.tiny)
                cliffLines.push(candidateLine)
                cliffLabels.push(candidateLabel)

    if showAuditTableInput
        color headerBackground =
             color.new(color.blue, 75)
        color bodyBackground =
             color.new(color.black, 82)
        color statusColor =
             profileComplete ?
             color.aqua :
             rangeFrozen ?
             color.orange :
             color.silver
        string statusText =
             profileComplete ?
             "READY / FROZEN" :
             rangeFrozen ?
             "INVALID / BLANK" :
             "WAITING FOR END"

        table.cell(
             auditTable,
             0,
             0,
             "VP RSD SHELF",
             text_color = color.white,
             bgcolor = headerBackground)
        table.cell(
             auditTable,
             1,
             0,
             SCRIPT_VERSION,
             text_color = color.white,
             bgcolor = headerBackground)

        table.cell(
             auditTable,
             0,
             1,
             "Status",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             1,
             statusText,
             text_color = statusColor,
             bgcolor = bodyBackground)

        table.cell(
             auditTable,
             0,
             2,
             "Range",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             2,
             timeText(rangeStartInput) +
             "\n→ " +
             timeText(rangeEndExclusiveInput),
             text_color = color.white,
             bgcolor = bodyBackground)

        table.cell(
             auditTable,
             0,
             3,
             "1m source",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             3,
             str.tostring(rangeSourceCount) +
             " bars / " +
             str.tostring(rangeRowCount) +
             " rows",
             text_color = color.white,
             bgcolor = bodyBackground)

        table.cell(
             auditTable,
             0,
             4,
             "Output bins",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             4,
             str.tostring(rangeBins.size()) +
             " @ " +
             str.tostring(outputTicksInput) +
             " ticks",
             text_color = color.white,
             bgcolor = bodyBackground)

        table.cell(
             auditTable,
             0,
             5,
             "Volume",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             5,
             str.tostring(
                 rangeTotalVolume,
                 "#.##"),
             text_color = color.white,
             bgcolor = bodyBackground)

        table.cell(
             auditTable,
             0,
             6,
             "POC",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             6,
             profileComplete ?
             str.tostring(
                 ticksToPrice(frozenPocTicks),
                 format.mintick) :
             "—",
             text_color = color.white,
             bgcolor = bodyBackground)

        table.cell(
             auditTable,
             0,
             7,
             "VAL / VAH",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             7,
             profileComplete ?
             str.tostring(
                 ticksToPrice(frozenValTicks),
                 format.mintick) +
             " / " +
             str.tostring(
                 ticksToPrice(frozenVahTicks),
                 format.mintick) +
             "\n" +
             str.tostring(
                 frozenAchievedPercent,
                 "#.###") +
             "%" :
             "—",
             text_color = color.white,
             bgcolor = bodyBackground)

        table.cell(
             auditTable,
             0,
             8,
             "Session gaps",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             8,
             str.tostring(rangeSessionBreaks),
             text_color = color.white,
             bgcolor = bodyBackground)

        table.cell(
             auditTable,
             0,
             9,
             "Error / FP",
             text_color = color.silver,
             bgcolor = bodyBackground)
        table.cell(
             auditTable,
             1,
             9,
             (rangeFrozen ?
              str.tostring(frozenErrorMask) :
              "pending") +
             "\n" +
             str.tostring(rangeSourceFingerprint),
             text_color =
                 rangeFrozen and frozenErrorMask != 0 ?
                 color.orange :
                 color.white,
             bgcolor = bodyBackground)
    else
        table.clear(
             auditTable,
             0,
             0,
             1,
             9)

Overview

VP RSD Fixed Range Shelf Diagnostic aggregates one-minute source bars into output rows sized by a chosen tick count, marks a configurable value-area percentage, and can plot the top five raw shelf boundaries ranked by a three-row median log-volume change. Users can show or hide the source-range box, those shelf lines, and an audit table. The boundaries are research candidates, not trade levels, and the script has no alerts or orders.

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, Support & resistance, Screeners & dashboards

Markets

Any symbol

Timeframes

Any timeframe

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 timestamp (included)timetimestamp("08 Jul 2026 13:30 +0000")rangeStartInput
End-exclusive timestamptimetimestamp("23 Jul 2026 13:45 +0000")rangeEndExclusiveInput
Output-row ticksint4outputTicksInput
Value Area target (%)float70.0valueAreaPercentInput
Show frozen source-range boxbooltrueshowRangeBoxInput
Show raw top-5 shelf boundariesbooltrueshowRawCliffsInput
Show audit tablebooltrueshowAuditTableInput
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

This overlay indicator builds a horizontal momentum profile over a chosen lookback. It splits price into rows and plots the average WaveTrend of each zone as a bar colored by direction, plus an optional MFI reference band that stays gray when real trade volume

Indicator v6 Oscillators & momentumVolume
Updated yesterday · MIT GitHub repository of Momentum Profile [WavesUnchained]
4 stars

This overlay indicator draws an on-chart depth-of-market table that splits volume at each price tick into estimated buy volume, sell volume, delta, and session volume. Growing text bars compare buyers and sellers, and recent bars approximate resting bid and as

Indicator v5 VolumeScreeners & dashboards
Updated 6 months ago · None stated GitHub repository of Custom DOM - Bookmap Style
4 stars

This indicator overlays an order flow intelligence suite directly on the price chart. It renders a liquidity heatmap across configured price zones, alongside an on-chart information panel displaying volume delta, cumulative volume delta, a momentum compass bas

Indicator v6 VolumeScreeners & dashboards
Updated 2 weeks ago · MIT GitHub repository of GHOST — OrderFlow Intelligence
4 stars

An overlay indicator that calculates multi-timeframe market conditions and displays them in an information panel on the price chart. It evaluates timeframe metrics such as 15-minute and 1-hour periods to monitor RSI values, support and resistance levels, and r

Indicator v5 Screeners & dashboardsOscillators & momentum Repaints
Updated 4 years ago · None stated GitHub repository of SnagProfit Logic 15M 1H

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