VP_RSD_Automatic_Local_Node_Edge_Consensus_Diagnostic_v0.6.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
// RESEARCH DIAGNOSTIC ONLY.
//
// This successor joins the two previously separate research components:
//
// 1. the confirmed RSD v1.7 Macro broadening-wall selector; and
// 2. the v0.5.1 local paired-node consensus detector.
//
// The newest eligible Macro wall automatically supplies the immutable profile
// interval. Its same-side Far/Prior pivot is the start and its Outer pivot is
// the included end. Macro candle timestamps are refined to the exact 15-minute
// bars that printed the pivot prices. If either endpoint cannot be refined, the
// indicator abstains. No manual dates are required.
//
// The RSD adapter intentionally matches the v1.7 swing path with HTF confluence
// OFF. It uses confirmed pivots only, the pullback-tolerant two-sided wall gate,
// the Far-versus-Prior containment rule, and the existing steep-wall filter.
// The filter's ATR snapshot is latched when the confirmed Macro pivot tuple
// changes so an already-selected profile cannot churn on ordinary ATR drift.
// When both walls qualify, the wall with the newest endpoint wins; an equal
// endpoint prefers the longer span.
//
// Every retained profile boundary keeps the direction of its dense side.
// Starting at that anchor, each grid scans outward on the dense side and accepts
// the nearest material local maximum in opposite-direction clean strength that
// encloses a local volume bulge. A node is drawn only when the base-grid pair is
// complete and at least one neighboring grid agrees on both endpoints.
//
// fine = round(base row ticks * 0.8)
// base = base row ticks
// coarse = round(base row ticks * 1.2)
//
// Each profile allocates every source bar's total volume by exact high/low tick
// overlap. The existing provisional local log-jump gates are applied to each
// grid independently. Anchor clustering keeps its configurable radius, while
// mate endpoints use the stricter fixed radius of one base row. Neighbor grids
// are evaluated pair-aware instead of inheriting the preselected standalone
// neighbor. Stable pairs rank by grid support, worst cross-grid node prominence,
// then the minimum anchor z score. Unpaired consensus anchors remain eligible
// as standalones. A selected pair consumes consensus anchors inside the node or
// within one consensus radius of either edge.
//
// Optional structural bounds can suppress otherwise-valid consensus shelves
// that are too close to the RSD range low or high. They are suppression inputs,
// not additional evidence.
//
// Source and endpoint contract:
// standard 15-minute chart OHLCV only;
// [Start 15m bar time, End 15m bar time + 15 minutes)
//
// This script does not create entries, exits, or support/resistance claims.
// It tests whether both edges of a volume node survive row-size perturbation.
indicator(
"VP RSD Automatic Local Node Edge Consensus Diagnostic v0.6.0",
shorttitle = "VP RSD AUTO 0.6",
overlay = true,
max_bars_back = 5000,
max_lines_count = 500,
max_boxes_count = 10,
max_labels_count = 100)
// ============================================================================
// INPUTS
// ============================================================================
string rangeGroup = "1. Automatic RSD Macro range"
string macroTFInput = input.string(
"240",
"Macro timeframe",
options = ["240", "60"],
group = rangeGroup,
tooltip = "Use 240 to match the normal locked 4-hour Macro structure.")
bool useMultiNInput = input.bool(
false,
"Multi-N swing union",
group = rangeGroup,
tooltip = "Must match RSD. The first comparison protocol keeps this OFF.")
int nFastInput = input.int(
3,
"N fast (left bars)",
minval = 2,
maxval = 10,
group = rangeGroup)
int nMediumInput = input.int(
5,
"N medium (left bars)",
minval = 2,
maxval = 20,
group = rangeGroup)
int rightBarsInput = input.int(
2,
"Right bars (confirmation)",
minval = 1,
maxval = 10,
group = rangeGroup)
bool wallRequireBothSidesInput = input.bool(
true,
"Require two-sided broadening",
group = rangeGroup,
tooltip = "Matches the corresponding RSD wall setting.")
bool hideSteepWallsInput = input.bool(
true,
"Suppress steep walls",
group = rangeGroup)
float maximumWallSlopeInput = input.float(
0.05,
"Maximum wall slope (ATR / 15m bar)",
minval = 0.0,
step = 0.01,
group = rangeGroup,
tooltip = "Uses the RSD steep-wall test with a confirmed ATR snapshot latched to each Macro pivot tuple, keeping the selected profile stable until structure changes.")
string constructionGroup = "2. Multi-resolution construction"
int baseTicksInput = input.int(
25,
"Preferred base ticks per row",
minval = 5,
maxval = 500,
group = constructionGroup,
tooltip = "The other grids are round(base × 0.8) and round(base × 1.2).")
bool widenRowsToCapacityInput = input.bool(
true,
"Automatically widen rows when required",
group = constructionGroup,
tooltip = "Raises the effective base row only when the fine grid would exceed the 400-row safety cap.")
float valueAreaPercentInput = input.float(
70.0,
"Base-grid Value Area target (%)",
minval = 1.0,
maxval = 100.0,
step = 0.5,
group = constructionGroup)
string scoringGroup = "3. Provisional local geometry gates"
int candidateCapPerGridInput = input.int(
12,
"Candidate cap per grid",
minval = 1,
maxval = 20,
group = scoringGroup)
float minimumRatioInput = input.float(
1.75,
"Minimum dense/thin ratio",
minval = 1.0,
step = 0.05,
group = scoringGroup)
float maximumNoiseFractionInput = input.float(
0.50,
"Maximum W / J",
minval = 0.0,
step = 0.05,
group = scoringGroup)
float minimumDenseFractionInput = input.float(
0.20,
"Minimum dense side / grid max row",
minval = 0.0,
maxval = 1.0,
step = 0.05,
group = scoringGroup)
float minimumRobustZInput = input.float(
2.50,
"Minimum robust z",
minval = 0.0,
step = 0.25,
group = scoringGroup)
int localClusterRowsInput = input.int(
2,
"Within-grid cluster radius (grid rows)",
minval = 0,
maxval = 10,
group = scoringGroup)
string consensusGroup = "4. Cross-grid consensus"
float consensusClusterBaseRowsInput = input.float(
1.0,
"Cross-grid cluster radius (base rows)",
minval = 0.25,
maxval = 5.0,
step = 0.25,
group = consensusGroup)
int maximumStructuresInput = input.int(
2,
"Maximum structures",
minval = 1,
maxval = 2,
group = consensusGroup)
float maximumNodeWidthBaseRowsInput = input.float(
12.0,
"Maximum paired-node width (base rows)",
minval = 3.0,
maxval = 50.0,
step = 0.5,
group = consensusGroup,
tooltip = "Local-node guard. A mate farther than this is not paired with the anchor; the anchor may remain a standalone shelf.")
bool extendStructuresInput = input.bool(
true,
"Extend structures to current bar",
group = consensusGroup)
string structureGroup = "5. Optional structural-bound suppression"
bool useStructuralBoundsInput = input.bool(
false,
"Suppress near structural low / high",
group = structureGroup)
float structuralLowInput = input.price(
0.0,
"Structural low",
group = structureGroup)
float structuralHighInput = input.price(
0.0,
"Structural high",
group = structureGroup)
float structuralDistanceBaseRowsInput = input.float(
1.0,
"Suppression distance (base rows)",
minval = 0.0,
maxval = 20.0,
step = 0.25,
group = structureGroup)
string displayGroup = "6. Diagnostic display"
float histogramWidthInput = input.float(
24.0,
"Base histogram width (% of range)",
minval = 5.0,
maxval = 50.0,
group = displayGroup)
color rangeColorInput = input.color(
#26c6da,
"Range / base histogram",
group = displayGroup)
color consensusColorInput = input.color(
#ffd54f,
"Consensus structures",
group = displayGroup)
// ============================================================================
// CONSTANTS AND HELPERS
// ============================================================================
const int MINUTE_MS = 60000
const int PROFILE_BAR_MS = 15 * MINUTE_MS
const int SOURCE_BAR_MS = 15 * MINUTE_MS
const int GRID_ORIGIN_TICKS = 0
const int MAX_PROFILE_BINS = 400
const int MAX_PROFILE_SOURCE_BARS = 2000
const int MAX_ALLOCATION_TOUCHES = 120000
const int SHELF_SIDE_ROWS = 3
const int MIN_NODE_ROWS = 3
const float MIN_MATE_TO_ANCHOR_CLEAN = 0.10
const int MAX_RANKED_DIAGNOSTICS = 60
const int MIN_CONSENSUS_GRIDS = 2
priceToTicks(float price) =>
na(price) ? na : int(math.round(price / syminfo.mintick))
binVolume(map<int, float> bins, int bin) =>
bins.contains(bin) ? bins.get(bin) : 0.0
// Most recent chart bar at or before an HTF candle's opening timestamp.
// Copied semantically from RSD v1.7. The chart contract is fixed at 15m.
htfTimeToBar(int pivotTime) =>
int maxOffset = math.min(bar_index, 4999)
int result = bar_index
if not na(pivotTime) and pivotTime > 0
result := bar_index - maxOffset
for offset = 0 to maxOffset
if time[offset] <= pivotTime
result := bar_index - offset
break
result
// Confirmed RSD v1.7 Macro swing path with HTF confluence OFF.
findMacroPivots() =>
bool highMediumCondition =
not na(ta.pivothigh(
high,
nMediumInput,
rightBarsInput))
bool lowMediumCondition =
not na(ta.pivotlow(
low,
nMediumInput,
rightBarsInput))
float high0Medium = ta.valuewhen(
highMediumCondition,
high[rightBarsInput],
0)[1]
int highTime0Medium = ta.valuewhen(
highMediumCondition,
time[rightBarsInput],
0)[1]
float high1Medium = ta.valuewhen(
highMediumCondition,
high[rightBarsInput],
1)[1]
int highTime1Medium = ta.valuewhen(
highMediumCondition,
time[rightBarsInput],
1)[1]
float high2Medium = ta.valuewhen(
highMediumCondition,
high[rightBarsInput],
2)[1]
int highTime2Medium = ta.valuewhen(
highMediumCondition,
time[rightBarsInput],
2)[1]
float low0Medium = ta.valuewhen(
lowMediumCondition,
low[rightBarsInput],
0)[1]
int lowTime0Medium = ta.valuewhen(
lowMediumCondition,
time[rightBarsInput],
0)[1]
float low1Medium = ta.valuewhen(
lowMediumCondition,
low[rightBarsInput],
1)[1]
int lowTime1Medium = ta.valuewhen(
lowMediumCondition,
time[rightBarsInput],
1)[1]
float low2Medium = ta.valuewhen(
lowMediumCondition,
low[rightBarsInput],
2)[1]
int lowTime2Medium = ta.valuewhen(
lowMediumCondition,
time[rightBarsInput],
2)[1]
bool highFastCondition =
not na(ta.pivothigh(
high,
nFastInput,
rightBarsInput))
bool lowFastCondition =
not na(ta.pivotlow(
low,
nFastInput,
rightBarsInput))
float high0Fast = useMultiNInput ?
ta.valuewhen(
highFastCondition,
high[rightBarsInput],
0)[1] : na
int highTime0Fast = useMultiNInput ?
ta.valuewhen(
highFastCondition,
time[rightBarsInput],
0)[1] : na
float high1Fast = useMultiNInput ?
ta.valuewhen(
highFastCondition,
high[rightBarsInput],
1)[1] : na
int highTime1Fast = useMultiNInput ?
ta.valuewhen(
highFastCondition,
time[rightBarsInput],
1)[1] : na
float high2Fast = useMultiNInput ?
ta.valuewhen(
highFastCondition,
high[rightBarsInput],
2)[1] : na
int highTime2Fast = useMultiNInput ?
ta.valuewhen(
highFastCondition,
time[rightBarsInput],
2)[1] : na
float low0Fast = useMultiNInput ?
ta.valuewhen(
lowFastCondition,
low[rightBarsInput],
0)[1] : na
int lowTime0Fast = useMultiNInput ?
ta.valuewhen(
lowFastCondition,
time[rightBarsInput],
0)[1] : na
float low1Fast = useMultiNInput ?
ta.valuewhen(
lowFastCondition,
low[rightBarsInput],
1)[1] : na
int lowTime1Fast = useMultiNInput ?
ta.valuewhen(
lowFastCondition,
time[rightBarsInput],
1)[1] : na
float low2Fast = useMultiNInput ?
ta.valuewhen(
lowFastCondition,
low[rightBarsInput],
2)[1] : na
int lowTime2Fast = useMultiNInput ?
ta.valuewhen(
lowFastCondition,
time[rightBarsInput],
2)[1] : na
[high0Medium, highTime0Medium,
high1Medium, highTime1Medium,
high2Medium, highTime2Medium,
low0Medium, lowTime0Medium,
low1Medium, lowTime1Medium,
low2Medium, lowTime2Medium,
high0Fast, highTime0Fast,
high1Fast, highTime1Fast,
high2Fast, highTime2Fast,
low0Fast, lowTime0Fast,
low1Fast, lowTime1Fast,
low2Fast, lowTime2Fast]
reduceTopThreeByTime(
float price0,
int time0,
float price1,
int time1,
float price2,
int time2,
float price3,
int time3,
float price4,
int time4,
float price5,
int time5) =>
array<float> prices = array.from(
price0,
price1,
price2,
price3,
price4,
price5)
array<int> times = array.from(
time0,
time1,
time2,
time3,
time4,
time5)
array<float> validPrices =
array.new_float()
array<int> validTimes =
array.new_int()
for index = 0 to 5
float candidatePrice =
prices.get(index)
int candidateTime =
times.get(index)
if not na(candidatePrice) and
not na(candidateTime) and
candidateTime > 0
bool duplicate = false
int validCount =
validTimes.size()
if validCount > 0
for validIndex =
0 to validCount - 1
if math.abs(
candidateTime -
validTimes.get(
validIndex)) <
MINUTE_MS and
math.abs(
candidatePrice -
validPrices.get(
validIndex)) <
syminfo.mintick
duplicate := true
break
if not duplicate
validPrices.push(
candidatePrice)
validTimes.push(
candidateTime)
int sortCount = validTimes.size()
if sortCount > 1
for index = 1 to sortCount - 1
int candidateTime =
validTimes.get(index)
float candidatePrice =
validPrices.get(index)
int insertionIndex = index - 1
while insertionIndex >= 0 and
validTimes.get(
insertionIndex) <
candidateTime
validTimes.set(
insertionIndex + 1,
validTimes.get(
insertionIndex))
validPrices.set(
insertionIndex + 1,
validPrices.get(
insertionIndex))
insertionIndex -= 1
validTimes.set(
insertionIndex + 1,
candidateTime)
validPrices.set(
insertionIndex + 1,
candidatePrice)
float newestPrice = na
int newestTime = na
float priorPrice = na
int priorTime = na
float farPrice = na
int farTime = na
int resultCount = validTimes.size()
if resultCount >= 1
newestPrice := validPrices.get(0)
newestTime := validTimes.get(0)
if resultCount >= 2
priorPrice := validPrices.get(1)
priorTime := validTimes.get(1)
if resultCount >= 3
farPrice := validPrices.get(2)
farTime := validTimes.get(2)
[newestPrice,
newestTime,
priorPrice,
priorTime,
farPrice,
farTime]
median3(float a, float b, float c) =>
a + b + c -
math.min(a, math.min(b, c)) -
math.max(a, math.max(b, c))
mad3(float a, float b, float c, float center) =>
median3(
math.abs(a - center),
math.abs(b - center),
math.abs(c - center))
medianSorted(array<float> values) =>
int count = values.size()
float result = na
if count > 0
int middle = int(math.floor(count / 2.0))
result := count % 2 == 1 ?
values.get(middle) :
math.avg(
values.get(middle - 1),
values.get(middle))
result
timeText(int value) =>
na(value) ? "NA" :
str.format_time(
value,
"yyyy-MM-dd HH:mm",
syminfo.timezone)
floatText(float value) =>
na(value) ? "NA" :
str.tostring(value, "#.########")
clearLines(array<line> drawings) =>
int count = drawings.size()
if count > 0
for index = 0 to count - 1
line.delete(drawings.get(index))
drawings.clear()
clearLabels(array<label> drawings) =>
int count = drawings.size()
if count > 0
for index = 0 to count - 1
label.delete(drawings.get(index))
drawings.clear()
diagnosticRow(
table target,
int row,
string key,
string value,
color background) =>
table.cell(
target,
0,
row,
key,
text_color = color.silver,
text_size = size.tiny,
bgcolor = background,
text_halign = text.align_left)
table.cell(
target,
1,
row,
value,
text_color = color.white,
text_size = size.tiny,
bgcolor = background,
text_halign = text.align_left)
boundedPut(
map<int, float> bins,
int key,
float addedVolume,
int currentMin,
int currentMax) =>
bool alreadyPresent = bins.contains(key)
int candidateMin = na(currentMin) ?
key : math.min(currentMin, key)
int candidateMax = na(currentMax) ?
key : math.max(currentMax, key)
bool overflow =
candidateMax - candidateMin + 1 >
MAX_PROFILE_BINS or
(not alreadyPresent and
bins.size() >= MAX_PROFILE_BINS)
int nextMin = currentMin
int nextMax = currentMax
if not overflow
bins.put(
key,
binVolume(bins, key) + addedVolume)
nextMin := candidateMin
nextMax := candidateMax
[nextMin, nextMax, overflow]
allocateBarToGrid(
map<int, float> bins,
int sourceLow,
int sourceHigh,
float sourceVolume,
int rowTicks,
int currentMin,
int currentMax) =>
int nextMin = currentMin
int nextMax = currentMax
float allocatedTotal = 0.0
bool overflow = false
int touchCount = 0
int sourceTickCount =
sourceHigh - sourceLow + 1
int sourceLowBin = int(math.floor(
(sourceLow - GRID_ORIGIN_TICKS) /
float(rowTicks)))
int sourceHighBin = int(math.floor(
(sourceHigh - GRID_ORIGIN_TICKS) /
float(rowTicks)))
if sourceTickCount > 0
for outputBin =
sourceLowBin to sourceHighBin
int binLowTick =
GRID_ORIGIN_TICKS +
outputBin * rowTicks
int binHighTick =
binLowTick + rowTicks - 1
int overlapLow =
math.max(sourceLow, binLowTick)
int overlapHigh =
math.min(sourceHigh, binHighTick)
int overlapTicks =
math.max(
0,
overlapHigh -
overlapLow + 1)
if overlapTicks > 0
float allocatedVolume =
sourceVolume *
overlapTicks /
sourceTickCount
[putMin,
putMax,
putOverflow] =
boundedPut(
bins,
outputBin,
allocatedVolume,
nextMin,
nextMax)
nextMin := putMin
nextMax := putMax
overflow :=
overflow or putOverflow
allocatedTotal +=
allocatedVolume
touchCount += 1
[nextMin,
nextMax,
allocatedTotal,
overflow,
touchCount]
betterConsensusCombination(
int support,
float minimumZ,
int totalDistance,
int incumbentSupport,
float incumbentMinimumZ,
int incumbentDistance) =>
support > incumbentSupport or
(
support == incumbentSupport and
minimumZ > incumbentMinimumZ) or
(
support == incumbentSupport and
minimumZ == incumbentMinimumZ and
totalDistance < incumbentDistance)
calculateProfile(
map<int, float> bins,
int minBin,
int maxBin,
float totalVolume,
int rowTicks) =>
bool valid =
not na(minBin) and
not na(maxBin) and
maxBin >= minBin and
maxBin - minBin + 1 <= MAX_PROFILE_BINS and
bins.size() > 0 and
totalVolume > 0.0
int pocBin = na
int valueAreaLowBin = na
int valueAreaHighBin = na
float achievedPercent = na
float maximumVolume = na
if valid
float weightedCenter2 = 0.0
for bin = minBin to maxBin
float volumeAtBin = binVolume(bins, bin)
int center2 =
2 * (
GRID_ORIGIN_TICKS +
bin * rowTicks) +
rowTicks
weightedCenter2 +=
volumeAtBin * center2
maximumVolume := na(maximumVolume) ?
volumeAtBin :
math.max(
maximumVolume,
volumeAtBin)
float meanCenter2 =
weightedCenter2 / totalVolume
float bestDistance = na
float tieTolerance = math.max(
0.000000001,
math.abs(maximumVolume) *
0.000000000001)
for bin = minBin to maxBin
float volumeAtBin =
binVolume(bins, bin)
if math.abs(
volumeAtBin -
maximumVolume) <= tieTolerance
int center2 =
2 * (
GRID_ORIGIN_TICKS +
bin * rowTicks) +
rowTicks
float distance =
math.abs(center2 - meanCenter2)
if na(bestDistance) or
distance <
bestDistance - 0.000000001 or
(math.abs(
distance -
bestDistance) <=
0.000000001 and
bin < pocBin)
pocBin := bin
bestDistance := distance
if not na(pocBin)
valueAreaLowBin := pocBin
valueAreaHighBin := pocBin
float includedVolume =
binVolume(bins, pocBin)
float targetVolume =
totalVolume *
valueAreaPercentInput /
100.0
float tolerance = math.max(
0.000000001,
math.abs(totalVolume) *
0.000000000001)
int steps = 0
while includedVolume <
targetVolume - tolerance and
steps < MAX_PROFILE_BINS
bool hasLower =
valueAreaLowBin > minBin
bool hasUpper =
valueAreaHighBin < maxBin
if not hasLower and not hasUpper
break
int lowerCandidate =
valueAreaLowBin - 1
int upperCandidate =
valueAreaHighBin + 1
float lowerVolume = hasLower ?
binVolume(
bins,
lowerCandidate) : na
float upperVolume = hasUpper ?
binVolume(
bins,
upperCandidate) : na
bool chooseUpper = not hasLower
if hasLower and hasUpper
chooseUpper :=
upperVolume >
lowerVolume or
(upperVolume ==
lowerVolume and
math.abs(
upperCandidate -
pocBin) <=
math.abs(
lowerCandidate -
pocBin))
float selectedVolume =
chooseUpper ?
upperVolume : lowerVolume
if includedVolume +
selectedVolume >
targetVolume + tolerance
break
if chooseUpper
valueAreaHighBin :=
upperCandidate
else
valueAreaLowBin :=
lowerCandidate
includedVolume += selectedVolume
steps += 1
achievedPercent :=
includedVolume /
totalVolume *
100.0
[valid and not na(pocBin),
pocBin,
valueAreaLowBin,
valueAreaHighBin,
achievedPercent,
maximumVolume]
scoreGatedCandidates(
map<int, float> bins,
int minBin,
int maxBin,
int rowTicks,
float maximumVolume,
float minimumRatio,
float maximumNoiseFraction,
float minimumDenseFraction,
float minimumRobustZ,
int localClusterRows,
int candidateCap) =>
array<int> rawBins = array.new_int()
array<float> rawJumps = array.new_float()
array<float> rawNoise = array.new_float()
array<float> rawClean = array.new_float()
array<float> rawRatios = array.new_float()
array<float> rawDenseVolumes =
array.new_float()
array<float> rawZ = array.new_float()
array<int> rawDenseDirections =
array.new_int()
array<float> positiveVolumes =
array.new_float()
array<int> selectedTicks = array.new_int()
array<float> selectedClean =
array.new_float()
array<float> selectedZ =
array.new_float()
array<int> selectedDenseDirections =
array.new_int()
float cleanMad = na
if not na(minBin) and
not na(maxBin) and
not na(maximumVolume) and
maximumVolume > 0.0
for bin = minBin to maxBin
float volumeAtBin =
binVolume(bins, bin)
if volumeAtBin > 0.0
positiveVolumes.push(
volumeAtBin)
positiveVolumes.sort(order.ascending)
float logEpsilon =
positiveVolumes.size() > 0 ?
math.max(
medianSorted(
positiveVolumes) *
0.01,
0.000000001) : na
int firstBoundary =
minBin + SHELF_SIDE_ROWS
int lastBoundary =
maxBin - SHELF_SIDE_ROWS + 1
if not na(logEpsilon) and
firstBoundary <= lastBoundary
for boundary =
firstBoundary to lastBoundary
float l0 = math.log(
binVolume(
bins,
boundary - 3) +
logEpsilon)
float l1 = math.log(
binVolume(
bins,
boundary - 2) +
logEpsilon)
float l2 = math.log(
binVolume(
bins,
boundary - 1) +
logEpsilon)
float r0 = math.log(
binVolume(
bins,
boundary) +
logEpsilon)
float r1 = math.log(
binVolume(
bins,
boundary + 1) +
logEpsilon)
float r2 = math.log(
binVolume(
bins,
boundary + 2) +
logEpsilon)
float leftMedian =
median3(l0, l1, l2)
float rightMedian =
median3(r0, r1, r2)
float jump = math.abs(
rightMedian -
leftMedian)
float noise =
mad3(
l0,
l1,
l2,
leftMedian) +
mad3(
r0,
r1,
r2,
rightMedian)
float cleanJump =
math.max(
0.0,
jump - noise)
float denseVolume =
math.max(
0.0,
math.exp(
math.max(
leftMedian,
rightMedian)) -
logEpsilon)
rawBins.push(boundary)
rawJumps.push(jump)
rawNoise.push(noise)
rawClean.push(cleanJump)
rawRatios.push(
math.exp(jump))
rawDenseVolumes.push(
denseVolume)
rawDenseDirections.push(
rightMedian >
leftMedian ? 1 : -1)
if rawClean.size() > 0
array<float> cleanSorted =
rawClean.copy()
cleanSorted.sort(order.ascending)
float cleanMedian =
medianSorted(cleanSorted)
array<float> deviations =
array.new_float()
for index =
0 to rawClean.size() - 1
deviations.push(
math.abs(
rawClean.get(index) -
cleanMedian))
deviations.sort(order.ascending)
cleanMad :=
medianSorted(deviations)
float robustScale =
math.max(
1.4826 * cleanMad,
0.000000001)
for index =
0 to rawClean.size() - 1
rawZ.push(
(
rawClean.get(index) -
cleanMedian) /
robustScale)
array<int> rankedIndices =
array.new_int()
array<bool> consumed =
array.new_bool(
rawClean.size(),
false)
int rankLimit = math.min(
rawClean.size(),
MAX_RANKED_DIAGNOSTICS)
if rankLimit > 0
for rank = 0 to rankLimit - 1
int bestIndex = -1
float bestClean = na
int bestBoundary = na
for index =
0 to rawClean.size() - 1
if not consumed.get(index)
float candidateClean =
rawClean.get(index)
int candidateBoundary =
rawBins.get(index)
if na(bestClean) or
candidateClean > bestClean or
(candidateClean ==
bestClean and
candidateBoundary <
bestBoundary)
bestIndex := index
bestClean :=
candidateClean
bestBoundary :=
candidateBoundary
if bestIndex >= 0
consumed.set(
bestIndex,
true)
rankedIndices.push(
bestIndex)
if rankedIndices.size() > 0
for rankedPosition =
0 to rankedIndices.size() - 1
int index =
rankedIndices.get(
rankedPosition)
bool passes =
rawRatios.get(index) >=
minimumRatio and
rawNoise.get(index) <=
maximumNoiseFraction *
rawJumps.get(index) and
rawDenseVolumes.get(index) >=
minimumDenseFraction *
maximumVolume and
rawZ.get(index) >=
minimumRobustZ
int boundary =
rawBins.get(index)
bool clustered = false
if passes and
selectedTicks.size() > 0
for selectedIndex =
0 to selectedTicks.size() - 1
int selectedBoundary =
int(math.round(
selectedTicks.get(
selectedIndex) /
float(rowTicks)))
if math.abs(
boundary -
selectedBoundary) <=
localClusterRows
clustered := true
break
if passes and
not clustered and
selectedTicks.size() <
candidateCap
selectedTicks.push(
GRID_ORIGIN_TICKS +
boundary * rowTicks)
selectedClean.push(
rawClean.get(index))
selectedZ.push(
rawZ.get(index))
selectedDenseDirections.push(
rawDenseDirections.get(
index))
[selectedTicks,
selectedClean,
selectedZ,
selectedDenseDirections,
rawClean.size(),
cleanMad]
profileLogEpsilon(
map<int, float> bins,
int minBin,
int maxBin) =>
array<float> positiveVolumes =
array.new_float()
if not na(minBin) and
not na(maxBin) and
minBin <= maxBin
for bin = minBin to maxBin
float volumeAtBin =
binVolume(bins, bin)
if volumeAtBin > 0.0
positiveVolumes.push(
volumeAtBin)
positiveVolumes.sort(order.ascending)
positiveVolumes.size() > 0 ?
math.max(
medianSorted(
positiveVolumes) *
0.01,
0.000000001) : na
boundaryGeometry(
map<int, float> bins,
int boundary,
float logEpsilon) =>
float l0 = math.log(
binVolume(
bins,
boundary - 3) +
logEpsilon)
float l1 = math.log(
binVolume(
bins,
boundary - 2) +
logEpsilon)
float l2 = math.log(
binVolume(
bins,
boundary - 1) +
logEpsilon)
float r0 = math.log(
binVolume(
bins,
boundary) +
logEpsilon)
float r1 = math.log(
binVolume(
bins,
boundary + 1) +
logEpsilon)
float r2 = math.log(
binVolume(
bins,
boundary + 2) +
logEpsilon)
float leftMedian =
median3(l0, l1, l2)
float rightMedian =
median3(r0, r1, r2)
float jump = math.abs(
rightMedian -
leftMedian)
float noise =
mad3(
l0,
l1,
l2,
leftMedian) +
mad3(
r0,
r1,
r2,
rightMedian)
float clean =
math.max(
0.0,
jump - noise)
int denseDirection =
rightMedian >
leftMedian ? 1 : -1
[leftMedian,
rightMedian,
clean,
denseDirection]
smoothedLogVolume(
map<int, float> bins,
int row,
float logEpsilon) =>
median3(
math.log(
binVolume(
bins,
row - 1) +
logEpsilon),
math.log(
binVolume(
bins,
row) +
logEpsilon),
math.log(
binVolume(
bins,
row + 1) +
logEpsilon))
traceNodeMate(
map<int, float> bins,
int minBin,
int maxBin,
int rowTicks,
int anchorTick,
int retainedDenseDirection,
float maximumWidthTicks,
float logEpsilon) =>
bool pairValid = false
int mateTick = na
float prominence = na
int widthRows = 0
int boundary = int(math.round(
(
anchorTick -
GRID_ORIGIN_TICKS) /
float(rowTicks)))
int firstBoundary =
minBin + SHELF_SIDE_ROWS
int lastBoundary =
maxBin - SHELF_SIDE_ROWS + 1
int maximumWidthRows =
math.max(
MIN_NODE_ROWS,
int(math.floor(
maximumWidthTicks /
rowTicks)))
if not na(logEpsilon) and
(
retainedDenseDirection == 1 or
retainedDenseDirection == -1) and
boundary >= firstBoundary and
boundary <= lastBoundary
[anchorLeft,
anchorRight,
anchorClean,
anchorDirection] =
boundaryGeometry(
bins,
boundary,
logEpsilon)
float thinAnchor =
retainedDenseDirection == 1 ?
anchorLeft :
anchorRight
float denseAnchor =
retainedDenseDirection == 1 ?
anchorRight :
anchorLeft
int firstDenseRow =
retainedDenseDirection == 1 ?
boundary :
boundary - 1
bool anchorConsistent =
anchorClean > 0.0 and
anchorDirection ==
retainedDenseDirection and
denseAnchor >
thinAnchor and
smoothedLogVolume(
bins,
firstDenseRow,
logEpsilon) >=
thinAnchor
if anchorConsistent
int candidateWidth =
MIN_NODE_ROWS
bool searching = true
while searching and
candidateWidth <=
maximumWidthRows
int exitBoundary =
boundary +
retainedDenseDirection *
candidateWidth
int nearBoundary =
boundary +
retainedDenseDirection *
(
candidateWidth -
1)
int farBoundary =
boundary +
retainedDenseDirection *
(
candidateWidth +
1)
bool tripletHasContext =
nearBoundary >=
firstBoundary and
nearBoundary <=
lastBoundary and
exitBoundary >=
firstBoundary and
exitBoundary <=
lastBoundary and
farBoundary >=
firstBoundary and
farBoundary <=
lastBoundary
if not tripletHasContext
searching := false
else
[nearLeft,
nearRight,
nearClean,
nearDenseDirection] =
boundaryGeometry(
bins,
nearBoundary,
logEpsilon)
[exitLeft,
exitRight,
exitClean,
exitDenseDirection] =
boundaryGeometry(
bins,
exitBoundary,
logEpsilon)
[farLeft,
farRight,
farClean,
farDenseDirection] =
boundaryGeometry(
bins,
farBoundary,
logEpsilon)
float nearOppositeClean =
nearDenseDirection ==
-retainedDenseDirection ?
nearClean : 0.0
float exitOppositeClean =
exitDenseDirection ==
-retainedDenseDirection ?
exitClean : 0.0
float farOppositeClean =
farDenseDirection ==
-retainedDenseDirection ?
farClean : 0.0
bool localOppositePeak =
exitOppositeClean >=
anchorClean *
MIN_MATE_TO_ANCHOR_CLEAN and
exitOppositeClean >=
nearOppositeClean and
exitOppositeClean >=
farOppositeClean and
(
exitOppositeClean >
nearOppositeClean or
exitOppositeClean >
farOppositeClean) and
exitDenseDirection ==
-retainedDenseDirection
if localOppositePeak
// The nearest material peak owns the local decision.
// If it does not enclose a bulge, do not jump across it
// to manufacture a broader composite pair.
searching := false
array<float> interior =
array.new_float()
for interiorIndex =
0 to candidateWidth - 1
int interiorRow =
firstDenseRow +
retainedDenseDirection *
interiorIndex
interior.push(
smoothedLogVolume(
bins,
interiorRow,
logEpsilon))
interior.sort(
order.ascending)
float interiorMedian =
medianSorted(interior)
float mateExterior =
retainedDenseDirection == 1 ?
exitRight :
exitLeft
float localProminence =
interiorMedian -
math.max(
thinAnchor,
mateExterior)
if localProminence > 0.0
pairValid := true
mateTick :=
GRID_ORIGIN_TICKS +
exitBoundary *
rowTicks
prominence :=
localProminence
widthRows :=
candidateWidth
candidateWidth += 1
[pairValid,
mateTick,
prominence,
widthRows]
betterNode(
int support,
float worstProminence,
float minimumZ,
int incumbentSupport,
float incumbentWorstProminence,
float incumbentMinimumZ) =>
support > incumbentSupport or
(
support == incumbentSupport and
(
na(incumbentWorstProminence) or
worstProminence >
incumbentWorstProminence)) or
(
support == incumbentSupport and
worstProminence ==
incumbentWorstProminence and
(
na(incumbentMinimumZ) or
minimumZ >
incumbentMinimumZ))
// ============================================================================
// CONFIRMED RSD MACRO RANGE AND RETROSPECTIVE 15-MINUTE PROFILE CACHE
// ============================================================================
[macroHigh0Medium,
macroHighTime0Medium,
macroHigh1Medium,
macroHighTime1Medium,
macroHigh2Medium,
macroHighTime2Medium,
macroLow0Medium,
macroLowTime0Medium,
macroLow1Medium,
macroLowTime1Medium,
macroLow2Medium,
macroLowTime2Medium,
macroHigh0Fast,
macroHighTime0Fast,
macroHigh1Fast,
macroHighTime1Fast,
macroHigh2Fast,
macroHighTime2Fast,
macroLow0Fast,
macroLowTime0Fast,
macroLow1Fast,
macroLowTime1Fast,
macroLow2Fast,
macroLowTime2Fast] = request.security(
syminfo.tickerid,
macroTFInput,
findMacroPivots(),
lookahead = barmerge.lookahead_on)
[macroOuterHigh,
macroOuterHighTime,
macroPriorHigh,
macroPriorHighTime,
macroFarHigh,
macroFarHighTime] = reduceTopThreeByTime(
macroHigh0Medium,
macroHighTime0Medium,
macroHigh1Medium,
macroHighTime1Medium,
macroHigh2Medium,
macroHighTime2Medium,
macroHigh0Fast,
macroHighTime0Fast,
macroHigh1Fast,
macroHighTime1Fast,
macroHigh2Fast,
macroHighTime2Fast)
[macroOuterLow,
macroOuterLowTime,
macroPriorLow,
macroPriorLowTime,
macroFarLow,
macroFarLowTime] = reduceTopThreeByTime(
macroLow0Medium,
macroLowTime0Medium,
macroLow1Medium,
macroLowTime1Medium,
macroLow2Medium,
macroLowTime2Medium,
macroLow0Fast,
macroLowTime0Fast,
macroLow1Fast,
macroLowTime1Fast,
macroLow2Fast,
macroLowTime2Fast)
resolveAutomaticRange(float currentAtr) =>
float maximumSlopeAbsolute =
maximumWallSlopeInput *
nz(currentAtr, 0.0)
bool primaryHighWall =
not na(macroOuterHigh) and
not na(macroOuterHighTime) and
not na(macroPriorHigh) and
not na(macroPriorHighTime) and
macroOuterHigh > macroPriorHigh
bool shiftedHighWall =
not primaryHighWall and
not na(macroPriorHigh) and
not na(macroPriorHighTime) and
not na(macroFarHigh) and
not na(macroFarHighTime) and
macroPriorHigh > macroFarHigh
bool primaryLowWall =
not na(macroOuterLow) and
not na(macroOuterLowTime) and
not na(macroPriorLow) and
not na(macroPriorLowTime) and
macroOuterLow < macroPriorLow
bool shiftedLowWall =
not primaryLowWall and
not na(macroPriorLow) and
not na(macroPriorLowTime) and
not na(macroFarLow) and
not na(macroFarLowTime) and
macroPriorLow < macroFarLow
bool highExpansion =
not na(macroOuterHigh) and
not na(macroPriorHigh) and
not na(macroFarHigh) and
math.max(
macroOuterHigh,
macroPriorHigh) >
macroFarHigh
bool lowExpansion =
not na(macroOuterLow) and
not na(macroPriorLow) and
not na(macroFarLow) and
math.min(
macroOuterLow,
macroPriorLow) <
macroFarLow
bool wallsAllowed =
not wallRequireBothSidesInput or
(highExpansion and lowExpansion)
bool upperCandidateValid = false
int upperStartTime = na
int upperEndTime = na
float upperStartPrice = na
float upperEndPrice = na
bool lowerCandidateValid = false
int lowerStartTime = na
int lowerEndTime = na
float lowerStartPrice = na
float lowerEndPrice = na
if wallsAllowed and primaryHighWall
int outerBar =
htfTimeToBar(macroOuterHighTime)
int priorBar =
htfTimeToBar(macroPriorHighTime)
bool haveFar =
not na(macroFarHigh) and
not na(macroFarHighTime) and
macroFarHigh < macroOuterHigh
bool useFar = haveFar
if haveFar
int farBar =
htfTimeToBar(macroFarHighTime)
if farBar != outerBar
float spanSlope =
(macroOuterHigh -
macroFarHigh) /
(outerBar - farBar)
float lineAtPrior =
macroFarHigh +
spanSlope *
(priorBar - farBar)
useFar :=
macroPriorHigh <=
lineAtPrior +
syminfo.mintick
float anchorPrice =
useFar ?
macroFarHigh :
macroPriorHigh
int anchorTime =
useFar ?
macroFarHighTime :
macroPriorHighTime
int anchorBar =
htfTimeToBar(anchorTime)
if anchorBar != outerBar
float slope =
(macroOuterHigh -
anchorPrice) /
(outerBar - anchorBar)
upperCandidateValid :=
not hideSteepWallsInput or
math.abs(slope) <=
maximumSlopeAbsolute
upperStartTime := anchorTime
upperEndTime := macroOuterHighTime
upperStartPrice := anchorPrice
upperEndPrice := macroOuterHigh
else if wallsAllowed and shiftedHighWall
int anchorBar =
htfTimeToBar(macroFarHighTime)
int endBar =
htfTimeToBar(macroPriorHighTime)
if anchorBar != endBar
float slope =
(macroPriorHigh -
macroFarHigh) /
(endBar - anchorBar)
upperCandidateValid :=
not hideSteepWallsInput or
math.abs(slope) <=
maximumSlopeAbsolute
upperStartTime := macroFarHighTime
upperEndTime := macroPriorHighTime
upperStartPrice := macroFarHigh
upperEndPrice := macroPriorHigh
if wallsAllowed and primaryLowWall
int outerBar =
htfTimeToBar(macroOuterLowTime)
int priorBar =
htfTimeToBar(macroPriorLowTime)
bool haveFar =
not na(macroFarLow) and
not na(macroFarLowTime) and
macroFarLow > macroOuterLow
bool useFar = haveFar
if haveFar
int farBar =
htfTimeToBar(macroFarLowTime)
if farBar != outerBar
float spanSlope =
(macroOuterLow -
macroFarLow) /
(outerBar - farBar)
float lineAtPrior =
macroFarLow +
spanSlope *
(priorBar - farBar)
useFar :=
macroPriorLow >=
lineAtPrior -
syminfo.mintick
float anchorPrice =
useFar ?
macroFarLow :
macroPriorLow
int anchorTime =
useFar ?
macroFarLowTime :
macroPriorLowTime
int anchorBar =
htfTimeToBar(anchorTime)
if anchorBar != outerBar
float slope =
(macroOuterLow -
anchorPrice) /
(outerBar - anchorBar)
lowerCandidateValid :=
not hideSteepWallsInput or
math.abs(slope) <=
maximumSlopeAbsolute
lowerStartTime := anchorTime
lowerEndTime := macroOuterLowTime
lowerStartPrice := anchorPrice
lowerEndPrice := macroOuterLow
else if wallsAllowed and shiftedLowWall
int anchorBar =
htfTimeToBar(macroFarLowTime)
int endBar =
htfTimeToBar(macroPriorLowTime)
if anchorBar != endBar
float slope =
(macroPriorLow -
macroFarLow) /
(endBar - anchorBar)
lowerCandidateValid :=
not hideSteepWallsInput or
math.abs(slope) <=
maximumSlopeAbsolute
lowerStartTime := macroFarLowTime
lowerEndTime := macroPriorLowTime
lowerStartPrice := macroFarLow
lowerEndPrice := macroPriorLow
bool useLowerCandidate =
lowerCandidateValid and
(
not upperCandidateValid or
lowerEndTime > upperEndTime or
(
lowerEndTime == upperEndTime and
lowerEndTime - lowerStartTime >=
upperEndTime - upperStartTime))
bool wallSelected =
upperCandidateValid or
lowerCandidateValid
int selectedSide =
useLowerCandidate ? -1 : 1
int selectedStartMacroTime =
useLowerCandidate ?
lowerStartTime :
upperStartTime
int selectedEndMacroTime =
useLowerCandidate ?
lowerEndTime :
upperEndTime
float selectedStartPrice =
useLowerCandidate ?
lowerStartPrice :
upperStartPrice
float selectedEndPrice =
useLowerCandidate ?
lowerEndPrice :
upperEndPrice
int selectedStartTime = na
int selectedEndBarTime = na
bool startRefined = false
bool endRefined = false
int macroDurationMs = int(
timeframe.in_seconds(
macroTFInput) * 1000)
if wallSelected
int refinementBars =
math.min(bar_index, 4999)
for offset = 0 to refinementBars
bool inStartMacroBar =
time[offset] >=
selectedStartMacroTime and
time[offset] <
selectedStartMacroTime +
macroDurationMs
bool inEndMacroBar =
time[offset] >=
selectedEndMacroTime and
time[offset] <
selectedEndMacroTime +
macroDurationMs
bool startPriceMatch =
selectedSide < 0 ?
priceToTicks(low[offset]) ==
priceToTicks(selectedStartPrice) :
priceToTicks(high[offset]) ==
priceToTicks(selectedStartPrice)
bool endPriceMatch =
selectedSide < 0 ?
priceToTicks(low[offset]) ==
priceToTicks(selectedEndPrice) :
priceToTicks(high[offset]) ==
priceToTicks(selectedEndPrice)
if inStartMacroBar and
startPriceMatch
selectedStartTime := time[offset]
startRefined := true
if inEndMacroBar and
endPriceMatch
selectedEndBarTime := time[offset]
endRefined := true
int selectedEndExclusive =
endRefined ?
selectedEndBarTime +
PROFILE_BAR_MS : na
bool endpointRefined =
wallSelected and
startRefined and
endRefined and
selectedStartTime <
selectedEndExclusive
[wallSelected,
endpointRefined,
selectedSide,
selectedStartMacroTime,
selectedEndMacroTime,
selectedStartTime,
selectedEndBarTime,
selectedEndExclusive,
selectedStartPrice,
selectedEndPrice,
wallsAllowed]
bool chartSupported =
chart.is_standard and
timeframe.isminutes and
timeframe.in_seconds() ==
15 * 60
float chartAtr = ta.atr(14)
bool selectorTupleChanged =
barstate.isfirst or
nz(macroOuterHighTime, -1) !=
nz(macroOuterHighTime[1], -1) or
nz(macroPriorHighTime, -1) !=
nz(macroPriorHighTime[1], -1) or
nz(macroFarHighTime, -1) !=
nz(macroFarHighTime[1], -1) or
nz(macroOuterLowTime, -1) !=
nz(macroOuterLowTime[1], -1) or
nz(macroPriorLowTime, -1) !=
nz(macroPriorLowTime[1], -1) or
nz(macroFarLowTime, -1) !=
nz(macroFarLowTime[1], -1)
float selectorAtr = nz(
ta.valuewhen(
selectorTupleChanged,
chartAtr[1],
0),
chartAtr)
var map<int, float> fineBins =
map.new<int, float>()
var map<int, float> baseBins =
map.new<int, float>()
var map<int, float> coarseBins =
map.new<int, float>()
var int fineMinBin = na
var int fineMaxBin = na
var int baseMinBin = na
var int baseMaxBin = na
var int coarseMinBin = na
var int coarseMaxBin = na
var int fineTicks = na
var int effectiveBaseTicks = na
var int coarseTicks = na
var bool gridConfigurationValid = false
var float sourceTotalVolume = 0.0
var float fineAllocatedVolume = 0.0
var float baseAllocatedVolume = 0.0
var float coarseAllocatedVolume = 0.0
var int includedSourceBars = 0
var int firstIncludedSourceTime = na
var int lastIncludedSourceTime = na
var int previousIncludedSourceTime = na
var int invalidSourceBars = 0
var bool sourceOrderIssue = false
var bool fineOverflow = false
var bool baseOverflow = false
var bool coarseOverflow = false
var bool rangeHistoryCovered = false
var bool rangeTooLong = false
var bool allocationWorkCapFail = false
var int allocationTouches = 0
var float cachedOutlineHigh = na
var float cachedOutlineLow = na
var bool cacheInitialized = false
var bool cachedWallSelected = false
var bool cachedEndpointRefined = false
var bool cachedWallsAllowed = false
var int cachedSide = 0
var int cachedStartMacroTime = na
var int cachedEndMacroTime = na
var int cachedProfileStartTime = na
var int cachedProfileEndBarTime = na
var int cachedProfileEndExclusive = na
var float cachedStartPrice = na
var float cachedEndPrice = na
bool rebuildTrigger =
barstate.islastconfirmedhistory or
(barstate.islast and barstate.isconfirmed)
if rebuildTrigger
[rebuildWallSelected,
rebuildEndpointRefined,
rebuildSide,
rebuildStartMacroTime,
rebuildEndMacroTime,
rebuildStartTime,
rebuildEndBarTime,
rebuildEndExclusive,
rebuildStartPrice,
rebuildEndPrice,
rebuildWallsAllowed] =
resolveAutomaticRange(selectorAtr)
bool rangeIdentityChanged =
not cacheInitialized or
rebuildWallSelected !=
cachedWallSelected or
rebuildWallsAllowed !=
cachedWallsAllowed or
rebuildEndpointRefined !=
cachedEndpointRefined or
rebuildSide != cachedSide or
rebuildStartMacroTime !=
cachedStartMacroTime or
rebuildEndMacroTime !=
cachedEndMacroTime or
nz(rebuildStartTime, -1) !=
nz(cachedProfileStartTime, -1) or
nz(rebuildEndBarTime, -1) !=
nz(cachedProfileEndBarTime, -1) or
nz(rebuildEndExclusive, -1) !=
nz(cachedProfileEndExclusive, -1) or
(
rebuildEndpointRefined and
cachedEndpointRefined and
(
priceToTicks(
rebuildStartPrice) !=
priceToTicks(
cachedStartPrice) or
priceToTicks(
rebuildEndPrice) !=
priceToTicks(
cachedEndPrice)))
if rangeIdentityChanged
fineBins.clear()
baseBins.clear()
coarseBins.clear()
fineMinBin := na
fineMaxBin := na
baseMinBin := na
baseMaxBin := na
coarseMinBin := na
coarseMaxBin := na
fineTicks := na
effectiveBaseTicks := na
coarseTicks := na
gridConfigurationValid := false
sourceTotalVolume := 0.0
fineAllocatedVolume := 0.0
baseAllocatedVolume := 0.0
coarseAllocatedVolume := 0.0
includedSourceBars := 0
firstIncludedSourceTime := na
lastIncludedSourceTime := na
previousIncludedSourceTime := na
invalidSourceBars := 0
sourceOrderIssue := false
fineOverflow := false
baseOverflow := false
coarseOverflow := false
rangeHistoryCovered := false
rangeTooLong := false
allocationWorkCapFail := false
allocationTouches := 0
cachedOutlineHigh := na
cachedOutlineLow := na
cacheInitialized := true
cachedWallSelected :=
rebuildWallSelected
cachedEndpointRefined :=
rebuildEndpointRefined
cachedWallsAllowed :=
rebuildWallsAllowed
cachedSide := rebuildSide
cachedStartMacroTime :=
rebuildStartMacroTime
cachedEndMacroTime :=
rebuildEndMacroTime
cachedProfileStartTime :=
rebuildStartTime
cachedProfileEndBarTime :=
rebuildEndBarTime
cachedProfileEndExclusive :=
rebuildEndExclusive
cachedStartPrice :=
rebuildStartPrice
cachedEndPrice :=
rebuildEndPrice
int rangeMinTick = na
int rangeMaxTick = na
int scanBars = math.min(
bar_index,
4999)
if chartSupported and
rebuildEndpointRefined
for offset = scanBars to 0
int sourceTime = time[offset]
bool useForProfile =
sourceTime >=
rebuildStartTime and
sourceTime <
rebuildEndExclusive
if useForProfile
int sourceHigh =
priceToTicks(high[offset])
int sourceLow =
priceToTicks(low[offset])
float sourceVolume =
volume[offset]
firstIncludedSourceTime :=
includedSourceBars == 0 ?
sourceTime :
firstIncludedSourceTime
sourceOrderIssue :=
sourceOrderIssue or
(
not na(
previousIncludedSourceTime) and
sourceTime <=
previousIncludedSourceTime)
previousIncludedSourceTime :=
sourceTime
lastIncludedSourceTime :=
sourceTime
includedSourceBars += 1
bool sourceValid =
not na(sourceHigh) and
not na(sourceLow) and
not na(sourceVolume) and
sourceVolume >= 0.0 and
sourceHigh >= sourceLow
invalidSourceBars +=
sourceValid ? 0 : 1
if sourceValid
sourceTotalVolume +=
sourceVolume
rangeMinTick :=
na(rangeMinTick) ?
sourceLow :
math.min(
rangeMinTick,
sourceLow)
rangeMaxTick :=
na(rangeMaxTick) ?
sourceHigh :
math.max(
rangeMaxTick,
sourceHigh)
cachedOutlineHigh :=
na(cachedOutlineHigh) ?
high[offset] :
math.max(
cachedOutlineHigh,
high[offset])
cachedOutlineLow :=
na(cachedOutlineLow) ?
low[offset] :
math.min(
cachedOutlineLow,
low[offset])
rangeHistoryCovered :=
includedSourceBars > 0 and
firstIncludedSourceTime ==
rebuildStartTime and
lastIncludedSourceTime +
SOURCE_BAR_MS ==
rebuildEndExclusive
rangeTooLong :=
includedSourceBars >
MAX_PROFILE_SOURCE_BARS
if rangeHistoryCovered and
not rangeTooLong and
invalidSourceBars == 0 and
not sourceOrderIssue and
not na(rangeMinTick) and
not na(rangeMaxTick)
effectiveBaseTicks :=
baseTicksInput
fineTicks := math.max(
1,
int(math.round(
effectiveBaseTicks *
0.8)))
coarseTicks := math.max(
1,
int(math.round(
effectiveBaseTicks *
1.2)))
int fineSpan =
int(math.floor(
(rangeMaxTick -
GRID_ORIGIN_TICKS) /
float(fineTicks))) -
int(math.floor(
(rangeMinTick -
GRID_ORIGIN_TICKS) /
float(fineTicks))) +
1
if widenRowsToCapacityInput
while fineSpan >
MAX_PROFILE_BINS and
effectiveBaseTicks < 500
effectiveBaseTicks += 1
fineTicks := math.max(
1,
int(math.round(
effectiveBaseTicks *
0.8)))
coarseTicks := math.max(
1,
int(math.round(
effectiveBaseTicks *
1.2)))
fineSpan :=
int(math.floor(
(rangeMaxTick -
GRID_ORIGIN_TICKS) /
float(fineTicks))) -
int(math.floor(
(rangeMinTick -
GRID_ORIGIN_TICKS) /
float(fineTicks))) +
1
gridConfigurationValid :=
fineTicks > 0 and
effectiveBaseTicks > 0 and
coarseTicks > 0 and
fineTicks !=
effectiveBaseTicks and
fineTicks != coarseTicks and
effectiveBaseTicks !=
coarseTicks
if gridConfigurationValid
for offset = scanBars to 0
int sourceTime = time[offset]
if sourceTime >=
rebuildStartTime and
sourceTime <
rebuildEndExclusive
int sourceHigh =
priceToTicks(
high[offset])
int sourceLow =
priceToTicks(
low[offset])
int fineLowBin = int(
math.floor(
(sourceLow -
GRID_ORIGIN_TICKS) /
float(fineTicks)))
int fineHighBin = int(
math.floor(
(sourceHigh -
GRID_ORIGIN_TICKS) /
float(fineTicks)))
int baseLowBin = int(
math.floor(
(sourceLow -
GRID_ORIGIN_TICKS) /
float(
effectiveBaseTicks)))
int baseHighBin = int(
math.floor(
(sourceHigh -
GRID_ORIGIN_TICKS) /
float(
effectiveBaseTicks)))
int coarseLowBin = int(
math.floor(
(sourceLow -
GRID_ORIGIN_TICKS) /
float(coarseTicks)))
int coarseHighBin = int(
math.floor(
(sourceHigh -
GRID_ORIGIN_TICKS) /
float(coarseTicks)))
allocationTouches +=
fineHighBin -
fineLowBin + 1 +
baseHighBin -
baseLowBin + 1 +
coarseHighBin -
coarseLowBin + 1
allocationWorkCapFail :=
allocationTouches >
MAX_ALLOCATION_TOUCHES
if gridConfigurationValid and
not allocationWorkCapFail
for offset = scanBars to 0
int sourceTime = time[offset]
if sourceTime >=
rebuildStartTime and
sourceTime <
rebuildEndExclusive
int sourceHigh =
priceToTicks(
high[offset])
int sourceLow =
priceToTicks(
low[offset])
float sourceVolume =
volume[offset]
[nextFineMin,
nextFineMax,
fineAdded,
finePutOverflow,
fineTouches] =
allocateBarToGrid(
fineBins,
sourceLow,
sourceHigh,
sourceVolume,
fineTicks,
fineMinBin,
fineMaxBin)
fineMinBin :=
nextFineMin
fineMaxBin :=
nextFineMax
fineAllocatedVolume +=
fineAdded
fineOverflow :=
fineOverflow or
finePutOverflow
[nextBaseMin,
nextBaseMax,
baseAdded,
basePutOverflow,
baseTouches] =
allocateBarToGrid(
baseBins,
sourceLow,
sourceHigh,
sourceVolume,
effectiveBaseTicks,
baseMinBin,
baseMaxBin)
baseMinBin :=
nextBaseMin
baseMaxBin :=
nextBaseMax
baseAllocatedVolume +=
baseAdded
baseOverflow :=
baseOverflow or
basePutOverflow
[nextCoarseMin,
nextCoarseMax,
coarseAdded,
coarsePutOverflow,
coarseTouches] =
allocateBarToGrid(
coarseBins,
sourceLow,
sourceHigh,
sourceVolume,
coarseTicks,
coarseMinBin,
coarseMaxBin)
coarseMinBin :=
nextCoarseMin
coarseMaxBin :=
nextCoarseMax
coarseAllocatedVolume +=
coarseAdded
coarseOverflow :=
coarseOverflow or
coarsePutOverflow
// ============================================================================
// DRAWING STATE
// ============================================================================
var box rangeBox = na
var line pocLine = na
var line valueAreaHighLine = na
var line valueAreaLowLine = na
var array<line> histogramLines =
array.new_line()
var array<line> consensusLines =
array.new_line()
var array<label> consensusLabels =
array.new_label()
var table diagnosticTable = table.new(
position.top_right,
2,
28,
frame_color =
color.new(color.gray, 35),
frame_width = 1,
border_color =
color.new(color.gray, 70),
border_width = 1)
// ============================================================================
// LAST-BAR PROFILE, CONSENSUS, AND VISUALS
// ============================================================================
if barstate.islast
if not na(rangeBox)
box.delete(rangeBox)
rangeBox := na
if not na(pocLine)
line.delete(pocLine)
pocLine := na
if not na(valueAreaHighLine)
line.delete(valueAreaHighLine)
valueAreaHighLine := na
if not na(valueAreaLowLine)
line.delete(valueAreaLowLine)
valueAreaLowLine := na
clearLines(histogramLines)
clearLines(consensusLines)
clearLabels(consensusLabels)
[liveWallSelected,
liveEndpointRefined,
liveSide,
liveStartMacroTime,
liveEndMacroTime,
liveStartTime,
liveEndBarTime,
liveEndExclusive,
liveStartPrice,
liveEndPrice,
liveWallsAllowed] =
resolveAutomaticRange(selectorAtr)
bool liveMatchesCache =
cacheInitialized and
liveWallSelected ==
cachedWallSelected and
liveEndpointRefined ==
cachedEndpointRefined and
liveSide == cachedSide and
liveStartMacroTime ==
cachedStartMacroTime and
liveEndMacroTime ==
cachedEndMacroTime and
nz(liveStartTime, -1) ==
nz(cachedProfileStartTime, -1) and
nz(liveEndBarTime, -1) ==
nz(cachedProfileEndBarTime, -1) and
nz(liveEndExclusive, -1) ==
nz(cachedProfileEndExclusive, -1) and
(
not liveEndpointRefined or
(
priceToTicks(
liveStartPrice) ==
priceToTicks(
cachedStartPrice) and
priceToTicks(
liveEndPrice) ==
priceToTicks(
cachedEndPrice)))
bool rangeUpdatePending =
liveWallSelected and
liveEndpointRefined and
not liveMatchesCache
bool activeRangeValid =
chartSupported and
liveWallSelected and
liveEndpointRefined and
liveMatchesCache
int profileStartTime =
activeRangeValid ?
cachedProfileStartTime :
liveStartTime
int profileEndBarTime =
activeRangeValid ?
cachedProfileEndBarTime :
liveEndBarTime
int activeProfileEndExclusive =
activeRangeValid ?
cachedProfileEndExclusive :
liveEndExclusive
float outlineHigh =
activeRangeValid ?
cachedOutlineHigh : na
float outlineLow =
activeRangeValid ?
cachedOutlineLow : na
if chartSupported and
liveEndpointRefined and
not activeRangeValid
int outlineScanBars =
math.min(bar_index, 4999)
for offset = 0 to outlineScanBars
if time[offset] >=
liveStartTime and
time[offset] <
liveEndExclusive
outlineHigh :=
na(outlineHigh) ?
high[offset] :
math.max(
outlineHigh,
high[offset])
outlineLow :=
na(outlineLow) ?
low[offset] :
math.min(
outlineLow,
low[offset])
if chartSupported and
liveEndpointRefined and
not na(outlineHigh)
rangeBox := box.new(
left = liveStartTime,
top = outlineHigh,
right = liveEndExclusive,
bottom = outlineLow,
xloc = xloc.bar_time,
border_color = rangeColorInput,
border_width = 2,
border_style =
line.style_dashed,
bgcolor =
color.new(rangeColorInput, 97))
bool endpointsComplete =
activeRangeValid and
rangeHistoryCovered and
firstIncludedSourceTime ==
cachedProfileStartTime and
lastIncludedSourceTime +
SOURCE_BAR_MS ==
cachedProfileEndExclusive
float reconciliationTolerance =
math.max(
0.000001,
math.abs(sourceTotalVolume) *
0.000000001)
bool fineReconciliationValid =
math.abs(
fineAllocatedVolume -
sourceTotalVolume) <=
reconciliationTolerance
bool baseReconciliationValid =
math.abs(
baseAllocatedVolume -
sourceTotalVolume) <=
reconciliationTolerance
bool coarseReconciliationValid =
math.abs(
coarseAllocatedVolume -
sourceTotalVolume) <=
reconciliationTolerance
bool constructionComplete =
chartSupported and
activeRangeValid and
gridConfigurationValid and
time >= cachedProfileEndExclusive and
endpointsComplete and
includedSourceBars > 0 and
not rangeTooLong and
not allocationWorkCapFail and
invalidSourceBars == 0 and
not sourceOrderIssue and
not fineOverflow and
not baseOverflow and
not coarseOverflow and
fineReconciliationValid and
baseReconciliationValid and
coarseReconciliationValid
[fineAlgorithmValid,
finePocBin,
fineValueAreaLowBin,
fineValueAreaHighBin,
fineAchievedValueAreaPercent,
fineMaximumBinVolume] =
calculateProfile(
fineBins,
fineMinBin,
fineMaxBin,
fineAllocatedVolume,
fineTicks)
[baseAlgorithmValid,
basePocBin,
baseValueAreaLowBin,
baseValueAreaHighBin,
baseAchievedValueAreaPercent,
baseMaximumBinVolume] =
calculateProfile(
baseBins,
baseMinBin,
baseMaxBin,
baseAllocatedVolume,
effectiveBaseTicks)
[coarseAlgorithmValid,
coarsePocBin,
coarseValueAreaLowBin,
coarseValueAreaHighBin,
coarseAchievedValueAreaPercent,
coarseMaximumBinVolume] =
calculateProfile(
coarseBins,
coarseMinBin,
coarseMaxBin,
coarseAllocatedVolume,
coarseTicks)
bool multiProfileValid =
constructionComplete and
fineAlgorithmValid and
baseAlgorithmValid and
coarseAlgorithmValid
float basePocPrice =
multiProfileValid ?
(
GRID_ORIGIN_TICKS +
basePocBin *
effectiveBaseTicks +
effectiveBaseTicks *
0.5) *
syminfo.mintick : na
float baseValueAreaLowPrice =
multiProfileValid ?
(
GRID_ORIGIN_TICKS +
baseValueAreaLowBin *
effectiveBaseTicks) *
syminfo.mintick : na
float baseValueAreaHighPrice =
multiProfileValid ?
(
GRID_ORIGIN_TICKS +
(
baseValueAreaHighBin +
1) *
effectiveBaseTicks) *
syminfo.mintick : na
array<int> fineCandidateTicks =
array.new_int()
array<float> fineCandidateClean =
array.new_float()
array<float> fineCandidateZ =
array.new_float()
array<int> fineCandidateDirections =
array.new_int()
int fineRawCount = 0
float fineCleanMad = na
array<int> baseCandidateTicks =
array.new_int()
array<float> baseCandidateClean =
array.new_float()
array<float> baseCandidateZ =
array.new_float()
array<int> baseCandidateDirections =
array.new_int()
int baseRawCount = 0
float baseCleanMad = na
array<int> coarseCandidateTicks =
array.new_int()
array<float> coarseCandidateClean =
array.new_float()
array<float> coarseCandidateZ =
array.new_float()
array<int> coarseCandidateDirections =
array.new_int()
int coarseRawCount = 0
float coarseCleanMad = na
if multiProfileValid
[fineSelectedTicks,
fineSelectedClean,
fineSelectedZ,
fineSelectedDirections,
fineRaw,
fineMad] =
scoreGatedCandidates(
fineBins,
fineMinBin,
fineMaxBin,
fineTicks,
fineMaximumBinVolume,
minimumRatioInput,
maximumNoiseFractionInput,
minimumDenseFractionInput,
minimumRobustZInput,
localClusterRowsInput,
candidateCapPerGridInput)
fineCandidateTicks :=
fineSelectedTicks
fineCandidateClean :=
fineSelectedClean
fineCandidateZ :=
fineSelectedZ
fineCandidateDirections :=
fineSelectedDirections
fineRawCount := fineRaw
fineCleanMad := fineMad
[baseSelectedTicks,
baseSelectedClean,
baseSelectedZ,
baseSelectedDirections,
baseRaw,
baseMad] =
scoreGatedCandidates(
baseBins,
baseMinBin,
baseMaxBin,
effectiveBaseTicks,
baseMaximumBinVolume,
minimumRatioInput,
maximumNoiseFractionInput,
minimumDenseFractionInput,
minimumRobustZInput,
localClusterRowsInput,
candidateCapPerGridInput)
baseCandidateTicks :=
baseSelectedTicks
baseCandidateClean :=
baseSelectedClean
baseCandidateZ :=
baseSelectedZ
baseCandidateDirections :=
baseSelectedDirections
baseRawCount := baseRaw
baseCleanMad := baseMad
[coarseSelectedTicks,
coarseSelectedClean,
coarseSelectedZ,
coarseSelectedDirections,
coarseRaw,
coarseMad] =
scoreGatedCandidates(
coarseBins,
coarseMinBin,
coarseMaxBin,
coarseTicks,
coarseMaximumBinVolume,
minimumRatioInput,
maximumNoiseFractionInput,
minimumDenseFractionInput,
minimumRobustZInput,
localClusterRowsInput,
candidateCapPerGridInput)
coarseCandidateTicks :=
coarseSelectedTicks
coarseCandidateClean :=
coarseSelectedClean
coarseCandidateZ :=
coarseSelectedZ
coarseCandidateDirections :=
coarseSelectedDirections
coarseRawCount := coarseRaw
coarseCleanMad := coarseMad
// Every consensus cluster is seeded by a passing base-grid candidate.
// Neighbors are accepted only when every pair in the cluster is within Q.
// This is complete-linkage: a fine candidate cannot chain through the base
// to a coarse candidate that is more than Q ticks away from it.
array<int> clusterBaseTicks =
array.new_int()
array<int> clusterFineTicks =
array.new_int()
array<int> clusterCoarseTicks =
array.new_int()
array<int> clusterBaseDirections =
array.new_int()
array<int> clusterFineDirections =
array.new_int()
array<int> clusterCoarseDirections =
array.new_int()
array<float> clusterBaseZ =
array.new_float()
array<float> clusterFineZ =
array.new_float()
array<float> clusterCoarseZ =
array.new_float()
array<int> clusterSupportCounts =
array.new_int()
array<float> clusterMinimumZ =
array.new_float()
float consensusRadiusTicks =
consensusClusterBaseRowsInput *
effectiveBaseTicks
float endpointRadiusTicks =
effectiveBaseTicks
float maximumNodeWidthTicks =
maximumNodeWidthBaseRowsInput *
effectiveBaseTicks
if baseCandidateTicks.size() > 0
for baseIndex =
0 to baseCandidateTicks.size() - 1
int baseTick =
baseCandidateTicks.get(
baseIndex)
float baseZ =
baseCandidateZ.get(
baseIndex)
int bestFineIndex = -1
int bestCoarseIndex = -1
int bestSupport = 1
float bestMinimumZ = baseZ
int bestTotalDistance = 0
if fineCandidateTicks.size() > 0
for fineIndex =
0 to fineCandidateTicks.size() - 1
int fineTick =
fineCandidateTicks.get(
fineIndex)
int fineDistance =
math.abs(
fineTick -
baseTick)
if fineDistance <=
consensusRadiusTicks
float combinationMinimumZ =
math.min(
baseZ,
fineCandidateZ.get(
fineIndex))
if betterConsensusCombination(
2,
combinationMinimumZ,
fineDistance,
bestSupport,
bestMinimumZ,
bestTotalDistance)
bestFineIndex :=
fineIndex
bestCoarseIndex := -1
bestSupport := 2
bestMinimumZ :=
combinationMinimumZ
bestTotalDistance :=
fineDistance
if coarseCandidateTicks.size() > 0
for coarseIndex =
0 to coarseCandidateTicks.size() - 1
int coarseTick =
coarseCandidateTicks.get(
coarseIndex)
int coarseDistance =
math.abs(
coarseTick -
baseTick)
if coarseDistance <=
consensusRadiusTicks
float combinationMinimumZ =
math.min(
baseZ,
coarseCandidateZ.get(
coarseIndex))
if betterConsensusCombination(
2,
combinationMinimumZ,
coarseDistance,
bestSupport,
bestMinimumZ,
bestTotalDistance)
bestFineIndex := -1
bestCoarseIndex :=
coarseIndex
bestSupport := 2
bestMinimumZ :=
combinationMinimumZ
bestTotalDistance :=
coarseDistance
if fineCandidateTicks.size() > 0 and
coarseCandidateTicks.size() > 0
for fineIndex =
0 to fineCandidateTicks.size() - 1
int fineTick =
fineCandidateTicks.get(
fineIndex)
int fineDistance =
math.abs(
fineTick -
baseTick)
if fineDistance <=
consensusRadiusTicks
for coarseIndex =
0 to coarseCandidateTicks.size() - 1
int coarseTick =
coarseCandidateTicks.get(
coarseIndex)
int coarseDistance =
math.abs(
coarseTick -
baseTick)
int neighborDistance =
math.abs(
fineTick -
coarseTick)
bool completeLink =
coarseDistance <=
consensusRadiusTicks and
neighborDistance <=
consensusRadiusTicks
if completeLink
float combinationMinimumZ =
math.min(
baseZ,
math.min(
fineCandidateZ.get(
fineIndex),
coarseCandidateZ.get(
coarseIndex)))
int totalDistance =
fineDistance +
coarseDistance +
neighborDistance
if betterConsensusCombination(
3,
combinationMinimumZ,
totalDistance,
bestSupport,
bestMinimumZ,
bestTotalDistance)
bestFineIndex :=
fineIndex
bestCoarseIndex :=
coarseIndex
bestSupport := 3
bestMinimumZ :=
combinationMinimumZ
bestTotalDistance :=
totalDistance
clusterBaseTicks.push(baseTick)
clusterBaseDirections.push(
baseCandidateDirections.get(
baseIndex))
clusterBaseZ.push(baseZ)
clusterFineTicks.push(
bestFineIndex >= 0 ?
fineCandidateTicks.get(
bestFineIndex) : na)
clusterFineDirections.push(
bestFineIndex >= 0 ?
fineCandidateDirections.get(
bestFineIndex) : na)
clusterFineZ.push(
bestFineIndex >= 0 ?
fineCandidateZ.get(
bestFineIndex) : na)
clusterCoarseTicks.push(
bestCoarseIndex >= 0 ?
coarseCandidateTicks.get(
bestCoarseIndex) : na)
clusterCoarseDirections.push(
bestCoarseIndex >= 0 ?
coarseCandidateDirections.get(
bestCoarseIndex) : na)
clusterCoarseZ.push(
bestCoarseIndex >= 0 ?
coarseCandidateZ.get(
bestCoarseIndex) : na)
clusterSupportCounts.push(
bestSupport)
clusterMinimumZ.push(
bestMinimumZ)
bool structuralBoundsValid =
not useStructuralBoundsInput or
structuralHighInput >
structuralLowInput
int structuralLowTicks =
priceToTicks(structuralLowInput)
int structuralHighTicks =
priceToTicks(structuralHighInput)
float structuralDistanceTicks =
structuralDistanceBaseRowsInput *
effectiveBaseTicks
array<int> eligibleClusterIndices =
array.new_int()
array<bool> clusterDrawingSuppressed =
array.new_bool(
clusterBaseTicks.size(),
false)
int structurallySuppressedCount = 0
if multiProfileValid and
structuralBoundsValid and
clusterBaseTicks.size() > 0
for clusterIndex =
0 to clusterBaseTicks.size() - 1
int supportCount =
clusterSupportCounts.get(
clusterIndex)
int consensusTick =
clusterBaseTicks.get(
clusterIndex)
bool nearStructuralBound =
useStructuralBoundsInput and
(
math.abs(
consensusTick -
structuralLowTicks) <=
structuralDistanceTicks or
math.abs(
consensusTick -
structuralHighTicks) <=
structuralDistanceTicks)
if supportCount >=
MIN_CONSENSUS_GRIDS
if nearStructuralBound
structurallySuppressedCount +=
1
clusterDrawingSuppressed.set(
clusterIndex,
nearStructuralBound)
eligibleClusterIndices.push(
clusterIndex)
// Build one paired-node candidate per eligible base-seeded cluster.
// The base pair is mandatory. Fine/coarse support counts only when its
// retained dense direction matches the base and both pair endpoints obey
// complete-link distance Q.
float fineLogEpsilon =
profileLogEpsilon(
fineBins,
fineMinBin,
fineMaxBin)
float baseLogEpsilon =
profileLogEpsilon(
baseBins,
baseMinBin,
baseMaxBin)
float coarseLogEpsilon =
profileLogEpsilon(
coarseBins,
coarseMinBin,
coarseMaxBin)
array<int> stableNodeClusters =
array.new_int()
array<int> stableNodeMates =
array.new_int()
array<int> stableNodeDirections =
array.new_int()
array<int> stableNodeSupports =
array.new_int()
array<float> stableNodeWorstProminence =
array.new_float()
array<float> stableNodeMinimumZ =
array.new_float()
array<int> stableNodeWidths =
array.new_int()
if multiProfileValid and
eligibleClusterIndices.size() > 0
for eligibleIndex =
0 to eligibleClusterIndices.size() - 1
int clusterIndex =
eligibleClusterIndices.get(
eligibleIndex)
int baseAnchor =
clusterBaseTicks.get(
clusterIndex)
int baseDirection =
clusterBaseDirections.get(
clusterIndex)
[basePairValid,
baseMate,
baseProminence,
baseWidth] =
traceNodeMate(
baseBins,
baseMinBin,
baseMaxBin,
effectiveBaseTicks,
baseAnchor,
baseDirection,
maximumNodeWidthTicks,
baseLogEpsilon)
// Search all nearby same-direction neighbors after the base pair
// exists. This avoids inheriting a standalone-neighbor choice that
// is close in price but has the wrong dense direction or mate.
array<int> agreeingFineIndices =
array.new_int()
array<int> agreeingFineAnchors =
array.new_int()
array<int> agreeingFineMates =
array.new_int()
array<float> agreeingFineProminences =
array.new_float()
array<int> agreeingCoarseIndices =
array.new_int()
array<int> agreeingCoarseAnchors =
array.new_int()
array<int> agreeingCoarseMates =
array.new_int()
array<float> agreeingCoarseProminences =
array.new_float()
if basePairValid and
fineCandidateTicks.size() > 0
for fineIndex =
0 to fineCandidateTicks.size() - 1
int fineAnchor =
fineCandidateTicks.get(
fineIndex)
int fineDirection =
fineCandidateDirections.get(
fineIndex)
bool anchorEligible =
fineDirection ==
baseDirection and
math.abs(
fineAnchor -
baseAnchor) <=
consensusRadiusTicks
if anchorEligible
[finePairValid,
fineMate,
fineProminence,
fineWidth] =
traceNodeMate(
fineBins,
fineMinBin,
fineMaxBin,
fineTicks,
fineAnchor,
fineDirection,
maximumNodeWidthTicks,
fineLogEpsilon)
if finePairValid and
math.abs(
fineMate -
baseMate) <=
endpointRadiusTicks
agreeingFineIndices.push(
fineIndex)
agreeingFineAnchors.push(
fineAnchor)
agreeingFineMates.push(
fineMate)
agreeingFineProminences.push(
fineProminence)
if basePairValid and
coarseCandidateTicks.size() > 0
for coarseIndex =
0 to coarseCandidateTicks.size() - 1
int coarseAnchor =
coarseCandidateTicks.get(
coarseIndex)
int coarseDirection =
coarseCandidateDirections.get(
coarseIndex)
bool anchorEligible =
coarseDirection ==
baseDirection and
math.abs(
coarseAnchor -
baseAnchor) <=
consensusRadiusTicks
if anchorEligible
[coarsePairValid,
coarseMate,
coarseProminence,
coarseWidth] =
traceNodeMate(
coarseBins,
coarseMinBin,
coarseMaxBin,
coarseTicks,
coarseAnchor,
coarseDirection,
maximumNodeWidthTicks,
coarseLogEpsilon)
if coarsePairValid and
math.abs(
coarseMate -
baseMate) <=
endpointRadiusTicks
agreeingCoarseIndices.push(
coarseIndex)
agreeingCoarseAnchors.push(
coarseAnchor)
agreeingCoarseMates.push(
coarseMate)
agreeingCoarseProminences.push(
coarseProminence)
int nodeSupport = 0
float nodeWorstProminence = na
float nodeMinimumZ = na
float baseZ =
clusterBaseZ.get(
clusterIndex)
// Two-grid alternatives.
if agreeingFineIndices.size() > 0
for agreeingFineIndex =
0 to agreeingFineIndices.size() - 1
int fineIndex =
agreeingFineIndices.get(
agreeingFineIndex)
float candidateProminence =
math.min(
baseProminence,
agreeingFineProminences.get(
agreeingFineIndex))
float candidateMinimumZ =
math.min(
baseZ,
fineCandidateZ.get(
fineIndex))
if betterNode(
2,
candidateProminence,
candidateMinimumZ,
nodeSupport,
nodeWorstProminence,
nodeMinimumZ)
nodeSupport := 2
nodeWorstProminence :=
candidateProminence
nodeMinimumZ :=
candidateMinimumZ
if agreeingCoarseIndices.size() > 0
for agreeingCoarseIndex =
0 to agreeingCoarseIndices.size() - 1
int coarseIndex =
agreeingCoarseIndices.get(
agreeingCoarseIndex)
float candidateProminence =
math.min(
baseProminence,
agreeingCoarseProminences.get(
agreeingCoarseIndex))
float candidateMinimumZ =
math.min(
baseZ,
coarseCandidateZ.get(
coarseIndex))
if betterNode(
2,
candidateProminence,
candidateMinimumZ,
nodeSupport,
nodeWorstProminence,
nodeMinimumZ)
nodeSupport := 2
nodeWorstProminence :=
candidateProminence
nodeMinimumZ :=
candidateMinimumZ
// Three-grid alternatives retain complete linkage for both
// anchors and mate endpoints.
if agreeingFineIndices.size() > 0 and
agreeingCoarseIndices.size() > 0
for agreeingFineIndex =
0 to agreeingFineIndices.size() - 1
int fineAnchor =
agreeingFineAnchors.get(
agreeingFineIndex)
int fineMate =
agreeingFineMates.get(
agreeingFineIndex)
int fineIndex =
agreeingFineIndices.get(
agreeingFineIndex)
for agreeingCoarseIndex =
0 to agreeingCoarseIndices.size() - 1
int coarseAnchor =
agreeingCoarseAnchors.get(
agreeingCoarseIndex)
int coarseMate =
agreeingCoarseMates.get(
agreeingCoarseIndex)
bool completeLink =
math.abs(
fineAnchor -
coarseAnchor) <=
consensusRadiusTicks and
math.abs(
fineMate -
coarseMate) <=
endpointRadiusTicks
if completeLink
int coarseIndex =
agreeingCoarseIndices.get(
agreeingCoarseIndex)
float candidateProminence =
math.min(
baseProminence,
math.min(
agreeingFineProminences.get(
agreeingFineIndex),
agreeingCoarseProminences.get(
agreeingCoarseIndex)))
float candidateMinimumZ =
math.min(
baseZ,
math.min(
fineCandidateZ.get(
fineIndex),
coarseCandidateZ.get(
coarseIndex)))
if betterNode(
3,
candidateProminence,
candidateMinimumZ,
nodeSupport,
nodeWorstProminence,
nodeMinimumZ)
nodeSupport := 3
nodeWorstProminence :=
candidateProminence
nodeMinimumZ :=
candidateMinimumZ
if basePairValid and
nodeSupport >=
MIN_CONSENSUS_GRIDS
stableNodeClusters.push(
clusterIndex)
stableNodeMates.push(
baseMate)
stableNodeDirections.push(
baseDirection)
stableNodeSupports.push(
nodeSupport)
stableNodeWorstProminence.push(
nodeWorstProminence)
stableNodeMinimumZ.push(
nodeMinimumZ)
stableNodeWidths.push(
baseWidth)
// Selected structures are stable pairs first, then only anchors for which
// no stable mate exists. Selecting a pair consumes every consensus anchor
// inside its interval or within Q of either edge.
array<bool> clusterConsumed =
array.new_bool(
clusterBaseTicks.size(),
false)
array<bool> clusterHasStableNode =
array.new_bool(
clusterBaseTicks.size(),
false)
if stableNodeClusters.size() > 0
for nodeIndex =
0 to stableNodeClusters.size() - 1
clusterHasStableNode.set(
stableNodeClusters.get(
nodeIndex),
true)
array<int> structureKinds =
array.new_int()
array<int> structureClusters =
array.new_int()
array<int> structureAnchorTicks =
array.new_int()
array<int> structureMateTicks =
array.new_int()
array<int> structureDirections =
array.new_int()
array<int> structureSupports =
array.new_int()
array<float> structureProminences =
array.new_float()
array<float> structureMinimumZ =
array.new_float()
array<int> structureWidths =
array.new_int()
array<bool> stableNodeConsumed =
array.new_bool(
stableNodeClusters.size(),
false)
if stableNodeClusters.size() > 0
for rank =
0 to maximumStructuresInput - 1
int bestNodeIndex = -1
int bestSupport = -1
float bestWorstProminence = na
float bestMinimumZ = na
int bestAnchor = na
for nodeIndex =
0 to stableNodeClusters.size() - 1
int clusterIndex =
stableNodeClusters.get(
nodeIndex)
bool available =
not stableNodeConsumed.get(
nodeIndex) and
not clusterConsumed.get(
clusterIndex)
if available
int support =
stableNodeSupports.get(
nodeIndex)
float worstProminence =
stableNodeWorstProminence.get(
nodeIndex)
float minimumZ =
stableNodeMinimumZ.get(
nodeIndex)
int anchor =
clusterBaseTicks.get(
clusterIndex)
bool better =
betterNode(
support,
worstProminence,
minimumZ,
bestSupport,
bestWorstProminence,
bestMinimumZ)
if better or
(
support ==
bestSupport and
worstProminence ==
bestWorstProminence and
minimumZ ==
bestMinimumZ and
(
na(bestAnchor) or
anchor < bestAnchor))
bestNodeIndex :=
nodeIndex
bestSupport := support
bestWorstProminence :=
worstProminence
bestMinimumZ :=
minimumZ
bestAnchor := anchor
if bestNodeIndex >= 0 and
structureKinds.size() <
maximumStructuresInput
stableNodeConsumed.set(
bestNodeIndex,
true)
int clusterIndex =
stableNodeClusters.get(
bestNodeIndex)
int anchor =
clusterBaseTicks.get(
clusterIndex)
int mate =
stableNodeMates.get(
bestNodeIndex)
structureKinds.push(1)
structureClusters.push(
clusterIndex)
structureAnchorTicks.push(
anchor)
structureMateTicks.push(
mate)
structureDirections.push(
stableNodeDirections.get(
bestNodeIndex))
structureSupports.push(
stableNodeSupports.get(
bestNodeIndex))
structureProminences.push(
stableNodeWorstProminence.get(
bestNodeIndex))
structureMinimumZ.push(
stableNodeMinimumZ.get(
bestNodeIndex))
structureWidths.push(
stableNodeWidths.get(
bestNodeIndex))
float consumeLow =
math.min(
anchor,
mate) -
consensusRadiusTicks
float consumeHigh =
math.max(
anchor,
mate) +
consensusRadiusTicks
if clusterBaseTicks.size() > 0
for consumeIndex =
0 to clusterBaseTicks.size() - 1
int candidateAnchor =
clusterBaseTicks.get(
consumeIndex)
if candidateAnchor >=
consumeLow and
candidateAnchor <=
consumeHigh
clusterConsumed.set(
consumeIndex,
true)
// Fill any remaining slots with v0.4 consensus anchors only when that
// anchor had no cross-grid-stable mate.
if eligibleClusterIndices.size() > 0
for rank =
0 to maximumStructuresInput - 1
int bestCluster = -1
int bestSupport = -1
float bestMinimumZ = na
int bestBaseTick = na
if structureKinds.size() <
maximumStructuresInput
for eligibleIndex =
0 to eligibleClusterIndices.size() - 1
int clusterIndex =
eligibleClusterIndices.get(
eligibleIndex)
bool available =
not clusterConsumed.get(
clusterIndex) and
not clusterHasStableNode.get(
clusterIndex)
if available
int supportCount =
clusterSupportCounts.get(
clusterIndex)
float minimumZ =
clusterMinimumZ.get(
clusterIndex)
int baseTick =
clusterBaseTicks.get(
clusterIndex)
if supportCount >
bestSupport or
(
supportCount ==
bestSupport and
(
na(bestMinimumZ) or
minimumZ >
bestMinimumZ)) or
(
supportCount ==
bestSupport and
minimumZ ==
bestMinimumZ and
(
na(bestBaseTick) or
baseTick <
bestBaseTick))
bestCluster :=
clusterIndex
bestSupport :=
supportCount
bestMinimumZ :=
minimumZ
bestBaseTick :=
baseTick
if bestCluster >= 0 and
structureKinds.size() <
maximumStructuresInput
clusterConsumed.set(
bestCluster,
true)
structureKinds.push(0)
structureClusters.push(
bestCluster)
structureAnchorTicks.push(
clusterBaseTicks.get(
bestCluster))
structureMateTicks.push(na)
structureDirections.push(
clusterBaseDirections.get(
bestCluster))
structureSupports.push(
clusterSupportCounts.get(
bestCluster))
structureProminences.push(na)
structureMinimumZ.push(
clusterMinimumZ.get(
bestCluster))
structureWidths.push(0)
if multiProfileValid
int histogramWidthMs = int(
(
activeProfileEndExclusive -
profileStartTime) *
histogramWidthInput /
100.0)
for bin =
baseMinBin to baseMaxBin
float volumeAtBin =
binVolume(baseBins, bin)
float relativeWidth =
baseMaximumBinVolume > 0.0 ?
volumeAtBin /
baseMaximumBinVolume :
0.0
float rowCenterPrice =
(
GRID_ORIGIN_TICKS +
bin *
effectiveBaseTicks +
effectiveBaseTicks *
0.5) *
syminfo.mintick
line histogramLine = line.new(
activeProfileEndExclusive -
int(
histogramWidthMs *
relativeWidth),
rowCenterPrice,
activeProfileEndExclusive,
rowCenterPrice,
xloc = xloc.bar_time,
color = color.new(
rangeColorInput,
65),
width = 2)
histogramLines.push(
histogramLine)
pocLine := line.new(
profileStartTime,
basePocPrice,
activeProfileEndExclusive,
basePocPrice,
xloc = xloc.bar_time,
color = color.orange,
width = 2)
valueAreaHighLine := line.new(
profileStartTime,
baseValueAreaHighPrice,
activeProfileEndExclusive,
baseValueAreaHighPrice,
xloc = xloc.bar_time,
color =
color.new(color.silver, 20),
style = line.style_dotted)
valueAreaLowLine := line.new(
profileStartTime,
baseValueAreaLowPrice,
activeProfileEndExclusive,
baseValueAreaLowPrice,
xloc = xloc.bar_time,
color =
color.new(color.silver, 20),
style = line.style_dotted)
int structureEndTime =
extendStructuresInput ?
time :
activeProfileEndExclusive
if structureKinds.size() > 0
for selectedPosition =
0 to structureKinds.size() - 1
int clusterIndex =
structureClusters.get(
selectedPosition)
int supportCount =
structureSupports.get(
selectedPosition)
int anchorTick =
structureAnchorTicks.get(
selectedPosition)
int mateTick =
structureMateTicks.get(
selectedPosition)
float anchorPrice =
anchorTick *
syminfo.mintick
bool drawingSuppressed =
clusterDrawingSuppressed.get(
clusterIndex)
bool paired =
structureKinds.get(
selectedPosition) == 1
int transparency =
supportCount == 3 ?
0 : 28
if not drawingSuppressed
line anchorLine = line.new(
profileStartTime,
anchorPrice,
structureEndTime,
anchorPrice,
xloc = xloc.bar_time,
color = color.new(
consensusColorInput,
transparency),
width =
supportCount == 3 ?
3 : 2)
consensusLines.push(
anchorLine)
if paired
float matePrice =
mateTick *
syminfo.mintick
line mateLine = line.new(
profileStartTime,
matePrice,
structureEndTime,
matePrice,
xloc = xloc.bar_time,
color = color.new(
consensusColorInput,
transparency),
width =
supportCount == 3 ?
3 : 2)
consensusLines.push(
mateLine)
string structureId =
(
paired ? "N" : "S") +
str.tostring(
selectedPosition + 1)
string anchorLabelText =
structureId +
(
paired ? " A " : " ") +
str.tostring(
anchorPrice,
format.mintick) +
" | " +
str.tostring(
supportCount) +
"/3 | min z " +
str.tostring(
structureMinimumZ.get(
selectedPosition),
"#.00")
if not drawingSuppressed
label anchorLabel =
label.new(
structureEndTime,
anchorPrice,
anchorLabelText,
xloc = xloc.bar_time,
style =
label.style_label_left,
color = color.new(
color.black,
78),
textcolor = color.new(
consensusColorInput,
transparency),
size = size.tiny)
consensusLabels.push(
anchorLabel)
if paired
float matePrice =
mateTick *
syminfo.mintick
string mateLabelText =
structureId +
" M " +
str.tostring(
matePrice,
format.mintick) +
" | P " +
str.tostring(
structureProminences.get(
selectedPosition),
"#.00") +
" | w " +
str.tostring(
structureWidths.get(
selectedPosition)) +
"r"
label mateLabel =
label.new(
structureEndTime,
matePrice,
mateLabelText,
xloc = xloc.bar_time,
style =
label.style_label_left,
color = color.new(
color.black,
78),
textcolor =
color.new(
consensusColorInput,
transparency),
size = size.tiny)
consensusLabels.push(
mateLabel)
string state =
multiProfileValid and
structuralBoundsValid ?
"VALID / EXPERIMENTAL" :
not chartSupported ?
"USE STANDARD 15m CHART" :
not liveWallSelected ?
(
not liveWallsAllowed ?
"NO TWO-SIDED RSD WALL" :
"NO ACTIVE RSD MACRO WALL") :
not liveEndpointRefined ?
"RSD ENDPOINT REFINE FAIL" :
rangeUpdatePending ?
"RANGE UPDATE PENDING" :
not rangeHistoryCovered ?
"RANGE OUTSIDE 5000 BARS" :
rangeTooLong ?
"RANGE TOO LONG" :
not gridConfigurationValid ?
"ROW GRIDS NOT DISTINCT" :
allocationWorkCapFail ?
"PROFILE WORK CAP FAIL" :
not endpointsComplete ?
"ENDPOINT COVERAGE FAIL" :
invalidSourceBars > 0 ?
"INVALID SOURCE BAR" :
sourceOrderIssue ?
"SOURCE ORDER FAIL" :
fineOverflow or
baseOverflow or
coarseOverflow ?
"PROFILE CAP FAIL" :
not fineReconciliationValid or
not baseReconciliationValid or
not coarseReconciliationValid ?
"VOLUME RECONCILIATION FAIL" :
not fineAlgorithmValid or
not baseAlgorithmValid or
not coarseAlgorithmValid ?
"PROFILE ALGORITHM FAIL" :
not structuralBoundsValid ?
"INVALID STRUCTURAL BOUNDS" :
"INCOMPLETE"
string gridText =
str.tostring(fineTicks) +
" / " +
str.tostring(effectiveBaseTicks) +
" / " +
str.tostring(coarseTicks) +
" ticks" +
(
not na(effectiveBaseTicks) and
effectiveBaseTicks !=
baseTicksInput ?
" (preferred " +
str.tostring(baseTicksInput) +
")" : "") +
(
gridConfigurationValid ?
"" : " (INVALID)")
string candidateCountText =
str.tostring(
fineCandidateTicks.size()) +
" / " +
str.tostring(
baseCandidateTicks.size()) +
" / " +
str.tostring(
coarseCandidateTicks.size())
string rawCountText =
str.tostring(fineRawCount) +
" / " +
str.tostring(baseRawCount) +
" / " +
str.tostring(coarseRawCount)
string cleanMadText =
floatText(fineCleanMad) +
" / " +
floatText(baseCleanMad) +
" / " +
floatText(coarseCleanMad)
string structuralStateText =
not useStructuralBoundsInput ?
"OFF" :
not structuralBoundsValid ?
"INVALID: high must exceed low" :
str.tostring(
structuralLowInput,
format.mintick) +
" / " +
str.tostring(
structuralHighInput,
format.mintick) +
" ± " +
str.tostring(
structuralDistanceBaseRowsInput,
"#.##") +
" rows"
string structureOneText = "NONE"
string structureTwoText = "NONE"
int visibleStructureCount = 0
int pairedStructureCount = 0
int standaloneStructureCount = 0
if structureKinds.size() > 0
for selectedPosition =
0 to structureKinds.size() - 1
bool paired =
structureKinds.get(
selectedPosition) == 1
int clusterIndex =
structureClusters.get(
selectedPosition)
bool drawingSuppressed =
clusterDrawingSuppressed.get(
clusterIndex)
visibleStructureCount +=
drawingSuppressed ? 0 : 1
pairedStructureCount +=
paired ? 1 : 0
standaloneStructureCount +=
paired ? 0 : 1
float anchorPrice =
structureAnchorTicks.get(
selectedPosition) *
syminfo.mintick
string item =
(
paired ? "NODE " : "SHELF ") +
str.tostring(
anchorPrice,
format.mintick)
if paired
float matePrice =
structureMateTicks.get(
selectedPosition) *
syminfo.mintick
item +=
" - " +
str.tostring(
matePrice,
format.mintick) +
" | " +
str.tostring(
structureSupports.get(
selectedPosition)) +
"/3 | P " +
str.tostring(
structureProminences.get(
selectedPosition),
"#.00") +
" | w " +
str.tostring(
structureWidths.get(
selectedPosition)) +
"r"
else
item +=
" | " +
str.tostring(
structureSupports.get(
selectedPosition)) +
"/3 | z " +
str.tostring(
structureMinimumZ.get(
selectedPosition),
"#.00")
item +=
drawingSuppressed ?
" | HIDDEN" : ""
if selectedPosition == 0
structureOneText := item
if selectedPosition == 1
structureTwoText := item
color headerBg =
color.new(#1565c0, 10)
color rowBg =
color.new(color.black, 20)
color warningBg =
color.new(#8d6e63, 15)
diagnosticRow(
diagnosticTable,
0,
"VP RSD AUTO 0.6",
state,
headerBg)
diagnosticRow(
diagnosticTable,
1,
"RSD range source",
(liveWallSelected ?
(
liveSide < 0 ?
"LOW WALL" :
"HIGH WALL") :
"NONE") +
" | " +
macroTFInput +
"m swing-only",
warningBg)
diagnosticRow(
diagnosticTable,
2,
"Automatic start",
timeText(liveStartTime),
rowBg)
diagnosticRow(
diagnosticTable,
3,
"Automatic end incl.",
timeText(liveEndBarTime),
rowBg)
diagnosticRow(
diagnosticTable,
4,
"Fine / base / coarse",
gridText,
gridConfigurationValid ?
rowBg : warningBg)
diagnosticRow(
diagnosticTable,
5,
"Source bars",
str.tostring(
includedSourceBars),
rowBg)
diagnosticRow(
diagnosticTable,
6,
"Invalid bars",
str.tostring(
invalidSourceBars),
rowBg)
diagnosticRow(
diagnosticTable,
7,
"Source volume",
floatText(
sourceTotalVolume),
rowBg)
diagnosticRow(
diagnosticTable,
8,
"Alloc diffs F/B/C",
floatText(
fineAllocatedVolume -
sourceTotalVolume) +
" / " +
floatText(
baseAllocatedVolume -
sourceTotalVolume) +
" / " +
floatText(
coarseAllocatedVolume -
sourceTotalVolume),
rowBg)
diagnosticRow(
diagnosticTable,
9,
"Base profile bins",
multiProfileValid ?
str.tostring(
baseMaxBin -
baseMinBin +
1) :
"NA",
rowBg)
diagnosticRow(
diagnosticTable,
10,
"Base POC",
str.tostring(
basePocPrice,
format.mintick),
rowBg)
diagnosticRow(
diagnosticTable,
11,
"Base VAL / VAH",
str.tostring(
baseValueAreaLowPrice,
format.mintick) +
" / " +
str.tostring(
baseValueAreaHighPrice,
format.mintick),
rowBg)
diagnosticRow(
diagnosticTable,
12,
"Base VA achieved",
str.tostring(
baseAchievedValueAreaPercent,
"#.0") +
"%",
rowBg)
diagnosticRow(
diagnosticTable,
13,
"Raw boundaries F/B/C",
rawCountText,
rowBg)
diagnosticRow(
diagnosticTable,
14,
"Gated candidates F/B/C",
candidateCountText,
rowBg)
diagnosticRow(
diagnosticTable,
15,
"Clean MAD F/B/C",
cleanMadText,
rowBg)
diagnosticRow(
diagnosticTable,
16,
"Base-seeded clusters",
str.tostring(
clusterBaseTicks.size()),
rowBg)
diagnosticRow(
diagnosticTable,
17,
"Stable pairs / selected",
str.tostring(
stableNodeClusters.size()) +
" / " +
str.tostring(
structureKinds.size()),
rowBg)
diagnosticRow(
diagnosticTable,
18,
"Nodes / shelves / visible",
str.tostring(
pairedStructureCount) +
" / " +
str.tostring(
standaloneStructureCount) +
" / " +
str.tostring(
visibleStructureCount),
rowBg)
diagnosticRow(
diagnosticTable,
19,
"Structure 1",
structureOneText,
rowBg)
diagnosticRow(
diagnosticTable,
20,
"Structure 2",
structureTwoText,
rowBg)
diagnosticRow(
diagnosticTable,
21,
"Structural bounds",
structuralStateText,
useStructuralBoundsInput ?
warningBg : rowBg)
diagnosticRow(
diagnosticTable,
22,
"Cluster / endpoint radii",
str.tostring(
consensusClusterBaseRowsInput,
"#.##") +
" / 1.00 base rows",
rowBg)
diagnosticRow(
diagnosticTable,
23,
"Local pair guard",
"nearest clean peak≥10%; P>0; 3-" +
str.tostring(
maximumNodeWidthBaseRowsInput,
"#.0") +
" base rows",
rowBg)
diagnosticRow(
diagnosticTable,
24,
"Geometry gates",
str.tostring(
minimumRatioInput,
"#.00") +
"x; W/J≤" +
str.tostring(
maximumNoiseFractionInput,
"#.00") +
"; dense≥" +
str.tostring(
minimumDenseFractionInput,
"#.00") +
"; z≥" +
str.tostring(
minimumRobustZInput,
"#.00"),
rowBg)
diagnosticRow(
diagnosticTable,
25,
"Suppressed at bounds",
str.tostring(
structurallySuppressedCount),
rowBg)
diagnosticRow(
diagnosticTable,
26,
"Maximum structures",
str.tostring(
maximumStructuresInput),
rowBg)
diagnosticRow(
diagnosticTable,
27,
"Warning",
"Research only; compare expert marks",
warningBg)
Show all 4,145 lines