Volume_Profile_Phase0_Probe_v0.1.0.pine
Copy
// 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)
Show all 925 lines