Indicator · Pine Script v6 · On the chart

Volume Profile Phase 0 Probe

by 345-strategies· code credited to TheStratSuite contributors

Volume Profile Phase 0 Probe is a non-visual diagnostic tool designed to perform parity testing and internal backend validation for volume profile calculations.

View on GitHub

GitHub stars5on the repository
Pine versionv6current
Lines of code925
Settings13inputs
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

925 lines of Pine Script v6 from diagnostics/volume-profile-phase0/Volume_Profile_Phase0_Probe_v0.1.0.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

Volume_Profile_Phase0_Probe_v0.1.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

// Personal diagnostic only. This is a parity probe, not a trading indicator.
// It deliberately has no signals, alerts, drawings, tables, or visible plots.
indicator("Volume Profile Phase 0 Probe v0.1.0", shorttitle = "VP Phase0 v0.1.0", overlay = true, max_bars_back = 5000)


// ============================================================================
// LOCKED PHASE 0 CONTRACT
// ============================================================================

const string SCRIPT_VERSION = "0.1.0"
const string SOURCE_TIMEFRAME = "1"
const string RTH_SESSION = "0830-1515:23456"
const string RTH_TIMEZONE = "America/Chicago"
const int MINUTE_MS = 60000
const int PRIMITIVE_TICKS = 1
const int GRID_ORIGIN_TICKS = 0
const int RTH_EXPECTED_MINUTES = 405
const int MAX_PROFILE_BINS = 2048
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 string FP_ALGORITHM = "fold31_v1"
const string POC_ALGORITHM = "max_nearest_weighted_mean_lower"
const string VA_ALGORITHM = "contiguous_larger_nearer_upper_no_overshoot"
const string PRIOR_RTH_SELECTION = "latest_complete_end_lte_measurement_start"
const string ROW_MAPPING_ALGORITHM = "midpoint_round_tick_floor_global"

const int ERR_UNSUPPORTED_CHART = 1
const int ERR_FOOTPRINT_UNAVAILABLE = 2
const int ERR_MANUAL_INCOMPLETE = 4
const int ERR_RTH_INCOMPLETE = 8
const int ERR_PRIMITIVE_ROW = 16
const int ERR_BIN_CAP = 32
const int ERR_VOLUME_RECONCILIATION = 64
const int ERR_INVALID_CONFIG = 128
const int ERR_SOURCE_ARRAYS = 256


// ============================================================================
// IMMUTABLE RUN ENVELOPE AND RANGE INPUTS
// ============================================================================

string identityGroup = "Run identity (manifest supplied)"
string logicalRunIdInput = input.string("vp_phase0_logical_001", "Logical run ID", group = identityGroup)
int logicalRunTokenInput = input.int(1, "Logical run token", minval = 0, maxval = 2147483647, group = identityGroup)
string attemptIdInput = input.string("attempt_001", "Attempt ID", group = identityGroup)
int attemptTokenInput = input.int(1, "Attempt token", minval = 0, maxval = 2147483647, group = identityGroup)
int configTokenInput = input.int(1, "Configuration token", minval = 0, maxval = 2147483647, group = identityGroup)
string pineSettingsHashInput = input.string("UNSET", "Expected Pine-settings hash", group = identityGroup)

string windowGroup = "Measurement window (half-open, UTC milliseconds)"
int measurementStartInput = input.time(timestamp("24 Jul 2026 13:30 +0000"), "Measurement start", group = windowGroup)
int measurementEndInput = input.time(timestamp("24 Jul 2026 20:15 +0000"), "Measurement end", group = windowGroup)

string manualGroup = "Manual frozen range (half-open)"
int manualStartInput = input.time(
     timestamp("23 Jul 2026 13:30 +0000"),
     "Manual start",
     confirm = false,
     group = manualGroup,
     inline = "manual_range")
int manualEndInput = input.time(
     timestamp("23 Jul 2026 20:15 +0000"),
     "Manual end",
     confirm = false,
     group = manualGroup,
     inline = "manual_range")
int manualExpectedMinutesInput = input.int(
     405,
     "Expected one-minute bars",
     minval = 1,
     maxval = 100000,
     group = manualGroup,
     tooltip = "Phase 0 ranges must have continuous one-minute coverage. For a normal RTH range use 405.")

string constructionGroup = "Profile construction (locked per logical run)"
int outputTicksInput = input.int(
     4,
     "Output-row ticks",
     options = [2, 4, 8],
     group = constructionGroup)
float valueAreaPercentInput = input.float(
     70.0,
     "Value Area target (%)",
     minval = 1.0,
     maxval = 100.0,
     step = 0.5,
     group = constructionGroup)


// ============================================================================
// PURE HELPERS
// ============================================================================

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

intText(int value) =>
    na(value) ? "NA" : str.tostring(value)

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

boolText(bool value) =>
    value ? "1" : "0"

safeIdentity(string value) =>
    str.length(value) > 0 and
     not str.contains(value, "|")

// Mutates only the referenced map. Scalar state is returned to the caller.
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]

// Calculates a deterministic range-wide profile on the fixed global grid.
// POC ties: nearest bin center to the volume-weighted mean, then lower bin.
// VA: compare the next contiguous bins, prefer larger volume, then nearer POC,
// then upper. Stop instead of adding a bin that would overshoot the 70% 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]

// The script contains exactly one request.footprint() call, copied into exactly
// one fixed one-minute request.security_lower_tf() context.
oneMinutePayload() =>
    footprint fp = request.footprint(PRIMITIVE_TICKS, va_percent = valueAreaPercentInput)
    bool inRth = not na(time(SOURCE_TIMEFRAME, RTH_SESSION, RTH_TIMEZONE))
    int dayKey = year(time, RTH_TIMEZONE) * 10000 +
     month(time, RTH_TIMEZONE) * 100 +
     dayofmonth(time, RTH_TIMEZONE)
    [time, fp, inRth, dayKey]

vpxRecord(string kind, string severity, int sequence, string fields) =>
    "VPX|schema=1|kind=" + kind +
     "|severity=" + severity +
     "|seq=" + str.tostring(sequence) +
     "|logical_run_id=" + logicalRunIdInput +
     "|attempt_id=" + attemptIdInput +
     "|attempt_token=" + str.tostring(attemptTokenInput) +
     "|config_token=" + str.tostring(configTokenInput) +
     fields

emitInfo(int sequence, string kind, string fields) =>
    int nextSequence = sequence + 1
    log.info(vpxRecord(kind, "INFO", nextSequence, fields))
    nextSequence

emitWarning(int sequence, string kind, string fields) =>
    int nextSequence = sequence + 1
    log.warning(vpxRecord(kind, "WARN", nextSequence, fields))
    nextSequence

emitError(int sequence, string kind, string fields) =>
    int nextSequence = sequence + 1
    log.error(vpxRecord(kind, "ERROR", nextSequence, fields))
    nextSequence


// ============================================================================
// CONFIGURATION AND SOURCE REQUEST
// ============================================================================

int chartSeconds = timeframe.in_seconds()
bool chartSupported = chart.is_standard and timeframe.isminutes and
     chartSeconds >= 60 and chartSeconds <= 3600
bool logicalRunIdValid = safeIdentity(logicalRunIdInput)
bool attemptIdValid = safeIdentity(attemptIdInput)
bool pineHashSafe = safeIdentity(pineSettingsHashInput)
bool pineHashLengthValid = str.length(pineSettingsHashInput) == 64
bool identityValid = logicalRunIdValid and attemptIdValid and
     pineHashSafe and pineHashLengthValid
bool measurementWindowValid = measurementStartInput < measurementEndInput
bool manualEndpointsAligned = manualStartInput % MINUTE_MS == 0 and
     manualEndInput % MINUTE_MS == 0
bool manualWindowValid = manualStartInput < manualEndInput and
     manualEndInput <= measurementStartInput and
     manualEndpointsAligned and
     int((manualEndInput - manualStartInput) / MINUTE_MS) == manualExpectedMinutesInput
bool configurationValid = identityValid and measurementWindowValid and manualWindowValid

array<int> sourceTimes = na
array<footprint> sourceFootprints = na
array<bool> sourceRthFlags = na
array<int> sourceDayKeys = na

if chartSupported
    [requestedTimes, requestedFootprints, requestedRthFlags, requestedDayKeys] =
         request.security_lower_tf(
             syminfo.tickerid,
             SOURCE_TIMEFRAME,
             oneMinutePayload(),
             ignore_invalid_timeframe = true)
    sourceTimes := requestedTimes
    sourceFootprints := requestedFootprints
    sourceRthFlags := requestedRthFlags
    sourceDayKeys := requestedDayKeys


// ============================================================================
// ORDERED SOURCE FINGERPRINT
// ============================================================================

var int chartSourceCount = 0
var int chartSourceFingerprint = HASH_SEED
var int chartSourceFirstTime = na
var int chartSourceLastTime = na

if barstate.isconfirmed and time >= measurementStartInput and time < measurementEndInput
    chartSourceCount += 1
    chartSourceFirstTime := na(chartSourceFirstTime) ? time : chartSourceFirstTime
    chartSourceLastTime := time
    chartSourceFingerprint := hashFold(chartSourceFingerprint, time)
    chartSourceFingerprint := hashFold(chartSourceFingerprint, priceToTicks(open))
    chartSourceFingerprint := hashFold(chartSourceFingerprint, priceToTicks(high))
    chartSourceFingerprint := hashFold(chartSourceFingerprint, priceToTicks(low))
    chartSourceFingerprint := hashFold(chartSourceFingerprint, priceToTicks(close))
    chartSourceFingerprint := hashFold(chartSourceFingerprint, volumeToFingerprintInt(volume))


// ============================================================================
// PROFILE STATE
// ============================================================================

var map<int, float> rthBins = map.new<int, float>()
var bool rthActive = false
var int rthDayKey = na
var int rthExpectedStart = na
var int rthExpectedEndExclusive = na
var int rthFirstTime = na
var int rthLastTime = na
var int rthSourceCount = 0
var int rthRowCount = 0
var int rthMissingFootprints = 0
var int rthGapCount = 0
var int rthMinBin = na
var int rthMaxBin = na
var float rthTotalVolume = 0.0
var int rthSourceFingerprint = HASH_SEED
var bool rthSeenOpen = false
var bool rthSeenClose = false
var bool rthPrimitiveIssue = false
var bool rthOverflow = false
var bool rthReconciliationIssue = false

var bool priorRthReady = false
var bool priorRthComplete = false
var int priorRthDayKey = na
var int priorRthStart = na
var int priorRthEndExclusive = na
var int priorRthPocTicks = na
var int priorRthVahTicks = na
var int priorRthValTicks = na
var float priorRthTotalVolume = na
var float priorRthAchievedPercent = na
var float priorRthPocVolume = na
var int priorRthSourceCount = 0
var int priorRthRowCount = 0
var int priorRthSourceFingerprint = na

var int latestRthCandidateDayKey = na
var bool latestRthCandidateComplete = false
var int latestRthCandidateMissing = 0
var bool latestRthCandidatePrimitiveIssue = false
var bool latestRthCandidateOverflow = false
var bool latestRthCandidateReconciliationIssue = false
var int discardedRthCandidates = 0

var map<int, float> manualBins = map.new<int, float>()
var bool manualFrozen = false
var bool manualComplete = false
var int manualFirstTime = na
var int manualLastTime = na
var int manualSourceCount = 0
var int manualRowCount = 0
var int manualMissingFootprints = 0
var int manualGapCount = 0
var int manualMinBin = na
var int manualMaxBin = na
var float manualTotalVolume = 0.0
var int manualSourceFingerprint = HASH_SEED
var bool manualPrimitiveIssue = false
var bool manualOverflow = false
var bool manualReconciliationIssue = false
var int manualPocTicks = na
var int manualVahTicks = na
var int manualValTicks = na
var float manualAchievedPercent = na
var float manualPocVolume = na

var bool sourceArrayIssue = false
var bool sourceOrderIssue = false
var int previousSourceTime = na
var bool sawRelevantSourceMinute = false


// ============================================================================
// CONFIRMED ONE-MINUTE SOURCE PROCESSING
// ============================================================================

if chartSupported and barstate.isconfirmed
    int timeCount = sourceTimes.size()
    int footprintCount = sourceFootprints.size()
    int rthFlagCount = sourceRthFlags.size()
    int dayKeyCount = sourceDayKeys.size()
    bool lengthsMatch = timeCount == footprintCount and
     timeCount == rthFlagCount and timeCount == dayKeyCount
    sourceArrayIssue := sourceArrayIssue or not lengthsMatch

    int safeCount = math.min(
         math.min(timeCount, footprintCount),
         math.min(rthFlagCount, dayKeyCount))

    if safeCount > 0
        for sourceIndex = 0 to safeCount - 1
            int sourceTime = sourceTimes.get(sourceIndex)
            footprint sourceFootprint = sourceFootprints.get(sourceIndex)
            bool sourceInRth = sourceRthFlags.get(sourceIndex)
            int sourceDayKey = sourceDayKeys.get(sourceIndex)

            if not na(previousSourceTime) and sourceTime <= previousSourceTime
                sourceOrderIssue := true
            previousSourceTime := sourceTime

            int sourceHour = hour(sourceTime, RTH_TIMEZONE)
            int sourceMinute = minute(sourceTime, RTH_TIMEZONE)
            bool isRthOpenMinute = sourceInRth and sourceHour == 8 and sourceMinute == 30
            bool isRthCloseMinute = sourceInRth and sourceHour == 15 and sourceMinute == 14

            // If a later source minute arrives while the previous RTH candidate
            // is still open, the exact 15:14 close marker was unavailable.
            if rthActive and sourceTime >= rthExpectedEndExclusive
                latestRthCandidateDayKey := rthDayKey
                latestRthCandidateComplete := false
                latestRthCandidateMissing := rthMissingFootprints
                latestRthCandidatePrimitiveIssue := rthPrimitiveIssue
                latestRthCandidateOverflow := rthOverflow
                latestRthCandidateReconciliationIssue := rthReconciliationIssue
                discardedRthCandidates += 1
                rthActive := false

            if sourceInRth and sourceTime < measurementStartInput and
             (not rthActive or sourceDayKey != rthDayKey)
                rthBins.clear()
                rthActive := true
                rthDayKey := sourceDayKey
                rthExpectedStart := timestamp(
                     RTH_TIMEZONE,
                     year(sourceTime, RTH_TIMEZONE),
                     month(sourceTime, RTH_TIMEZONE),
                     dayofmonth(sourceTime, RTH_TIMEZONE),
                     8,
                     30)
                rthExpectedEndExclusive := rthExpectedStart + RTH_EXPECTED_MINUTES * MINUTE_MS
                rthFirstTime := na
                rthLastTime := na
                rthSourceCount := 0
                rthRowCount := 0
                rthMissingFootprints := 0
                rthGapCount := 0
                rthMinBin := na
                rthMaxBin := na
                rthTotalVolume := 0.0
                rthSourceFingerprint := HASH_SEED
                rthSeenOpen := false
                rthSeenClose := false
                rthPrimitiveIssue := false
                rthOverflow := false
                rthReconciliationIssue := false

            bool useForRth = sourceInRth and sourceTime < measurementStartInput and
             rthActive and sourceDayKey == rthDayKey
            // Profile construction depends only on the range contract. Identity
            // defects still abort the run, but must not erase valid source
            // evidence or disguise themselves as a zero-observation range.
            bool useForManual = manualWindowValid and not manualFrozen and
             sourceTime >= manualStartInput and sourceTime < manualEndInput
            bool freezeManualNow = manualWindowValid and not manualFrozen and
             sourceTime >= manualEndInput

            if useForRth
                rthSeenOpen := rthSeenOpen or isRthOpenMinute
                rthSeenClose := rthSeenClose or isRthCloseMinute
                rthFirstTime := rthSourceCount == 0 ? sourceTime : rthFirstTime
                if rthSourceCount > 0 and sourceTime != rthLastTime + MINUTE_MS
                    rthGapCount += 1
                rthLastTime := sourceTime
                rthSourceCount += 1

            if useForManual
                manualFirstTime := manualSourceCount == 0 ? sourceTime : manualFirstTime
                if manualSourceCount > 0 and sourceTime != manualLastTime + MINUTE_MS
                    manualGapCount += 1
                manualLastTime := sourceTime
                manualSourceCount += 1
                freezeManualNow := sourceTime + MINUTE_MS >= manualEndInput

            if useForRth or useForManual
                sawRelevantSourceMinute := true
                bool footprintAvailable = not na(sourceFootprint)
                int minuteFingerprint = hashFold(HASH_SEED, sourceTime)
                int minuteRowCount = 0
                bool minutePrimitiveIssue = false
                bool minuteReconciliationIssue = false

                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)
                        int rowVolumeFingerprint = volumeToFingerprintInt(rowVolume)
                        minuteFingerprint := hashFold(minuteFingerprint, downTicks)
                        minuteFingerprint := hashFold(minuteFingerprint, upTicks)
                        minuteFingerprint := hashFold(minuteFingerprint, rowVolumeFingerprint)

                        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
                        minutePrimitiveIssue := minutePrimitiveIssue 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)))

                            if useForRth
                                [nextRthMin, nextRthMax, rthPutOverflow] =
                                     boundedPut(rthBins, outputBin, rowVolume, rthMinBin, rthMaxBin)
                                rthMinBin := nextRthMin
                                rthMaxBin := nextRthMax
                                rthOverflow := rthOverflow or rthPutOverflow
                                rthTotalVolume += rowVolume

                            if useForManual
                                [nextManualMin, nextManualMax, manualPutOverflow] =
                                     boundedPut(
                                         manualBins,
                                         outputBin,
                                         rowVolume,
                                         manualMinBin,
                                         manualMaxBin)
                                manualMinBin := nextManualMin
                                manualMaxBin := nextManualMax
                                manualOverflow := manualOverflow or manualPutOverflow
                                manualTotalVolume += rowVolume

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

                if useForRth
                    rthSourceFingerprint := hashFold(rthSourceFingerprint, minuteFingerprint)
                    rthRowCount += minuteRowCount
                    rthMissingFootprints += footprintAvailable ? 0 : 1
                    rthPrimitiveIssue := rthPrimitiveIssue or minutePrimitiveIssue
                    rthReconciliationIssue := rthReconciliationIssue or minuteReconciliationIssue

                if useForManual
                    manualSourceFingerprint := hashFold(manualSourceFingerprint, minuteFingerprint)
                    manualRowCount += minuteRowCount
                    manualMissingFootprints += footprintAvailable ? 0 : 1
                    manualPrimitiveIssue := manualPrimitiveIssue or minutePrimitiveIssue
                    manualReconciliationIssue := manualReconciliationIssue or minuteReconciliationIssue

            if useForRth and isRthCloseMinute
                bool rthBaseComplete = rthSeenOpen and rthSeenClose and
                     rthFirstTime == rthExpectedStart and
                     rthLastTime + MINUTE_MS == rthExpectedEndExclusive and
                     rthSourceCount == RTH_EXPECTED_MINUTES and
                     rthGapCount == 0 and rthMissingFootprints == 0 and
                     not rthPrimitiveIssue and not rthOverflow and
                     not rthReconciliationIssue
                [rthSnapshotComplete, rthPocTicks, rthVahTicks, rthValTicks,
                 rthAchievedPercent, rthPocVolume] =
                     freezeProfile(
                         rthBins,
                         rthMinBin,
                         rthMaxBin,
                         rthTotalVolume,
                         rthBaseComplete)

                latestRthCandidateDayKey := rthDayKey
                latestRthCandidateComplete := rthSnapshotComplete
                latestRthCandidateMissing := rthMissingFootprints
                latestRthCandidatePrimitiveIssue := rthPrimitiveIssue
                latestRthCandidateOverflow := rthOverflow
                latestRthCandidateReconciliationIssue := rthReconciliationIssue

                if rthSnapshotComplete
                    priorRthReady := true
                    priorRthComplete := true
                    priorRthDayKey := rthDayKey
                    priorRthStart := rthExpectedStart
                    priorRthEndExclusive := rthExpectedEndExclusive
                    priorRthPocTicks := rthPocTicks
                    priorRthVahTicks := rthVahTicks
                    priorRthValTicks := rthValTicks
                    priorRthTotalVolume := rthTotalVolume
                    priorRthAchievedPercent := rthAchievedPercent
                    priorRthPocVolume := rthPocVolume
                    priorRthSourceCount := rthSourceCount
                    priorRthRowCount := rthRowCount
                    priorRthSourceFingerprint := rthSourceFingerprint
                else
                    discardedRthCandidates += 1
                rthActive := false

            if freezeManualNow and not manualFrozen
                bool manualBaseComplete = manualWindowValid and
                     manualFirstTime == manualStartInput and
                     manualLastTime + MINUTE_MS == manualEndInput and
                     manualSourceCount == manualExpectedMinutesInput and
                     manualGapCount == 0 and manualMissingFootprints == 0 and
                     not manualPrimitiveIssue and not manualOverflow and
                     not manualReconciliationIssue
                [manualSnapshotComplete, manualSnapshotPocTicks, manualSnapshotVahTicks,
                 manualSnapshotValTicks, manualSnapshotAchievedPercent,
                 manualSnapshotPocVolume] =
                     freezeProfile(
                         manualBins,
                         manualMinBin,
                         manualMaxBin,
                         manualTotalVolume,
                         manualBaseComplete)
                manualFrozen := true
                manualComplete := manualSnapshotComplete
                manualPocTicks := manualSnapshotPocTicks
                manualVahTicks := manualSnapshotVahTicks
                manualValTicks := manualSnapshotValTicks
                manualAchievedPercent := manualSnapshotAchievedPercent
                manualPocVolume := manualSnapshotPocVolume


// ============================================================================
// TERMINAL RECONCILIATION AND STRUCTURED LOGS
// ============================================================================

var bool terminalEmitted = false
var int terminalErrorMask = na
var int terminalChartCount = na
var int terminalChartFingerprint = na
var int terminalProfileCount = na
var int terminalProfileFingerprint = na
var int terminalSequence = na

if barstate.islastconfirmedhistory and not terminalEmitted
    // A candidate whose expected close is behind the confirmed chart endpoint
    // but never supplied the exact 15:14 minute is an incomplete normal RTH.
    if rthActive and time_close >= rthExpectedEndExclusive
        latestRthCandidateDayKey := rthDayKey
        latestRthCandidateComplete := false
        latestRthCandidateMissing := rthMissingFootprints
        latestRthCandidatePrimitiveIssue := rthPrimitiveIssue
        latestRthCandidateOverflow := rthOverflow
        latestRthCandidateReconciliationIssue := rthReconciliationIssue
        discardedRthCandidates += 1
        rthActive := false

    if not manualFrozen
        manualFrozen := true
        manualComplete := false

    bool rthTerminalComplete = priorRthReady and priorRthComplete and
         (na(latestRthCandidateDayKey) or latestRthCandidateComplete)
    bool anyRequiredFootprintUnavailable = not sawRelevantSourceMinute or
         manualMissingFootprints > 0 or latestRthCandidateMissing > 0
    bool anyPrimitiveIssue = manualPrimitiveIssue or
         latestRthCandidatePrimitiveIssue
    bool anyOverflow = manualOverflow or latestRthCandidateOverflow
    bool anyReconciliationIssue = manualReconciliationIssue or
         latestRthCandidateReconciliationIssue

    int errorMask = 0
    errorMask += chartSupported ? 0 : ERR_UNSUPPORTED_CHART
    errorMask += anyRequiredFootprintUnavailable ? ERR_FOOTPRINT_UNAVAILABLE : 0
    errorMask += manualComplete ? 0 : ERR_MANUAL_INCOMPLETE
    errorMask += rthTerminalComplete ? 0 : ERR_RTH_INCOMPLETE
    errorMask += anyPrimitiveIssue ? ERR_PRIMITIVE_ROW : 0
    errorMask += anyOverflow ? ERR_BIN_CAP : 0
    errorMask += anyReconciliationIssue ? ERR_VOLUME_RECONCILIATION : 0
    errorMask += configurationValid ? 0 : ERR_INVALID_CONFIG
    errorMask += sourceArrayIssue or sourceOrderIssue ? ERR_SOURCE_ARRAYS : 0

    int profileSourceCount = priorRthSourceCount + manualSourceCount
    int profileSourceFingerprint = HASH_SEED
    if priorRthReady
        profileSourceFingerprint := hashFold(profileSourceFingerprint, 1)
        profileSourceFingerprint := hashFold(profileSourceFingerprint, priorRthSourceCount)
        profileSourceFingerprint := hashFold(profileSourceFingerprint, priorRthRowCount)
        profileSourceFingerprint := hashFold(profileSourceFingerprint, priorRthSourceFingerprint)
    if manualSourceCount > 0
        profileSourceFingerprint := hashFold(profileSourceFingerprint, 2)
        profileSourceFingerprint := hashFold(profileSourceFingerprint, manualSourceCount)
        profileSourceFingerprint := hashFold(profileSourceFingerprint, manualRowCount)
        profileSourceFingerprint := hashFold(profileSourceFingerprint, manualSourceFingerprint)

    terminalErrorMask := errorMask
    terminalChartCount := chartSourceCount
    terminalChartFingerprint := chartSourceFingerprint
    terminalProfileCount := profileSourceCount
    terminalProfileFingerprint := profileSourceFingerprint

    int sequence = 0
    sequence := emitInfo(
         sequence,
         "RUN_BEGIN",
         "|logical_run_token=" + str.tostring(logicalRunTokenInput) +
         "|pine_settings_hash=" + pineSettingsHashInput +
         "|script=" + SCRIPT_VERSION +
         "|symbol=" + syminfo.tickerid +
         "|chart_timeframe=" + timeframe.period +
         "|chart_standard=" + boolText(chart.is_standard) +
         "|chart_session=" + syminfo.session +
         "|identity_valid=" + boolText(identityValid) +
         "|logical_run_id_valid=" + boolText(logicalRunIdValid) +
         "|attempt_id_valid=" + boolText(attemptIdValid) +
         "|pine_hash_safe=" + boolText(pineHashSafe) +
         "|pine_hash_length_valid=" + boolText(pineHashLengthValid) +
         "|measurement_window_valid=" + boolText(measurementWindowValid) +
         "|manual_window_valid=" + boolText(manualWindowValid) +
         "|pine_hash_length=" + str.tostring(str.length(pineSettingsHashInput)) +
         "|source_timeframe=" + SOURCE_TIMEFRAME +
         "|window_start_utc=" + str.tostring(measurementStartInput) +
         "|window_end_utc=" + str.tostring(measurementEndInput))

    // CONFIG keys are emitted in strict lexical order.
    sequence := emitInfo(sequence, "CONFIG", "|key=grid_origin_ticks|value=" + str.tostring(GRID_ORIGIN_TICKS))
    sequence := emitInfo(sequence, "CONFIG", "|key=manual_end_ms|value=" + str.tostring(manualEndInput))
    sequence := emitInfo(sequence, "CONFIG", "|key=manual_expected_minutes|value=" + str.tostring(manualExpectedMinutesInput))
    sequence := emitInfo(sequence, "CONFIG", "|key=manual_start_ms|value=" + str.tostring(manualStartInput))
    sequence := emitInfo(sequence, "CONFIG", "|key=max_profile_bins|value=" + str.tostring(MAX_PROFILE_BINS))
    sequence := emitInfo(sequence, "CONFIG", "|key=measurement_end_ms|value=" + str.tostring(measurementEndInput))
    sequence := emitInfo(sequence, "CONFIG", "|key=measurement_start_ms|value=" + str.tostring(measurementStartInput))
    sequence := emitInfo(sequence, "CONFIG", "|key=output_ticks|value=" + str.tostring(outputTicksInput))
    sequence := emitInfo(sequence, "CONFIG", "|key=poc_algorithm|value=" + POC_ALGORITHM)
    sequence := emitInfo(sequence, "CONFIG", "|key=primitive_ticks|value=" + str.tostring(PRIMITIVE_TICKS))
    sequence := emitInfo(sequence, "CONFIG", "|key=prior_rth_selection|value=" + PRIOR_RTH_SELECTION)
    sequence := emitInfo(sequence, "CONFIG", "|key=profile_fp_algo|value=" + FP_ALGORITHM)
    sequence := emitInfo(sequence, "CONFIG", "|key=row_mapping_algorithm|value=" + ROW_MAPPING_ALGORITHM)
    sequence := emitInfo(sequence, "CONFIG", "|key=rth_expected_minutes|value=" + str.tostring(RTH_EXPECTED_MINUTES))
    sequence := emitInfo(sequence, "CONFIG", "|key=rth_session|value=" + RTH_SESSION)
    sequence := emitInfo(sequence, "CONFIG", "|key=rth_timezone|value=" + RTH_TIMEZONE)
    sequence := emitInfo(sequence, "CONFIG", "|key=source_timeframe|value=" + SOURCE_TIMEFRAME)
    sequence := emitInfo(sequence, "CONFIG", "|key=value_area_algorithm|value=" + VA_ALGORITHM)
    sequence := emitInfo(sequence, "CONFIG", "|key=value_area_percent|value=" + floatText(valueAreaPercentInput))
    sequence := emitInfo(sequence, "CONFIG", "|key=volume_scale|value=" + str.tostring(VOLUME_SCALE))

    string rthProfileId = priorRthReady ?
         "RTH:" + str.tostring(priorRthDayKey) + ":B" + str.tostring(outputTicksInput) :
         "RTH:NONE:B" + str.tostring(outputTicksInput)
    string rthProfileFields =
         "|profile_id=" + rthProfileId +
         "|family=PRIOR_RTH" +
         "|effective_start_ms=" + str.tostring(measurementStartInput) +
         "|effective_end_ms=" + str.tostring(measurementEndInput) +
         "|source_start_ms=" + intText(priorRthStart) +
         "|source_end_ms=" + intText(priorRthEndExclusive) +
         "|poc_ticks=" + intText(priorRthPocTicks) +
         "|vah_ticks=" + intText(priorRthVahTicks) +
         "|val_ticks=" + intText(priorRthValTicks) +
         "|complete=" + boolText(priorRthReady and priorRthComplete) +
         "|expected_source_count=" + str.tostring(RTH_EXPECTED_MINUTES) +
         "|source_count=" + str.tostring(priorRthSourceCount) +
         "|row_count=" + str.tostring(priorRthRowCount) +
         "|source_fp=" + intText(priorRthSourceFingerprint) +
         "|gap_count=" + str.tostring(rthGapCount) +
         "|missing_footprints=" + str.tostring(latestRthCandidateMissing) +
         "|primitive_issue=" + boolText(latestRthCandidatePrimitiveIssue) +
         "|bin_overflow=" + boolText(latestRthCandidateOverflow) +
         "|reconciliation_issue=" + boolText(latestRthCandidateReconciliationIssue) +
         "|total_volume=" + floatText(priorRthTotalVolume) +
         "|achieved_va_percent=" + floatText(priorRthAchievedPercent) +
         "|poc_volume=" + floatText(priorRthPocVolume) +
         "|discarded_candidates=" + str.tostring(discardedRthCandidates)
    sequence := priorRthReady and priorRthComplete ?
         emitInfo(sequence, "PROFILE", rthProfileFields) :
         emitWarning(sequence, "PROFILE", rthProfileFields)

    string manualProfileId =
         "MAN:" + str.tostring(manualStartInput) + ":" +
         str.tostring(manualEndInput) + ":B" + str.tostring(outputTicksInput)
    string manualProfileFields =
         "|profile_id=" + manualProfileId +
         "|family=MANUAL" +
         "|effective_start_ms=" + str.tostring(math.max(manualEndInput, measurementStartInput)) +
         "|effective_end_ms=" + str.tostring(measurementEndInput) +
         "|source_start_ms=" + str.tostring(manualStartInput) +
         "|source_end_ms=" + str.tostring(manualEndInput) +
         "|poc_ticks=" + intText(manualPocTicks) +
         "|vah_ticks=" + intText(manualVahTicks) +
         "|val_ticks=" + intText(manualValTicks) +
         "|complete=" + boolText(manualComplete) +
         "|actual_first_ms=" + intText(manualFirstTime) +
         "|actual_last_ms=" + intText(manualLastTime) +
         "|expected_source_count=" + str.tostring(manualExpectedMinutesInput) +
         "|source_count=" + str.tostring(manualSourceCount) +
         "|row_count=" + str.tostring(manualRowCount) +
         "|source_fp=" + str.tostring(manualSourceFingerprint) +
         "|gap_count=" + str.tostring(manualGapCount) +
         "|missing_footprints=" + str.tostring(manualMissingFootprints) +
         "|primitive_issue=" + boolText(manualPrimitiveIssue) +
         "|bin_overflow=" + boolText(manualOverflow) +
         "|reconciliation_issue=" + boolText(manualReconciliationIssue) +
         "|total_volume=" + floatText(manualTotalVolume) +
         "|achieved_va_percent=" + floatText(manualAchievedPercent) +
         "|poc_volume=" + floatText(manualPocVolume)
    sequence := manualComplete ?
         emitInfo(sequence, "PROFILE", manualProfileFields) :
         emitWarning(sequence, "PROFILE", manualProfileFields)

    int warningCount = (priorRthReady and priorRthComplete ? 0 : 1) +
         (manualComplete ? 0 : 1)
    int errorCount = errorMask == 0 ? 0 : 1
    int terminalMaxSequence = sequence + 1
    string terminalFields =
         "|profiles=2" +
         "|events=0" +
         "|warnings=" + str.tostring(warningCount) +
         "|errors=" + str.tostring(errorCount) +
         "|overflow=" + boolText(anyOverflow) +
         "|max_seq=" + str.tostring(terminalMaxSequence) +
         "|fp_algo=" + FP_ALGORITHM +
         "|first_utc=" + intText(chartSourceFirstTime) +
         "|last_utc=" + intText(chartSourceLastTime) +
         "|chart_count=" + str.tostring(chartSourceCount) +
         "|chart_fp=" + str.tostring(chartSourceFingerprint) +
         "|profile_count=" + str.tostring(profileSourceCount) +
         "|profile_fp=" + str.tostring(profileSourceFingerprint) +
         "|error_mask=" + str.tostring(errorMask)

    if errorMask == 0
        sequence := emitInfo(sequence, "RUN_END", terminalFields)
    else
        sequence := emitError(
             sequence,
             "RUN_ABORT",
             "|code=ERROR_MASK_" + str.tostring(errorMask) + terminalFields)

    terminalSequence := sequence
    terminalEmitted := true


// ============================================================================
// DATA-WINDOW-ONLY, GLOBAL, CSV-EXPORTABLE OUTPUTS
// ============================================================================

bool terminalBar = terminalEmitted and barstate.islastconfirmedhistory
bool measurementBar = time >= measurementStartInput and time < measurementEndInput

plot(float(logicalRunTokenInput), "VPX.Run", display = display.data_window)
plot(float(attemptTokenInput), "VPX.Attempt", display = display.data_window)
plot(float(configTokenInput), "VPX.Config", display = display.data_window)
plot(terminalBar ? 1.0 : 0.0, "VPX.Terminal", display = display.data_window)

plot(measurementBar and priorRthReady ? float(priorRthPocTicks) : na, "VPX.PriorRTH.POC", display = display.data_window)
plot(measurementBar and priorRthReady ? float(priorRthVahTicks) : na, "VPX.PriorRTH.VAH", display = display.data_window)
plot(measurementBar and priorRthReady ? float(priorRthValTicks) : na, "VPX.PriorRTH.VAL", display = display.data_window)
plot(priorRthReady ? 1.0 : 0.0, "VPX.PriorRTH.Ready", display = display.data_window)
plot(priorRthComplete ? 1.0 : 0.0, "VPX.PriorRTH.Complete", display = display.data_window)
plot(priorRthReady ? float(priorRthSourceCount) : na, "VPX.PriorRTH.SourceCount", display = display.data_window)
plot(priorRthReady ? float(priorRthSourceFingerprint) : na, "VPX.PriorRTH.SourceFP", display = display.data_window)

plot(measurementBar and manualComplete ? float(manualPocTicks) : na, "VPX.Manual.POC", display = display.data_window)
plot(measurementBar and manualComplete ? float(manualVahTicks) : na, "VPX.Manual.VAH", display = display.data_window)
plot(measurementBar and manualComplete ? float(manualValTicks) : na, "VPX.Manual.VAL", display = display.data_window)
plot(manualFrozen ? 1.0 : 0.0, "VPX.Manual.Ready", display = display.data_window)
plot(manualComplete ? 1.0 : 0.0, "VPX.Manual.Complete", display = display.data_window)
plot(manualFrozen ? float(manualSourceCount) : na, "VPX.Manual.SourceCount", display = display.data_window)
plot(manualFrozen ? float(manualSourceFingerprint) : na, "VPX.Manual.SourceFP", display = display.data_window)

plot(terminalBar ? float(terminalChartCount) : na, "VPX.ChartCount", display = display.data_window)
plot(terminalBar ? float(terminalChartFingerprint) : na, "VPX.ChartFP", display = display.data_window)
plot(terminalBar ? float(terminalProfileCount) : na, "VPX.ProfileCount", display = display.data_window)
plot(terminalBar ? float(terminalProfileFingerprint) : na, "VPX.ProfileFP", display = display.data_window)
plot(terminalBar ? float(terminalErrorMask) : 0.0, "VPX.ErrorMask", display = display.data_window)
plot(terminalBar ? float(terminalSequence) : na, "VPX.MaxSeq", display = display.data_window)

Overview

Volume Profile Phase 0 Probe deliberately features no chart plots, signals, alerts, or visual drawings. The script computes internal metrics such as Point of Control and Value Area boundaries across specified measurement windows and manual time ranges. User settings allow adjustments to measurement window timestamps, execution run tokens, expected bar counts, value area percentage targets, and output tick sizes.

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

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
Logical run IDstringvp_phase0_logical_001logicalRunIdInput
Logical run tokenint1logicalRunTokenInput
Attempt IDstringattempt_001attemptIdInput
Attempt tokenint1attemptTokenInput
Configuration tokenint1configTokenInput
Expected Pine-settings hashstringUNSETpineSettingsHashInput
Measurement starttimetimestamp("24 Jul 2026 13:30 +0000")measurementStartInput
Measurement endtimetimestamp("24 Jul 2026 20:15 +0000")measurementEndInput
Manual starttimetimestamp("23 Jul 2026 13:30 +0000")manualStartInput
Manual endtimetimestamp("23 Jul 2026 20:15 +0000")manualEndInput
Expected one-minute barsint405manualExpectedMinutesInput
Output-row ticksint4outputTicksInput
Show all 13 settings
SettingTypeDefaultVariable
Value Area target (%)float70.0valueAreaPercentInput
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